Second HTTP overhaul

- Rename message types for clarity
  - HttpMessage -> FullHttpMessage
  - HttpHeader -> HttpMessage
  - HttpRequest -> FullHttpRequest
  - HttpResponse -> FulllHttpResponse
  - HttpRequestHeader -> HttpRequest
  - HttpResponseHeader -> HttpResponse
- HttpContent now extends ByteBufHolder; no more content() method
- Make HttpHeaders abstract, make its header access methods public, and
  add DefaultHttpHeaders
- Header accessor methods in HttpMessage and LastHttpContent are
  replaced with HttpMessage.headers() and
  LastHttpContent.trailingHeaders(). Both methods return HttpHeaders.
- Remove setters wherever possible and remove 'get' prefix
- Instead of calling setContent(), a user can either specify the content
  when constructing a message or write content into the buffer.
  (e.g. m.content().writeBytes(...))
- Overall cleanup & fixes
This commit is contained in:
Trustin Lee 2013-01-16 13:22:50 +09:00
parent f136eafd5e
commit 34820511ff
98 changed files with 2173 additions and 2254 deletions

View File

@ -936,6 +936,10 @@ public abstract class AbstractByteBuf implements ByteBuf {
@Override @Override
public String toString() { public String toString() {
if (isFreed()) {
return getClass().getSimpleName() + "(freed)";
}
return getClass().getSimpleName() + '(' + return getClass().getSimpleName() + '(' +
"ridx=" + readerIndex + ", " + "ridx=" + readerIndex + ", " +
"widx=" + writerIndex + ", " + "widx=" + writerIndex + ", " +

View File

@ -57,9 +57,6 @@ public class DefaultByteBufHolder implements ByteBufHolder {
@Override @Override
public String toString() { public String toString() {
if (isFreed()) { return getClass().getSimpleName() + '(' + data().toString() + ')';
return "Message{data=(FREED)}";
}
return "Message{data=" + ByteBufUtil.hexDump(data()) + '}';
} }
} }

View File

@ -22,7 +22,7 @@ import static io.netty.handler.codec.http.CookieEncoderUtil.*;
* the HTTP cookie version 0, 1, and 2. * the HTTP cookie version 0, 1, and 2.
* <pre> * <pre>
* // Example * // Example
* {@link HttpRequestHeader} req = ...; * {@link HttpRequest} req = ...;
* res.setHeader("Cookie", {@link ClientCookieEncoder}.encode("JSESSIONID", "1234")); * res.setHeader("Cookie", {@link ClientCookieEncoder}.encode("JSESSIONID", "1234"));
* </pre> * </pre>
* *

View File

@ -29,7 +29,7 @@ import java.util.TreeSet;
* the HTTP cookie version 0, 1, and 2. * the HTTP cookie version 0, 1, and 2.
* *
* <pre> * <pre>
* {@link HttpRequestHeader} req = ...; * {@link HttpRequest} req = ...;
* String value = req.getHeader("Cookie"); * String value = req.getHeader("Cookie");
* Set&lt;{@link Cookie}&gt; cookies = new {@link CookieDecoder}().decode(value); * Set&lt;{@link Cookie}&gt; cookies = new {@link CookieDecoder}().decode(value);
* </pre> * </pre>

View File

@ -0,0 +1,67 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
/**
* Default implementation of {@link FullHttpRequest}.
*/
public class DefaultFullHttpRequest extends DefaultHttpRequest implements FullHttpRequest {
private final ByteBuf content;
private final HttpHeaders trailingHeader = new DefaultHttpHeaders();
public DefaultFullHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri) {
this(httpVersion, method, uri, Unpooled.buffer(0));
}
public DefaultFullHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri, ByteBuf content) {
super(httpVersion, method, uri);
if (content == null) {
throw new NullPointerException("content");
}
this.content = content;
}
@Override
public HttpHeaders trailingHeaders() {
return trailingHeader;
}
@Override
public ByteBuf data() {
return content;
}
@Override
public boolean isFreed() {
return content.isFreed();
}
@Override
public void free() {
content.free();
}
@Override
public FullHttpRequest copy() {
DefaultFullHttpRequest copy = new DefaultFullHttpRequest(protocolVersion(), method(), uri(), data().copy());
copy.headers().set(headers());
copy.trailingHeaders().set(trailingHeaders());
return copy;
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
/**
* Default implementation of a {@link FullHttpResponse}.
*/
public class DefaultFullHttpResponse extends DefaultHttpResponse implements FullHttpResponse {
private final ByteBuf content;
private final HttpHeaders trailingHeaders = new DefaultHttpHeaders();
public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status) {
this(version, status, Unpooled.buffer(0));
}
public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status, ByteBuf content) {
super(version, status);
if (content == null) {
throw new NullPointerException("content");
}
this.content = content;
}
@Override
public HttpHeaders trailingHeaders() {
return trailingHeaders;
}
@Override
public ByteBuf data() {
return content;
}
@Override
public boolean isFreed() {
return content.isFreed();
}
@Override
public void free() {
content.free();
}
@Override
public FullHttpResponse copy() {
DefaultFullHttpResponse copy = new DefaultFullHttpResponse(protocolVersion(), status(), data().copy());
copy.headers().set(headers());
copy.trailingHeaders().set(trailingHeaders());
return copy;
}
}

View File

@ -22,40 +22,40 @@ import io.netty.buffer.ByteBuf;
*/ */
public class DefaultHttpContent extends DefaultHttpObject implements HttpContent { public class DefaultHttpContent extends DefaultHttpObject implements HttpContent {
private ByteBuf content; private final ByteBuf content;
/** /**
* Creates a new instance with the specified chunk content. * Creates a new instance with the specified chunk content.
*/ */
public DefaultHttpContent(ByteBuf content) { public DefaultHttpContent(ByteBuf content) {
setContent(content);
}
@Override
public ByteBuf getContent() {
return content;
}
@Override
public void setContent(ByteBuf content) {
if (content == null) { if (content == null) {
throw new NullPointerException("content"); throw new NullPointerException("content");
} }
this.content = content; this.content = content;
} }
@Override
public ByteBuf data() {
return content;
}
@Override
public HttpContent copy() {
return new DefaultHttpContent(data().copy());
}
@Override
public boolean isFreed() {
return content.isFreed();
}
@Override
public void free() {
content.free();
}
@Override @Override
public String toString() { public String toString() {
StringBuilder buf = new StringBuilder(); return getClass().getSimpleName() + "(data: " + data() + ", decoderResult: " + decoderResult() + ')';
buf.append(getClass().getSimpleName());
buf.append(" size: ");
buf.append(getContent().readableBytes());
buf.append(", decodeResult: ");
buf.append(getDecoderResult());
buf.append(')');
return buf.toString();
} }
} }

View File

@ -1,127 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http;
import io.netty.util.internal.StringUtil;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* The default {@link HttpHeader} implementation.
*/
public abstract class DefaultHttpHeader extends DefaultHttpObject implements HttpHeader {
private final HttpHeaders headers = new HttpHeaders();
private HttpVersion version;
/**
* Creates a new instance.
*/
protected DefaultHttpHeader(final HttpVersion version) {
setProtocolVersion(version);
}
@Override
public void addHeader(final String name, final Object value) {
headers.addHeader(name, value);
}
@Override
public void setHeader(final String name, final Object value) {
headers.setHeader(name, value);
}
@Override
public void setHeader(final String name, final Iterable<?> values) {
headers.setHeader(name, values);
}
@Override
public void removeHeader(final String name) {
headers.removeHeader(name);
}
@Override
public void clearHeaders() {
headers.clearHeaders();
}
@Override
public String getHeader(final String name) {
return headers.getHeader(name);
}
@Override
public List<String> getHeaders(final String name) {
return headers.getHeaders(name);
}
@Override
public List<Map.Entry<String, String>> getHeaders() {
return headers.getHeaders();
}
@Override
public boolean containsHeader(final String name) {
return headers.containsHeader(name);
}
@Override
public Set<String> getHeaderNames() {
return headers.getHeaderNames();
}
@Override
public HttpVersion getProtocolVersion() {
return version;
}
@Override
public void setProtocolVersion(HttpVersion version) {
if (version == null) {
throw new NullPointerException("version");
}
this.version = version;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName());
buf.append("(version: ");
buf.append(getProtocolVersion().getText());
buf.append(", keepAlive: ");
buf.append(HttpHeaders.isKeepAlive(this));
buf.append(')');
buf.append(StringUtil.NEWLINE);
appendHeaders(buf);
// Remove the last newline.
buf.setLength(buf.length() - StringUtil.NEWLINE.length());
return buf.toString();
}
void appendHeaders(StringBuilder buf) {
for (Map.Entry<String, String> e: getHeaders()) {
buf.append(e.getKey());
buf.append(": ");
buf.append(e.getValue());
buf.append(StringUtil.NEWLINE);
}
}
}

View File

@ -0,0 +1,359 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
public class DefaultHttpHeaders extends HttpHeaders {
private static final int BUCKET_SIZE = 17;
private static int hash(String name) {
int h = 0;
for (int i = name.length() - 1; i >= 0; i --) {
char c = name.charAt(i);
if (c >= 'A' && c <= 'Z') {
c += 32;
}
h = 31 * h + c;
}
if (h > 0) {
return h;
} else if (h == Integer.MIN_VALUE) {
return Integer.MAX_VALUE;
} else {
return -h;
}
}
private static boolean eq(String name1, String name2) {
int nameLen = name1.length();
if (nameLen != name2.length()) {
return false;
}
for (int i = nameLen - 1; i >= 0; i --) {
char c1 = name1.charAt(i);
char c2 = name2.charAt(i);
if (c1 != c2) {
if (c1 >= 'A' && c1 <= 'Z') {
c1 += 32;
}
if (c2 >= 'A' && c2 <= 'Z') {
c2 += 32;
}
if (c1 != c2) {
return false;
}
}
}
return true;
}
private static int index(int hash) {
return hash % BUCKET_SIZE;
}
private final HeaderEntry[] entries = new HeaderEntry[BUCKET_SIZE];
private final HeaderEntry head = new HeaderEntry(-1, null, null);
public DefaultHttpHeaders() {
head.before = head.after = head;
}
void validateHeaderName0(String headerName) {
validateHeaderName(headerName);
}
@Override
public void add(final String name, final Object value) {
validateHeaderName0(name);
String strVal = toString(value);
validateHeaderValue(strVal);
int h = hash(name);
int i = index(h);
add0(h, i, name, strVal);
}
@Override
public void add(String name, Iterable<?> values) {
validateHeaderName0(name);
int h = hash(name);
int i = index(h);
for (Object v: values) {
String vstr = toString(v);
validateHeaderValue(vstr);
add0(h, i, name, vstr);
}
}
private void add0(int h, int i, final String name, final String value) {
// Update the hash table.
HeaderEntry e = entries[i];
HeaderEntry newEntry;
entries[i] = newEntry = new HeaderEntry(h, name, value);
newEntry.next = e;
// Update the linked list.
newEntry.addBefore(head);
}
@Override
public void remove(final String name) {
if (name == null) {
throw new NullPointerException("name");
}
int h = hash(name);
int i = index(h);
remove0(h, i, name);
}
private void remove0(int h, int i, String name) {
HeaderEntry e = entries[i];
if (e == null) {
return;
}
for (;;) {
if (e.hash == h && eq(name, e.key)) {
e.remove();
HeaderEntry next = e.next;
if (next != null) {
entries[i] = next;
e = next;
} else {
entries[i] = null;
return;
}
} else {
break;
}
}
for (;;) {
HeaderEntry next = e.next;
if (next == null) {
break;
}
if (next.hash == h && eq(name, next.key)) {
e.next = next.next;
next.remove();
} else {
e = next;
}
}
}
@Override
public void set(final String name, final Object value) {
validateHeaderName0(name);
String strVal = toString(value);
validateHeaderValue(strVal);
int h = hash(name);
int i = index(h);
remove0(h, i, name);
add0(h, i, name, strVal);
}
@Override
public void set(final String name, final Iterable<?> values) {
if (values == null) {
throw new NullPointerException("values");
}
validateHeaderName0(name);
int h = hash(name);
int i = index(h);
remove0(h, i, name);
for (Object v: values) {
if (v == null) {
break;
}
String strVal = toString(v);
validateHeaderValue(strVal);
add0(h, i, name, strVal);
}
}
@Override
public void clear() {
for (int i = 0; i < entries.length; i ++) {
entries[i] = null;
}
head.before = head.after = head;
}
@Override
public String get(final String name) {
if (name == null) {
throw new NullPointerException("name");
}
int h = hash(name);
int i = index(h);
HeaderEntry e = entries[i];
while (e != null) {
if (e.hash == h && eq(name, e.key)) {
return e.value;
}
e = e.next;
}
return null;
}
@Override
public List<String> getAll(final String name) {
if (name == null) {
throw new NullPointerException("name");
}
LinkedList<String> values = new LinkedList<String>();
int h = hash(name);
int i = index(h);
HeaderEntry e = entries[i];
while (e != null) {
if (e.hash == h && eq(name, e.key)) {
values.addFirst(e.value);
}
e = e.next;
}
return values;
}
@Override
public List<Map.Entry<String, String>> entries() {
List<Map.Entry<String, String>> all =
new LinkedList<Map.Entry<String, String>>();
HeaderEntry e = head.after;
while (e != head) {
all.add(e);
e = e.after;
}
return all;
}
@Override
public Iterator<Map.Entry<String, String>> iterator() {
return entries().iterator();
}
@Override
public boolean contains(String name) {
return get(name) != null;
}
@Override
public boolean isEmpty() {
return head == head.after;
}
@Override
public Set<String> names() {
Set<String> names = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
HeaderEntry e = head.after;
while (e != head) {
names.add(e.key);
e = e.after;
}
return names;
}
private static String toString(Object value) {
if (value == null) {
return null;
}
if (value instanceof String) {
return (String) value;
}
if (value instanceof Number) {
return value.toString();
}
if (value instanceof Date) {
return new HttpHeaderDateFormat().format((Date) value);
}
if (value instanceof Calendar) {
return new HttpHeaderDateFormat().format(((Calendar) value).getTime());
}
return value.toString();
}
private static final class HeaderEntry implements Map.Entry<String, String> {
final int hash;
final String key;
String value;
HeaderEntry next;
HeaderEntry before, after;
HeaderEntry(int hash, String key, String value) {
this.hash = hash;
this.key = key;
this.value = value;
}
void remove() {
before.after = after;
after.before = before;
}
void addBefore(HeaderEntry e) {
after = e;
before = e.before;
before.after = this;
after.before = this;
}
@Override
public String getKey() {
return key;
}
@Override
public String getValue() {
return value;
}
@Override
public String setValue(String value) {
if (value == null) {
throw new NullPointerException("value");
}
validateHeaderValue(value);
String oldValue = this.value;
this.value = value;
return oldValue;
}
@Override
public String toString() {
return key + '=' + value;
}
}
}

View File

@ -15,31 +15,61 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.util.internal.StringUtil;
import io.netty.buffer.Unpooled;
import java.util.Map;
/** /**
* Combination of {@link HttpHeader} and {@link LastHttpContent} which can be used to <i>combine</i> the headers and * The default {@link HttpMessage} implementation.
* the actual content. {@link HttpObjectAggregator} makes use of this.
*
*/ */
public abstract class DefaultHttpMessage extends DefaultHttpHeader implements LastHttpContent { public abstract class DefaultHttpMessage extends DefaultHttpObject implements HttpMessage {
private ByteBuf content = Unpooled.EMPTY_BUFFER;
public DefaultHttpMessage(HttpVersion version) { private final HttpVersion version;
super(version); private final HttpHeaders headers = new DefaultHttpHeaders();
}
@Override /**
public ByteBuf getContent() { * Creates a new instance.
return content; */
} protected DefaultHttpMessage(final HttpVersion version) {
if (version == null) {
@Override throw new NullPointerException("version");
public void setContent(ByteBuf content) { }
if (content == null) { this.version = version;
throw new NullPointerException("content"); }
@Override
public HttpHeaders headers() {
return headers;
}
@Override
public HttpVersion protocolVersion() {
return version;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName());
buf.append("(version: ");
buf.append(protocolVersion().getText());
buf.append(", keepAlive: ");
buf.append(HttpHeaders.isKeepAlive(this));
buf.append(')');
buf.append(StringUtil.NEWLINE);
appendHeaders(buf);
// Remove the last newline.
buf.setLength(buf.length() - StringUtil.NEWLINE.length());
return buf.toString();
}
void appendHeaders(StringBuilder buf) {
for (Map.Entry<String, String> e: headers().entries()) {
buf.append(e.getKey());
buf.append(": ");
buf.append(e.getValue());
buf.append(StringUtil.NEWLINE);
} }
this.content = content;
} }
} }

View File

@ -19,22 +19,22 @@ import io.netty.handler.codec.DecoderResult;
public class DefaultHttpObject implements HttpObject { public class DefaultHttpObject implements HttpObject {
private DecoderResult decodeResult = DecoderResult.SUCCESS; private DecoderResult decoderResult = DecoderResult.SUCCESS;
protected DefaultHttpObject() { protected DefaultHttpObject() {
// Disallow direct instantiation // Disallow direct instantiation
} }
@Override @Override
public DecoderResult getDecoderResult() { public DecoderResult decoderResult() {
return decodeResult; return decoderResult;
} }
@Override @Override
public void setDecoderResult(DecoderResult result) { public void updateDecoderResult(DecoderResult decoderResult) {
if (result == null) { if (decoderResult == null) {
throw new NullPointerException("result"); throw new NullPointerException("decoderResult");
} }
decodeResult = result; this.decoderResult = decoderResult;
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2013 The Netty Project * Copyright 2012 The Netty Project
* *
* The Netty Project licenses this file to you under the Apache License, * The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance * version 2.0 (the "License"); you may not use this file except in compliance
@ -15,34 +15,63 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.util.internal.StringUtil;
import io.netty.buffer.Unpooled;
/** /**
* Default implementation of {@link HttpRequest}. * The default {@link HttpRequest} implementation.
*/ */
public class DefaultHttpRequest extends DefaultHttpRequestHeader implements HttpRequest { public class DefaultHttpRequest extends DefaultHttpMessage implements HttpRequest {
private ByteBuf content = Unpooled.EMPTY_BUFFER;
private final HttpMethod method;
private final String uri;
/**
* Creates a new instance.
*
* @param httpVersion the HTTP version of the request
* @param method the HTTP method of the request
* @param uri the URI or path of the request
*/
public DefaultHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri) { public DefaultHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri) {
this(httpVersion, method, uri, Unpooled.EMPTY_BUFFER); super(httpVersion);
} if (method == null) {
throw new NullPointerException("method");
public DefaultHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri, ByteBuf content) {
super(httpVersion, method, uri);
setContent(content);
}
@Override
public ByteBuf getContent() {
return content;
}
@Override
public void setContent(ByteBuf content) {
if (content == null) {
throw new NullPointerException("content");
} }
this.content = content; if (uri == null) {
throw new NullPointerException("uri");
}
this.method = method;
this.uri = uri;
}
@Override
public HttpMethod method() {
return method;
}
@Override
public String uri() {
return uri;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName());
buf.append(", decodeResult: ");
buf.append(decoderResult());
buf.append(')');
buf.append(StringUtil.NEWLINE);
buf.append(method().toString());
buf.append(' ');
buf.append(uri());
buf.append(' ');
buf.append(protocolVersion().getText());
buf.append(StringUtil.NEWLINE);
appendHeaders(buf);
// Remove the last newline.
buf.setLength(buf.length() - StringUtil.NEWLINE.length());
return buf.toString();
} }
} }

View File

@ -1,87 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http;
import io.netty.util.internal.StringUtil;
/**
* The default {@link HttpRequestHeader} implementation.
*/
public class DefaultHttpRequestHeader extends DefaultHttpHeader implements HttpRequestHeader {
private HttpMethod method;
private String uri;
/**
* Creates a new instance.
*
* @param httpVersion the HTTP version of the request
* @param method the HTTP method of the request
* @param uri the URI or path of the request
*/
public DefaultHttpRequestHeader(HttpVersion httpVersion, HttpMethod method, String uri) {
super(httpVersion);
setMethod(method);
setUri(uri);
}
@Override
public HttpMethod getMethod() {
return method;
}
@Override
public void setMethod(HttpMethod method) {
if (method == null) {
throw new NullPointerException("method");
}
this.method = method;
}
@Override
public String getUri() {
return uri;
}
@Override
public void setUri(String uri) {
if (uri == null) {
throw new NullPointerException("uri");
}
this.uri = uri;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName());
buf.append(", decodeResult: ");
buf.append(getDecoderResult());
buf.append(')');
buf.append(StringUtil.NEWLINE);
buf.append(getMethod().toString());
buf.append(' ');
buf.append(getUri());
buf.append(' ');
buf.append(getProtocolVersion().getText());
buf.append(StringUtil.NEWLINE);
appendHeaders(buf);
// Remove the last newline.
buf.setLength(buf.length() - StringUtil.NEWLINE.length());
return buf.toString();
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2013 The Netty Project * Copyright 2012 The Netty Project
* *
* The Netty Project licenses this file to you under the Apache License, * The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance * version 2.0 (the "License"); you may not use this file except in compliance
@ -15,35 +15,50 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.util.internal.StringUtil;
import io.netty.buffer.Unpooled;
/** /**
* Default implementation of a {@link HttpResponse}. * The default {@link HttpResponse} implementation.
*/ */
public class DefaultHttpResponse extends DefaultHttpResponseHeader implements HttpResponse { public class DefaultHttpResponse extends DefaultHttpMessage implements HttpResponse {
private ByteBuf content = Unpooled.EMPTY_BUFFER;
private final HttpResponseStatus status;
/**
* Creates a new instance.
*
* @param version the HTTP version of this response
* @param status the status of this response
*/
public DefaultHttpResponse(HttpVersion version, HttpResponseStatus status) { public DefaultHttpResponse(HttpVersion version, HttpResponseStatus status) {
this(version, status, Unpooled.EMPTY_BUFFER); super(version);
} if (status == null) {
throw new NullPointerException("status");
public DefaultHttpResponse(HttpVersion version, HttpResponseStatus status, ByteBuf content) {
super(version, status);
setContent(content);
}
@Override
public ByteBuf getContent() {
return content;
}
@Override
public void setContent(ByteBuf content) {
if (content == null) {
throw new NullPointerException("content");
} }
this.content = content; this.status = status;
}
@Override
public HttpResponseStatus status() {
return status;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName());
buf.append(", decodeResult: ");
buf.append(decoderResult());
buf.append(')');
buf.append(StringUtil.NEWLINE);
buf.append(protocolVersion().getText());
buf.append(' ');
buf.append(status().toString());
buf.append(StringUtil.NEWLINE);
appendHeaders(buf);
// Remove the last newline.
buf.setLength(buf.length() - StringUtil.NEWLINE.length());
return buf.toString();
} }
} }

View File

@ -1,69 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http;
import io.netty.util.internal.StringUtil;
/**
* The default {@link HttpResponseHeader} implementation.
*/
public class DefaultHttpResponseHeader extends DefaultHttpHeader implements HttpResponseHeader {
private HttpResponseStatus status;
/**
* Creates a new instance.
*
* @param version the HTTP version of this response
* @param status the status of this response
*/
public DefaultHttpResponseHeader(HttpVersion version, HttpResponseStatus status) {
super(version);
setStatus(status);
}
@Override
public HttpResponseStatus getStatus() {
return status;
}
@Override
public void setStatus(HttpResponseStatus status) {
if (status == null) {
throw new NullPointerException("status");
}
this.status = status;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName());
buf.append(", decodeResult: ");
buf.append(getDecoderResult());
buf.append(')');
buf.append(StringUtil.NEWLINE);
buf.append(getProtocolVersion().getText());
buf.append(' ');
buf.append(getStatus().toString());
buf.append(StringUtil.NEWLINE);
appendHeaders(buf);
// Remove the last newline.
buf.setLength(buf.length() - StringUtil.NEWLINE.length());
return buf.toString();
}
}

View File

@ -19,16 +19,14 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* The default {@link LastHttpContent} implementation. * The default {@link LastHttpContent} implementation.
*/ */
public class DefaultLastHttpContent extends DefaultHttpContent implements LastHttpContent { public class DefaultLastHttpContent extends DefaultHttpContent implements LastHttpContent {
private final HttpHeaders headers = new HttpHeaders() { private final HttpHeaders trailingHeaders = new DefaultHttpHeaders() {
@Override @Override
void validateHeaderName0(String name) { void validateHeaderName0(String name) {
super.validateHeaderName0(name); super.validateHeaderName0(name);
@ -42,7 +40,7 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
}; };
public DefaultLastHttpContent() { public DefaultLastHttpContent() {
this(Unpooled.EMPTY_BUFFER); this(Unpooled.buffer(0));
} }
public DefaultLastHttpContent(ByteBuf content) { public DefaultLastHttpContent(ByteBuf content) {
@ -50,64 +48,20 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
} }
@Override @Override
public void addHeader(final String name, final Object value) { public LastHttpContent copy() {
headers.addHeader(name, value); DefaultLastHttpContent copy = new DefaultLastHttpContent(data().copy());
copy.trailingHeaders().set(trailingHeaders());
return copy;
} }
@Override @Override
public void setHeader(final String name, final Object value) { public HttpHeaders trailingHeaders() {
headers.setHeader(name, value); return trailingHeaders;
}
@Override
public void setHeader(final String name, final Iterable<?> values) {
headers.setHeader(name, values);
}
@Override
public void removeHeader(final String name) {
headers.removeHeader(name);
}
@Override
public void clearHeaders() {
headers.clearHeaders();
}
@Override
public String getHeader(final String name) {
return headers.getHeader(name);
}
@Override
public List<String> getHeaders(final String name) {
return headers.getHeaders(name);
}
@Override
public List<Map.Entry<String, String>> getHeaders() {
return headers.getHeaders();
}
@Override
public boolean containsHeader(final String name) {
return headers.containsHeader(name);
}
@Override
public Set<String> getHeaderNames() {
return headers.getHeaderNames();
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder(super.toString());
buf.append(getClass().getSimpleName());
buf.append(", size: ");
buf.append(getContent().readableBytes());
buf.append(", decodeResult: ");
buf.append(getDecoderResult());
buf.append(')');
buf.append(StringUtil.NEWLINE); buf.append(StringUtil.NEWLINE);
appendHeaders(buf); appendHeaders(buf);
@ -117,7 +71,7 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
} }
private void appendHeaders(StringBuilder buf) { private void appendHeaders(StringBuilder buf) {
for (Map.Entry<String, String> e: getHeaders()) { for (Map.Entry<String, String> e: trailingHeaders()) {
buf.append(e.getKey()); buf.append(e.getKey());
buf.append(": "); buf.append(": ");
buf.append(e.getValue()); buf.append(e.getValue());

View File

@ -0,0 +1,25 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http;
/**
* Combines {@link FullHttpMessage} and {@link LastHttpContent} into one
* message. So it represent a <i>complete</i> http message.
*/
public interface FullHttpMessage extends HttpMessage, LastHttpContent {
@Override
FullHttpMessage copy();
}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http;
/**
* Combinate the {@link HttpRequest} and {@link FullHttpMessage}, so the request is a <i>complete</i> HTTP
* request.
*/
public interface FullHttpRequest extends HttpRequest, FullHttpMessage {
@Override
FullHttpRequest copy();
}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http;
/**
* Combination of a {@link HttpResponse} and {@link FullHttpMessage}.
* So it represent a <i>complete</i> http response.
*/
public interface FullHttpResponse extends HttpResponse, FullHttpMessage {
@Override
FullHttpResponse copy();
}

View File

@ -84,8 +84,8 @@ public class HttpClientCodec extends CombinedChannelHandler {
@Override @Override
protected void encode( protected void encode(
ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception { ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
if (msg instanceof HttpRequestHeader && !done) { if (msg instanceof HttpRequest && !done) {
queue.offer(((HttpRequestHeader) msg).getMethod()); queue.offer(((HttpRequest) msg).method());
} }
super.encode(ctx, msg, out); super.encode(ctx, msg, out);
@ -137,8 +137,8 @@ public class HttpClientCodec extends CombinedChannelHandler {
} }
@Override @Override
protected boolean isContentAlwaysEmpty(HttpHeader msg) { protected boolean isContentAlwaysEmpty(HttpMessage msg) {
final int statusCode = ((HttpResponseHeader) msg).getStatus().getCode(); final int statusCode = ((HttpResponse) msg).status().code();
if (statusCode == 100) { if (statusCode == 100) {
// 100-continue response should be excluded from paired comparison. // 100-continue response should be excluded from paired comparison.
return true; return true;
@ -148,7 +148,7 @@ public class HttpClientCodec extends CombinedChannelHandler {
// current response. // current response.
HttpMethod method = queue.poll(); HttpMethod method = queue.poll();
char firstChar = method.getName().charAt(0); char firstChar = method.name().charAt(0);
switch (firstChar) { switch (firstChar) {
case 'H': case 'H':
// According to 4.3, RFC2616: // According to 4.3, RFC2616:

View File

@ -15,134 +15,19 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufHolder;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.DecoderResult;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
* An HTTP chunk which is used for HTTP chunked transfer-encoding. * An HTTP chunk which is used for HTTP chunked transfer-encoding.
* {@link HttpObjectDecoder} generates {@link HttpContent} after * {@link HttpObjectDecoder} generates {@link HttpContent} after
* {@link HttpHeader} when the content is large or the encoding of the content * {@link HttpMessage} when the content is large or the encoding of the content
* is 'chunked. If you prefer not to receive {@link HttpContent} in your handler, * is 'chunked. If you prefer not to receive {@link HttpContent} in your handler,
* please {@link HttpObjectAggregator} after {@link HttpObjectDecoder} in the * place {@link HttpObjectAggregator} after {@link HttpObjectDecoder} in the
* {@link ChannelPipeline}. * {@link ChannelPipeline}.
* @apiviz.landmark * @apiviz.landmark
*/ */
public interface HttpContent extends HttpObject { public interface HttpContent extends HttpObject, ByteBufHolder {
@Override
HttpContent EMPTY = new HttpContent() { HttpContent copy();
@Override
public ByteBuf getContent() {
return Unpooled.EMPTY_BUFFER;
}
@Override
public void setContent(ByteBuf content) {
throw new IllegalStateException("read-only");
}
@Override
public DecoderResult getDecoderResult() {
return DecoderResult.SUCCESS;
}
@Override
public void setDecoderResult(DecoderResult result) {
throw new IllegalStateException("read-only");
}
};
/**
* The 'end of content' marker in chunked encoding.
*/
LastHttpContent LAST_CONTENT = new LastHttpContent() {
@Override
public ByteBuf getContent() {
return Unpooled.EMPTY_BUFFER;
}
@Override
public void setContent(ByteBuf content) {
throw new IllegalStateException("read-only");
}
@Override
public void addHeader(String name, Object value) {
throw new IllegalStateException("read-only");
}
@Override
public void clearHeaders() {
// NOOP
}
@Override
public boolean containsHeader(String name) {
return false;
}
@Override
public String getHeader(String name) {
return null;
}
@Override
public Set<String> getHeaderNames() {
return Collections.emptySet();
}
@Override
public List<String> getHeaders(String name) {
return Collections.emptyList();
}
@Override
public List<Map.Entry<String, String>> getHeaders() {
return Collections.emptyList();
}
@Override
public void removeHeader(String name) {
// NOOP
}
@Override
public void setHeader(String name, Object value) {
throw new IllegalStateException("read-only");
}
@Override
public void setHeader(String name, Iterable<?> values) {
throw new IllegalStateException("read-only");
}
@Override
public DecoderResult getDecoderResult() {
return DecoderResult.SUCCESS;
}
@Override
public void setDecoderResult(DecoderResult result) {
throw new IllegalStateException("read-only");
}
};
/**
* Returns the content of this chunk. If this is the 'end of content'
* marker, {@link Unpooled#EMPTY_BUFFER} will be returned.
*/
ByteBuf getContent();
/**
* Sets the content of this chunk. If an empty buffer is specified,
* this chunk becomes the 'end of content' marker.
*/
void setContent(ByteBuf content);
} }

View File

@ -21,7 +21,7 @@ import io.netty.handler.codec.compression.ZlibWrapper;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
/** /**
* Compresses an {@link HttpHeader} and an {@link HttpContent} in {@code gzip} or * Compresses an {@link HttpMessage} and an {@link HttpContent} in {@code gzip} or
* {@code deflate} encoding while respecting the {@code "Accept-Encoding"} header. * {@code deflate} encoding while respecting the {@code "Accept-Encoding"} header.
* If there is no matching encoding, no compression is done. For more * If there is no matching encoding, no compression is done. For more
* information on how this handler modifies the message, please refer to * information on how this handler modifies the message, please refer to
@ -93,8 +93,8 @@ public class HttpContentCompressor extends HttpContentEncoder {
} }
@Override @Override
protected Result beginEncode(HttpHeader header, HttpContent msg, String acceptEncoding) throws Exception { protected Result beginEncode(HttpMessage header, HttpContent msg, String acceptEncoding) throws Exception {
String contentEncoding = header.getHeader(HttpHeaders.Names.CONTENT_ENCODING); String contentEncoding = header.headers().get(HttpHeaders.Names.CONTENT_ENCODING);
if (contentEncoding != null && if (contentEncoding != null &&
!HttpHeaders.Values.IDENTITY.equalsIgnoreCase(contentEncoding)) { !HttpHeaders.Values.IDENTITY.equalsIgnoreCase(contentEncoding)) {
return null; return null;

View File

@ -16,13 +16,14 @@
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufHolder;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.embedded.EmbeddedByteChannel; import io.netty.channel.embedded.EmbeddedByteChannel;
import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageDecoder;
/** /**
* Decodes the content of the received {@link HttpRequestHeader} and {@link HttpContent}. * Decodes the content of the received {@link HttpRequest} and {@link HttpContent}.
* The original content is replaced with the new content decoded by the * The original content is replaced with the new content decoded by the
* {@link EmbeddedByteChannel}, which is created by {@link #newContentDecoder(String)}. * {@link EmbeddedByteChannel}, which is created by {@link #newContentDecoder(String)}.
* Once decoding is finished, the value of the <tt>'Content-Encoding'</tt> * Once decoding is finished, the value of the <tt>'Content-Encoding'</tt>
@ -43,7 +44,7 @@ import io.netty.handler.codec.MessageToMessageDecoder;
public abstract class HttpContentDecoder extends MessageToMessageDecoder<Object> { public abstract class HttpContentDecoder extends MessageToMessageDecoder<Object> {
private EmbeddedByteChannel decoder; private EmbeddedByteChannel decoder;
private HttpHeader header; private HttpMessage header;
private boolean decodeStarted; private boolean decodeStarted;
/** /**
@ -55,13 +56,13 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<Object>
@Override @Override
protected Object decode(ChannelHandlerContext ctx, Object msg) throws Exception { protected Object decode(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpResponseHeader && ((HttpResponseHeader) msg).getStatus().getCode() == 100) { if (msg instanceof HttpResponse && ((HttpResponse) msg).status().code() == 100) {
// 100-continue response must be passed through. // 100-continue response must be passed through.
return msg; return msg;
} }
if (msg instanceof HttpHeader) { if (msg instanceof HttpMessage) {
assert header == null; assert header == null;
header = (HttpHeader) msg; header = (HttpMessage) msg;
cleanup(); cleanup();
} }
@ -71,11 +72,11 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<Object>
if (!decodeStarted) { if (!decodeStarted) {
decodeStarted = true; decodeStarted = true;
HttpHeader header = this.header; HttpMessage header = this.header;
this.header = null; this.header = null;
// Determine the content encoding. // Determine the content encoding.
String contentEncoding = header.getHeader(HttpHeaders.Names.CONTENT_ENCODING); String contentEncoding = header.headers().get(HttpHeaders.Names.CONTENT_ENCODING);
if (contentEncoding != null) { if (contentEncoding != null) {
contentEncoding = contentEncoding.trim(); contentEncoding = contentEncoding.trim();
} else { } else {
@ -89,17 +90,17 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<Object>
if (HttpHeaders.Values.IDENTITY.equals(targetContentEncoding)) { if (HttpHeaders.Values.IDENTITY.equals(targetContentEncoding)) {
// Do NOT set the 'Content-Encoding' header if the target encoding is 'identity' // Do NOT set the 'Content-Encoding' header if the target encoding is 'identity'
// as per: http://tools.ietf.org/html/rfc2616#section-14.11 // as per: http://tools.ietf.org/html/rfc2616#section-14.11
header.removeHeader(HttpHeaders.Names.CONTENT_ENCODING); header.headers().remove(HttpHeaders.Names.CONTENT_ENCODING);
} else { } else {
header.setHeader(HttpHeaders.Names.CONTENT_ENCODING, targetContentEncoding); header.headers().set(HttpHeaders.Names.CONTENT_ENCODING, targetContentEncoding);
} }
Object[] decoded = decodeContent(header, c); Object[] decoded = decodeContent(header, c);
// Replace the content. // Replace the content.
if (header.containsHeader(HttpHeaders.Names.CONTENT_LENGTH)) { if (header.headers().contains(HttpHeaders.Names.CONTENT_LENGTH)) {
header.setHeader( header.headers().set(
HttpHeaders.Names.CONTENT_LENGTH, HttpHeaders.Names.CONTENT_LENGTH,
Integer.toString(((HttpContent) decoded[1]).getContent().readableBytes())); Integer.toString(((ByteBufHolder) decoded[1]).data().readableBytes()));
} }
return decoded; return decoded;
} }
@ -108,13 +109,13 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<Object>
return decodeContent(null, c); return decodeContent(null, c);
} }
// Because HttpMessage and HttpChunk is a mutable object, we can simply forward it. // Because FullHttpMessage and HttpChunk is a mutable object, we can simply forward it.
return msg; return msg;
} }
private Object[] decodeContent(HttpHeader header, HttpContent c) { private Object[] decodeContent(HttpMessage header, HttpContent c) {
ByteBuf newContent = Unpooled.buffer(); ByteBuf newContent = Unpooled.buffer();
ByteBuf content = c.getContent(); ByteBuf content = c.data();
decode(content, newContent); decode(content, newContent);
if (c instanceof LastHttpContent) { if (c instanceof LastHttpContent) {

View File

@ -20,7 +20,7 @@ import io.netty.handler.codec.compression.ZlibCodecFactory;
import io.netty.handler.codec.compression.ZlibWrapper; import io.netty.handler.codec.compression.ZlibWrapper;
/** /**
* Decompresses an {@link HttpHeader} and an {@link HttpContent} compressed in * Decompresses an {@link HttpMessage} and an {@link HttpContent} compressed in
* {@code gzip} or {@code deflate} encoding. For more information on how this * {@code gzip} or {@code deflate} encoding. For more information on how this
* handler modifies the message, please refer to {@link HttpContentDecoder}. * handler modifies the message, please refer to {@link HttpContentDecoder}.
*/ */

View File

@ -16,6 +16,7 @@
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufHolder;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.embedded.EmbeddedByteChannel; import io.netty.channel.embedded.EmbeddedByteChannel;
@ -25,20 +26,20 @@ import java.util.ArrayDeque;
import java.util.Queue; import java.util.Queue;
/** /**
* Encodes the content of the outbound {@link HttpResponseHeader} and {@link HttpContent}. * Encodes the content of the outbound {@link HttpResponse} and {@link HttpContent}.
* The original content is replaced with the new content encoded by the * The original content is replaced with the new content encoded by the
* {@link EmbeddedByteChannel}, which is created by {@link #beginEncode(HttpHeader, HttpContent, String)}. * {@link EmbeddedByteChannel}, which is created by {@link #beginEncode(HttpMessage, HttpContent, String)}.
* Once encoding is finished, the value of the <tt>'Content-Encoding'</tt> header * Once encoding is finished, the value of the <tt>'Content-Encoding'</tt> header
* is set to the target content encoding, as returned by * is set to the target content encoding, as returned by
* {@link #beginEncode(HttpHeader, HttpContent, String)}. * {@link #beginEncode(HttpMessage, HttpContent, String)}.
* Also, the <tt>'Content-Length'</tt> header is updated to the length of the * Also, the <tt>'Content-Length'</tt> header is updated to the length of the
* encoded content. If there is no supported or allowed encoding in the * encoded content. If there is no supported or allowed encoding in the
* corresponding {@link HttpRequestHeader}'s {@code "Accept-Encoding"} header, * corresponding {@link HttpRequest}'s {@code "Accept-Encoding"} header,
* {@link #beginEncode(HttpHeader, HttpContent, String)} should return {@code null} so that * {@link #beginEncode(HttpMessage, HttpContent, String)} should return {@code null} so that
* no encoding occurs (i.e. pass-through). * no encoding occurs (i.e. pass-through).
* <p> * <p>
* Please note that this is an abstract class. You have to extend this class * Please note that this is an abstract class. You have to extend this class
* and implement {@link #beginEncode(HttpHeader, HttpContent, String)} properly to make * and implement {@link #beginEncode(HttpMessage, HttpContent, String)} properly to make
* this class functional. For example, refer to the source code of * this class functional. For example, refer to the source code of
* {@link HttpContentCompressor}. * {@link HttpContentCompressor}.
* <p> * <p>
@ -46,11 +47,11 @@ import java.util.Queue;
* so that this handler can intercept HTTP responses before {@link HttpObjectEncoder} * so that this handler can intercept HTTP responses before {@link HttpObjectEncoder}
* converts them into {@link ByteBuf}s. * converts them into {@link ByteBuf}s.
*/ */
public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeader, Object> { public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpMessage, Object> {
private final Queue<String> acceptEncodingQueue = new ArrayDeque<String>(); private final Queue<String> acceptEncodingQueue = new ArrayDeque<String>();
private EmbeddedByteChannel encoder; private EmbeddedByteChannel encoder;
private HttpHeader header; private HttpMessage header;
private boolean encodeStarted; private boolean encodeStarted;
/** /**
@ -58,14 +59,14 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeade
*/ */
protected HttpContentEncoder() { protected HttpContentEncoder() {
super( super(
new Class<?>[] { HttpHeader.class }, new Class<?>[] { HttpMessage.class },
new Class<?>[] { HttpObject.class }); new Class<?>[] { HttpObject.class });
} }
@Override @Override
protected Object decode(ChannelHandlerContext ctx, HttpHeader msg) protected Object decode(ChannelHandlerContext ctx, HttpMessage msg)
throws Exception { throws Exception {
String acceptedEncoding = msg.getHeader(HttpHeaders.Names.ACCEPT_ENCODING); String acceptedEncoding = msg.headers().get(HttpHeaders.Names.ACCEPT_ENCODING);
if (acceptedEncoding == null) { if (acceptedEncoding == null) {
acceptedEncoding = HttpHeaders.Values.IDENTITY; acceptedEncoding = HttpHeaders.Values.IDENTITY;
} }
@ -78,31 +79,31 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeade
public Object encode(ChannelHandlerContext ctx, Object msg) public Object encode(ChannelHandlerContext ctx, Object msg)
throws Exception { throws Exception {
if (msg instanceof HttpResponseHeader && ((HttpResponseHeader) msg).getStatus().getCode() == 100) { if (msg instanceof HttpResponse && ((HttpResponse) msg).status().code() == 100) {
// 100-continue response must be passed through. // 100-continue response must be passed through.
return msg; return msg;
} }
if (msg instanceof HttpHeader) { if (msg instanceof HttpMessage) {
assert header == null; assert header == null;
// check if this message is also of type HttpContent is such case just make a safe copy of the headers // check if this message is also of type HttpContent is such case just make a safe copy of the headers
// as the content will get handled later and this simplify the handling // as the content will get handled later and this simplify the handling
if (msg instanceof HttpContent) { if (msg instanceof HttpContent) {
if (msg instanceof HttpRequestHeader) { if (msg instanceof HttpRequest) {
HttpRequestHeader reqHeader = (HttpRequestHeader) msg; HttpRequest reqHeader = (HttpRequest) msg;
header = new DefaultHttpRequestHeader(reqHeader.getProtocolVersion(), reqHeader.getMethod(), header = new DefaultHttpRequest(reqHeader.protocolVersion(), reqHeader.method(),
reqHeader.getUri()); reqHeader.uri());
HttpHeaders.setHeaders(reqHeader, header); HttpHeaders.setHeaders(reqHeader, header);
} else if (msg instanceof HttpResponseHeader) { } else if (msg instanceof HttpResponse) {
HttpResponseHeader responseHeader = (HttpResponseHeader) msg; HttpResponse responseHeader = (HttpResponse) msg;
header = new DefaultHttpResponseHeader(responseHeader.getProtocolVersion(), header = new DefaultHttpResponse(responseHeader.protocolVersion(),
responseHeader.getStatus()); responseHeader.status());
HttpHeaders.setHeaders(responseHeader, header); HttpHeaders.setHeaders(responseHeader, header);
} else { } else {
return msg; return msg;
} }
} else { } else {
header = (HttpHeader) msg; header = (HttpMessage) msg;
} }
cleanup(); cleanup();
@ -113,7 +114,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeade
if (!encodeStarted) { if (!encodeStarted) {
encodeStarted = true; encodeStarted = true;
HttpHeader header = this.header; HttpMessage header = this.header;
this.header = null; this.header = null;
// Determine the content encoding. // Determine the content encoding.
@ -124,25 +125,29 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeade
Result result = beginEncode(header, c, acceptEncoding); Result result = beginEncode(header, c, acceptEncoding);
if (result == null) { if (result == null) {
return new Object[] { header, c }; if (c instanceof LastHttpContent) {
return new Object[] { header, new DefaultLastHttpContent(c.data()) };
} else {
return new Object[] { header, new DefaultHttpContent(c.data()) };
}
} }
encoder = result.getContentEncoder(); encoder = result.getContentEncoder();
// Encode the content and remove or replace the existing headers // Encode the content and remove or replace the existing headers
// so that the message looks like a decoded message. // so that the message looks like a decoded message.
header.setHeader( header.headers().set(
HttpHeaders.Names.CONTENT_ENCODING, HttpHeaders.Names.CONTENT_ENCODING,
result.getTargetContentEncoding()); result.getTargetContentEncoding());
Object[] encoded = encodeContent(header, c); Object[] encoded = encodeContent(header, c);
if (!HttpHeaders.isTransferEncodingChunked(header) && encoded.length == 3) { if (!HttpHeaders.isTransferEncodingChunked(header) && encoded.length == 3) {
if (header.containsHeader(HttpHeaders.Names.CONTENT_LENGTH)) { if (header.headers().contains(HttpHeaders.Names.CONTENT_LENGTH)) {
long length = ((HttpContent) encoded[1]).getContent().readableBytes() + long length = ((ByteBufHolder) encoded[1]).data().readableBytes() +
((HttpContent) encoded[2]).getContent().readableBytes(); ((ByteBufHolder) encoded[2]).data().readableBytes();
header.setHeader( header.headers().set(
HttpHeaders.Names.CONTENT_LENGTH, HttpHeaders.Names.CONTENT_LENGTH,
Long.toString(length)); Long.toString(length));
} }
@ -157,9 +162,9 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeade
return null; return null;
} }
private Object[] encodeContent(HttpHeader header, HttpContent c) { private Object[] encodeContent(HttpMessage header, HttpContent c) {
ByteBuf newContent = Unpooled.buffer(); ByteBuf newContent = Unpooled.buffer();
ByteBuf content = c.getContent(); ByteBuf content = c.data();
encode(content, newContent); encode(content, newContent);
if (c instanceof LastHttpContent) { if (c instanceof LastHttpContent) {
@ -200,7 +205,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeade
* {@code null} if {@code acceptEncoding} is unsupported or rejected * {@code null} if {@code acceptEncoding} is unsupported or rejected
* and thus the content should be handled as-is (i.e. no encoding). * and thus the content should be handled as-is (i.e. no encoding).
*/ */
protected abstract Result beginEncode(HttpHeader header, HttpContent msg, String acceptEncoding) throws Exception; protected abstract Result beginEncode(HttpMessage header, HttpContent msg, String acceptEncoding) throws Exception;
@Override @Override
public void afterRemove(ChannelHandlerContext ctx) throws Exception { public void afterRemove(ChannelHandlerContext ctx) throws Exception {

View File

@ -1,150 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* An interface that defines a HTTP message, providing common properties for
* {@link HttpRequestHeader} and {@link HttpResponseHeader}.
* @see HttpResponseHeader
* @see HttpRequestHeader
* @see HttpHeaders
*
* @apiviz.landmark
* @apiviz.has io.netty.handler.codec.http.HttpChunk oneway - - is followed by
*/
public interface HttpHeader extends HttpObject {
/**
* Returns the value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name The name of the header to search
* @return The first header value or {@code null} if there is no such header
*/
String getHeader(String name);
/**
* Returns the values of headers with the specified name
*
* @param name The name of the headers to search
* @return A {@link List} of header values which will be empty if no values
* are found
*/
List<String> getHeaders(String name);
/**
* Returns the all headers that this message contains.
*
* @return A {@link List} of the header name-value entries, which will be
* empty if no pairs are found
*/
List<Map.Entry<String, String>> getHeaders();
/**
* Checks to see if there is a header with the specified name
*
* @param name The name of the header to search for
* @return True if at least one header is found
*/
boolean containsHeader(String name);
/**
* Gets a {@link Set} of all header names that this message contains
*
* @return A {@link Set} of all header names
*/
Set<String> getHeaderNames();
/**
* Returns the protocol version of this {@link HttpHeader}
*
* @return The protocol version
*/
HttpVersion getProtocolVersion();
/**
* Sets the protocol version of this {@link HttpHeader}
*
* @param version The version to set
*/
void setProtocolVersion(HttpVersion version);
/**
* Adds a new header with the specified name and value.
*
* If the specified value is not a {@link String}, it is converted
* into a {@link String} by {@link Object#toString()}, except in the cases
* of {@link Date} and {@link Calendar}, which are formatted to the date
* format defined in <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
*
* @param name The name of the header being added
* @param value The value of the header being added
*/
void addHeader(String name, Object value);
/**
* Sets a header with the specified name and value.
*
* If there is an existing header with the same name, it is removed.
* If the specified value is not a {@link String}, it is converted into a
* {@link String} by {@link Object#toString()}, except for {@link Date}
* and {@link Calendar}, which are formatted to the date format defined in
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
*
* @param name The name of the header being set
* @param value The value of the header being set
*/
void setHeader(String name, Object value);
/**
* Sets a header with the specified name and values.
*
* If there is an existing header with the same name, it is removed.
* This method can be represented approximately as the following code:
* <pre>
* m.removeHeader(name);
* for (Object v: values) {
* if (v == null) {
* break;
* }
* m.addHeader(name, v);
* }
* </pre>
*
* @param name The name of the headers being set
* @param values The values of the headers being set
*/
void setHeader(String name, Iterable<?> values);
/**
* Removes the header with the specified name.
*
* @param name The name of the header to remove
*/
void removeHeader(String name);
/**
* Removes all headers from this {@link HttpHeader}.
*/
void clearHeaders();
}

View File

@ -17,21 +17,89 @@ package io.netty.handler.codec.http;
import java.text.ParseException; import java.text.ParseException;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.LinkedList; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.TreeSet;
/** /**
* Provides the constants for the standard HTTP header names and values and * Provides the constants for the standard HTTP header names and values and
* commonly used utility methods that accesses an {@link HttpHeader}. * commonly used utility methods that accesses an {@link HttpMessage}.
* @apiviz.landmark * @apiviz.landmark
* @apiviz.stereotype static * @apiviz.stereotype static
*/ */
public class HttpHeaders { public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>> {
public static final HttpHeaders EMPTY_HEADERS = new HttpHeaders() {
@Override
public String get(String name) {
return null;
}
@Override
public List<String> getAll(String name) {
return Collections.emptyList();
}
@Override
public List<Entry<String, String>> entries() {
return Collections.emptyList();
}
@Override
public boolean contains(String name) {
return false;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public Set<String> names() {
return Collections.emptySet();
}
@Override
public void add(String name, Object value) {
throw new UnsupportedOperationException("read only");
}
@Override
public void add(String name, Iterable<?> values) {
throw new UnsupportedOperationException("read only");
}
@Override
public void set(String name, Object value) {
throw new UnsupportedOperationException("read only");
}
@Override
public void set(String name, Iterable<?> values) {
throw new UnsupportedOperationException("read only");
}
@Override
public void remove(String name) {
throw new UnsupportedOperationException("read only");
}
@Override
public void clear() {
throw new UnsupportedOperationException("read only");
}
@Override
public Iterator<Entry<String, String>> iterator() {
return entries().iterator();
}
};
/** /**
* Standard and CORS HTTP header names. * Standard and CORS HTTP header names.
@ -483,13 +551,13 @@ public class HttpHeaders {
* {@code "Connection"} header first and then the return value of * {@code "Connection"} header first and then the return value of
* {@link HttpVersion#isKeepAliveDefault()}. * {@link HttpVersion#isKeepAliveDefault()}.
*/ */
public static boolean isKeepAlive(HttpHeader message) { public static boolean isKeepAlive(HttpMessage message) {
String connection = message.getHeader(Names.CONNECTION); String connection = message.headers().get(Names.CONNECTION);
if (Values.CLOSE.equalsIgnoreCase(connection)) { if (Values.CLOSE.equalsIgnoreCase(connection)) {
return false; return false;
} }
if (message.getProtocolVersion().isKeepAliveDefault()) { if (message.protocolVersion().isKeepAliveDefault()) {
return !Values.CLOSE.equalsIgnoreCase(connection); return !Values.CLOSE.equalsIgnoreCase(connection);
} else { } else {
return Values.KEEP_ALIVE.equalsIgnoreCase(connection); return Values.KEEP_ALIVE.equalsIgnoreCase(connection);
@ -515,18 +583,18 @@ public class HttpHeaders {
* </ul></li> * </ul></li>
* </ul> * </ul>
*/ */
public static void setKeepAlive(HttpHeader message, boolean keepAlive) { public static void setKeepAlive(HttpMessage message, boolean keepAlive) {
if (message.getProtocolVersion().isKeepAliveDefault()) { if (message.protocolVersion().isKeepAliveDefault()) {
if (keepAlive) { if (keepAlive) {
message.removeHeader(Names.CONNECTION); message.headers().remove(Names.CONNECTION);
} else { } else {
message.setHeader(Names.CONNECTION, Values.CLOSE); message.headers().set(Names.CONNECTION, Values.CLOSE);
} }
} else { } else {
if (keepAlive) { if (keepAlive) {
message.setHeader(Names.CONNECTION, Values.KEEP_ALIVE); message.headers().set(Names.CONNECTION, Values.KEEP_ALIVE);
} else { } else {
message.removeHeader(Names.CONNECTION); message.headers().remove(Names.CONNECTION);
} }
} }
} }
@ -538,8 +606,8 @@ public class HttpHeaders {
* *
* @return the header value or {@code null} if there is no such header * @return the header value or {@code null} if there is no such header
*/ */
public static String getHeader(HttpHeader message, String name) { public static String getHeader(HttpMessage message, String name) {
return message.getHeader(name); return message.headers().get(name);
} }
/** /**
@ -550,8 +618,8 @@ public class HttpHeaders {
* @return the header value or the {@code defaultValue} if there is no such * @return the header value or the {@code defaultValue} if there is no such
* header * header
*/ */
public static String getHeader(HttpHeader message, String name, String defaultValue) { public static String getHeader(HttpMessage message, String name, String defaultValue) {
String value = message.getHeader(name); String value = message.headers().get(name);
if (value == null) { if (value == null) {
return defaultValue; return defaultValue;
} }
@ -566,8 +634,8 @@ public class HttpHeaders {
* and {@link Calendar} which are formatted to the date format defined in * and {@link Calendar} which are formatted to the date format defined in
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>. * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
*/ */
public static void setHeader(HttpHeader message, String name, Object value) { public static void setHeader(HttpMessage message, String name, Object value) {
message.setHeader(name, value); message.headers().set(name, value);
} }
/** /**
@ -584,8 +652,8 @@ public class HttpHeaders {
* } * }
* </pre> * </pre>
*/ */
public static void setHeader(HttpHeader message, String name, Iterable<?> values) { public static void setHeader(HttpMessage message, String name, Iterable<?> values) {
message.setHeader(name, values); message.headers().set(name, values);
} }
/** /**
@ -595,22 +663,22 @@ public class HttpHeaders {
* and {@link Calendar} which are formatted to the date format defined in * and {@link Calendar} which are formatted to the date format defined in
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>. * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
*/ */
public static void addHeader(HttpHeader message, String name, Object value) { public static void addHeader(HttpMessage message, String name, Object value) {
message.addHeader(name, value); message.headers().add(name, value);
} }
/** /**
* Removes the header with the specified name. * Removes the header with the specified name.
*/ */
public static void removeHeader(HttpHeader message, String name) { public static void removeHeader(HttpMessage message, String name) {
message.removeHeader(name); message.headers().remove(name);
} }
/** /**
* Removes all headers from the specified message. * Removes all headers from the specified message.
*/ */
public static void clearHeaders(HttpHeader message) { public static void clearHeaders(HttpMessage message) {
message.clearHeaders(); message.headers().clear();
} }
/** /**
@ -622,7 +690,7 @@ public class HttpHeaders {
* @throws NumberFormatException * @throws NumberFormatException
* if there is no such header or the header value is not a number * if there is no such header or the header value is not a number
*/ */
public static int getIntHeader(HttpHeader message, String name) { public static int getIntHeader(HttpMessage message, String name) {
String value = getHeader(message, name); String value = getHeader(message, name);
if (value == null) { if (value == null) {
throw new NumberFormatException("header not found: " + name); throw new NumberFormatException("header not found: " + name);
@ -638,7 +706,7 @@ public class HttpHeaders {
* @return the header value or the {@code defaultValue} if there is no such * @return the header value or the {@code defaultValue} if there is no such
* header or the header value is not a number * header or the header value is not a number
*/ */
public static int getIntHeader(HttpHeader message, String name, int defaultValue) { public static int getIntHeader(HttpMessage message, String name, int defaultValue) {
String value = getHeader(message, name); String value = getHeader(message, name);
if (value == null) { if (value == null) {
return defaultValue; return defaultValue;
@ -655,23 +723,23 @@ public class HttpHeaders {
* Sets a new integer header with the specified name and value. If there * Sets a new integer header with the specified name and value. If there
* is an existing header with the same name, the existing header is removed. * is an existing header with the same name, the existing header is removed.
*/ */
public static void setIntHeader(HttpHeader message, String name, int value) { public static void setIntHeader(HttpMessage message, String name, int value) {
message.setHeader(name, value); message.headers().set(name, value);
} }
/** /**
* Sets a new integer header with the specified name and values. If there * Sets a new integer header with the specified name and values. If there
* is an existing header with the same name, the existing header is removed. * is an existing header with the same name, the existing header is removed.
*/ */
public static void setIntHeader(HttpHeader message, String name, Iterable<Integer> values) { public static void setIntHeader(HttpMessage message, String name, Iterable<Integer> values) {
message.setHeader(name, values); message.headers().set(name, values);
} }
/** /**
* Adds a new integer header with the specified name and value. * Adds a new integer header with the specified name and value.
*/ */
public static void addIntHeader(HttpHeader message, String name, int value) { public static void addIntHeader(HttpMessage message, String name, int value) {
message.addHeader(name, value); message.headers().add(name, value);
} }
/** /**
@ -683,7 +751,7 @@ public class HttpHeaders {
* @throws ParseException * @throws ParseException
* if there is no such header or the header value is not a formatted date * if there is no such header or the header value is not a formatted date
*/ */
public static Date getDateHeader(HttpHeader message, String name) throws ParseException { public static Date getDateHeader(HttpMessage message, String name) throws ParseException {
String value = getHeader(message, name); String value = getHeader(message, name);
if (value == null) { if (value == null) {
throw new ParseException("header not found: " + name, 0); throw new ParseException("header not found: " + name, 0);
@ -699,7 +767,7 @@ public class HttpHeaders {
* @return the header value or the {@code defaultValue} if there is no such * @return the header value or the {@code defaultValue} if there is no such
* header or the header value is not a formatted date * header or the header value is not a formatted date
*/ */
public static Date getDateHeader(HttpHeader message, String name, Date defaultValue) { public static Date getDateHeader(HttpMessage message, String name, Date defaultValue) {
final String value = getHeader(message, name); final String value = getHeader(message, name);
if (value == null) { if (value == null) {
return defaultValue; return defaultValue;
@ -718,11 +786,11 @@ public class HttpHeaders {
* The specified value is formatted as defined in * The specified value is formatted as defined in
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a> * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>
*/ */
public static void setDateHeader(HttpHeader message, String name, Date value) { public static void setDateHeader(HttpMessage message, String name, Date value) {
if (value != null) { if (value != null) {
message.setHeader(name, new HttpHeaderDateFormat().format(value)); message.headers().set(name, new HttpHeaderDateFormat().format(value));
} else { } else {
message.setHeader(name, null); message.headers().set(name, null);
} }
} }
@ -732,8 +800,8 @@ public class HttpHeaders {
* The specified values are formatted as defined in * The specified values are formatted as defined in
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a> * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>
*/ */
public static void setDateHeader(HttpHeader message, String name, Iterable<Date> values) { public static void setDateHeader(HttpMessage message, String name, Iterable<Date> values) {
message.setHeader(name, values); message.headers().set(name, values);
} }
/** /**
@ -741,13 +809,13 @@ public class HttpHeaders {
* value is formatted as defined in * value is formatted as defined in
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a> * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>
*/ */
public static void addDateHeader(HttpHeader message, String name, Date value) { public static void addDateHeader(HttpMessage message, String name, Date value) {
message.addHeader(name, value); message.headers().add(name, value);
} }
/** /**
* Returns the length of the content. Please note that this value is * Returns the length of the content. Please note that this value is
* not retrieved from {@link HttpContent#getContent()} but from the * not retrieved from {@link HttpContent#data()} but from the
* {@code "Content-Length"} header, and thus they are independent from each * {@code "Content-Length"} header, and thus they are independent from each
* other. * other.
* *
@ -757,7 +825,7 @@ public class HttpHeaders {
* if the message does not have the {@code "Content-Length"} header * if the message does not have the {@code "Content-Length"} header
* or its value is not a number * or its value is not a number
*/ */
public static long getContentLength(HttpHeader message) { public static long getContentLength(HttpMessage message) {
String value = getHeader(message, Names.CONTENT_LENGTH); String value = getHeader(message, Names.CONTENT_LENGTH);
if (value != null) { if (value != null) {
return Long.parseLong(value); return Long.parseLong(value);
@ -776,7 +844,7 @@ public class HttpHeaders {
/** /**
* Returns the length of the content. Please note that this value is * Returns the length of the content. Please note that this value is
* not retrieved from {@link HttpContent#getContent()} but from the * not retrieved from {@link HttpContent#data()} but from the
* {@code "Content-Length"} header, and thus they are independent from each * {@code "Content-Length"} header, and thus they are independent from each
* other. * other.
* *
@ -784,8 +852,8 @@ public class HttpHeaders {
* not have the {@code "Content-Length"} header or its value is not * not have the {@code "Content-Length"} header or its value is not
* a number * a number
*/ */
public static long getContentLength(HttpHeader message, long defaultValue) { public static long getContentLength(HttpMessage message, long defaultValue) {
String contentLength = message.getHeader(Names.CONTENT_LENGTH); String contentLength = message.headers().get(Names.CONTENT_LENGTH);
if (contentLength != null) { if (contentLength != null) {
try { try {
return Long.parseLong(contentLength); return Long.parseLong(contentLength);
@ -809,20 +877,20 @@ public class HttpHeaders {
* Returns the content length of the specified web socket message. If the * Returns the content length of the specified web socket message. If the
* specified message is not a web socket message, {@code -1} is returned. * specified message is not a web socket message, {@code -1} is returned.
*/ */
private static int getWebSocketContentLength(HttpHeader message) { private static int getWebSocketContentLength(HttpMessage message) {
// WebSockset messages have constant content-lengths. // WebSockset messages have constant content-lengths.
if (message instanceof HttpRequestHeader) { if (message instanceof HttpRequest) {
HttpRequestHeader req = (HttpRequestHeader) message; HttpRequest req = (HttpRequest) message;
if (HttpMethod.GET.equals(req.getMethod()) && if (HttpMethod.GET.equals(req.method()) &&
req.containsHeader(Names.SEC_WEBSOCKET_KEY1) && req.headers().contains(Names.SEC_WEBSOCKET_KEY1) &&
req.containsHeader(Names.SEC_WEBSOCKET_KEY2)) { req.headers().contains(Names.SEC_WEBSOCKET_KEY2)) {
return 8; return 8;
} }
} else if (message instanceof HttpResponseHeader) { } else if (message instanceof HttpResponse) {
HttpResponseHeader res = (HttpResponseHeader) message; HttpResponse res = (HttpResponse) message;
if (res.getStatus().getCode() == 101 && if (res.status().code() == 101 &&
res.containsHeader(Names.SEC_WEBSOCKET_ORIGIN) && res.headers().contains(Names.SEC_WEBSOCKET_ORIGIN) &&
res.containsHeader(Names.SEC_WEBSOCKET_LOCATION)) { res.headers().contains(Names.SEC_WEBSOCKET_LOCATION)) {
return 16; return 16;
} }
} }
@ -834,30 +902,30 @@ public class HttpHeaders {
/** /**
* Sets the {@code "Content-Length"} header. * Sets the {@code "Content-Length"} header.
*/ */
public static void setContentLength(HttpHeader message, long length) { public static void setContentLength(HttpMessage message, long length) {
message.setHeader(Names.CONTENT_LENGTH, length); message.headers().set(Names.CONTENT_LENGTH, length);
} }
/** /**
* Returns the value of the {@code "Host"} header. * Returns the value of the {@code "Host"} header.
*/ */
public static String getHost(HttpHeader message) { public static String getHost(HttpMessage message) {
return message.getHeader(Names.HOST); return message.headers().get(Names.HOST);
} }
/** /**
* Returns the value of the {@code "Host"} header. If there is no such * Returns the value of the {@code "Host"} header. If there is no such
* header, the {@code defaultValue} is returned. * header, the {@code defaultValue} is returned.
*/ */
public static String getHost(HttpHeader message, String defaultValue) { public static String getHost(HttpMessage message, String defaultValue) {
return getHeader(message, Names.HOST, defaultValue); return getHeader(message, Names.HOST, defaultValue);
} }
/** /**
* Sets the {@code "Host"} header. * Sets the {@code "Host"} header.
*/ */
public static void setHost(HttpHeader message, String value) { public static void setHost(HttpMessage message, String value) {
message.setHeader(Names.HOST, value); message.headers().set(Names.HOST, value);
} }
/** /**
@ -866,7 +934,7 @@ public class HttpHeaders {
* @throws ParseException * @throws ParseException
* if there is no such header or the header value is not a formatted date * if there is no such header or the header value is not a formatted date
*/ */
public static Date getDate(HttpHeader message) throws ParseException { public static Date getDate(HttpMessage message) throws ParseException {
return getDateHeader(message, Names.DATE); return getDateHeader(message, Names.DATE);
} }
@ -875,18 +943,18 @@ public class HttpHeaders {
* header or the header is not a formatted date, the {@code defaultValue} * header or the header is not a formatted date, the {@code defaultValue}
* is returned. * is returned.
*/ */
public static Date getDate(HttpHeader message, Date defaultValue) { public static Date getDate(HttpMessage message, Date defaultValue) {
return getDateHeader(message, Names.DATE, defaultValue); return getDateHeader(message, Names.DATE, defaultValue);
} }
/** /**
* Sets the {@code "Date"} header. * Sets the {@code "Date"} header.
*/ */
public static void setDate(HttpHeader message, Date value) { public static void setDate(HttpMessage message, Date value) {
if (value != null) { if (value != null) {
message.setHeader(Names.DATE, new HttpHeaderDateFormat().format(value)); message.headers().set(Names.DATE, new HttpHeaderDateFormat().format(value));
} else { } else {
message.setHeader(Names.DATE, null); message.headers().set(Names.DATE, null);
} }
} }
@ -894,19 +962,19 @@ public class HttpHeaders {
* Returns {@code true} if and only if the specified message contains the * Returns {@code true} if and only if the specified message contains the
* {@code "Expect: 100-continue"} header. * {@code "Expect: 100-continue"} header.
*/ */
public static boolean is100ContinueExpected(HttpHeader message) { public static boolean is100ContinueExpected(HttpMessage message) {
// Expect: 100-continue is for requests only. // Expect: 100-continue is for requests only.
if (!(message instanceof HttpRequestHeader)) { if (!(message instanceof HttpRequest)) {
return false; return false;
} }
// It works only on HTTP/1.1 or later. // It works only on HTTP/1.1 or later.
if (message.getProtocolVersion().compareTo(HttpVersion.HTTP_1_1) < 0) { if (message.protocolVersion().compareTo(HttpVersion.HTTP_1_1) < 0) {
return false; return false;
} }
// In most cases, there will be one or zero 'Expect' header. // In most cases, there will be one or zero 'Expect' header.
String value = message.getHeader(Names.EXPECT); String value = message.headers().get(Names.EXPECT);
if (value == null) { if (value == null) {
return false; return false;
} }
@ -915,7 +983,7 @@ public class HttpHeaders {
} }
// Multiple 'Expect' headers. Search through them. // Multiple 'Expect' headers. Search through them.
for (String v: message.getHeaders(Names.EXPECT)) { for (String v: message.headers().getAll(Names.EXPECT)) {
if (Values.CONTINUE.equalsIgnoreCase(v)) { if (Values.CONTINUE.equalsIgnoreCase(v)) {
return true; return true;
} }
@ -928,7 +996,7 @@ public class HttpHeaders {
* If there is any existing {@code "Expect"} header, they are replaced with * If there is any existing {@code "Expect"} header, they are replaced with
* the new one. * the new one.
*/ */
public static void set100ContinueExpected(HttpHeader message) { public static void set100ContinueExpected(HttpMessage message) {
set100ContinueExpected(message, true); set100ContinueExpected(message, true);
} }
@ -939,20 +1007,20 @@ public class HttpHeaders {
* {@code "Expect"} headers are removed. Otherwise, all {@code "Expect"} * {@code "Expect"} headers are removed. Otherwise, all {@code "Expect"}
* headers are removed completely. * headers are removed completely.
*/ */
public static void set100ContinueExpected(HttpHeader message, boolean set) { public static void set100ContinueExpected(HttpMessage message, boolean set) {
if (set) { if (set) {
message.setHeader(Names.EXPECT, Values.CONTINUE); message.headers().set(Names.EXPECT, Values.CONTINUE);
} else { } else {
message.removeHeader(Names.EXPECT); message.headers().remove(Names.EXPECT);
} }
} }
/** /**
* Set the headers on the dst like they are set on the src * Set the headers on the dst like they are set on the src
*/ */
public static void setHeaders(HttpHeader src, HttpHeader dst) { public static void setHeaders(HttpMessage src, HttpMessage dst) {
for (String name: src.getHeaderNames()) { for (String name: src.headers().names()) {
dst.setHeader(name, src.getHeaders(name)); dst.headers().set(name, src.headers().getAll(name));
} }
} }
/** /**
@ -1065,13 +1133,13 @@ public class HttpHeaders {
} }
/** /**
* Checks to see if the transfer encoding in a specified {@link HttpHeader} is chunked * Checks to see if the transfer encoding in a specified {@link HttpMessage} is chunked
* *
* @param message The message to check * @param message The message to check
* @return True if transfer encoding is chunked, otherwise false * @return True if transfer encoding is chunked, otherwise false
*/ */
public static boolean isTransferEncodingChunked(HttpHeader message) { public static boolean isTransferEncodingChunked(HttpMessage message) {
List<String> transferEncodingHeaders = message.getHeaders(Names.TRANSFER_ENCODING); List<String> transferEncodingHeaders = message.headers().getAll(Names.TRANSFER_ENCODING);
if (transferEncodingHeaders.isEmpty()) { if (transferEncodingHeaders.isEmpty()) {
return false; return false;
} }
@ -1084,322 +1152,167 @@ public class HttpHeaders {
return false; return false;
} }
public static void removeTransferEncodingChunked(HttpHeader m) { public static void removeTransferEncodingChunked(HttpMessage m) {
List<String> values = m.getHeaders(Names.TRANSFER_ENCODING); List<String> values = m.headers().getAll(Names.TRANSFER_ENCODING);
values.remove(Values.CHUNKED); values.remove(Values.CHUNKED);
if (values.isEmpty()) { if (values.isEmpty()) {
m.removeHeader(Names.TRANSFER_ENCODING); m.headers().remove(Names.TRANSFER_ENCODING);
} else { } else {
m.setHeader(Names.TRANSFER_ENCODING, values); m.headers().set(Names.TRANSFER_ENCODING, values);
} }
} }
public static void setTransferEncodingChunked(HttpHeader m) { public static void setTransferEncodingChunked(HttpMessage m) {
addHeader(m, Names.TRANSFER_ENCODING, Values.CHUNKED); addHeader(m, Names.TRANSFER_ENCODING, Values.CHUNKED);
removeHeader(m, Names.CONTENT_LENGTH); removeHeader(m, Names.CONTENT_LENGTH);
} }
public static boolean isContentLengthSet(HttpHeader m) { public static boolean isContentLengthSet(HttpMessage m) {
List<String> contentLength = m.getHeaders(Names.CONTENT_LENGTH); List<String> contentLength = m.headers().getAll(Names.CONTENT_LENGTH);
return !contentLength.isEmpty(); return !contentLength.isEmpty();
} }
private static final int BUCKET_SIZE = 17; protected HttpHeaders() { }
private static int hash(String name) { /**
int h = 0; * Returns the value of a header with the specified name. If there are
for (int i = name.length() - 1; i >= 0; i --) { * more than one values for the specified name, the first value is returned.
char c = name.charAt(i); *
if (c >= 'A' && c <= 'Z') { * @param name The name of the header to search
c += 32; * @return The first header value or {@code null} if there is no such header
} */
h = 31 * h + c; public abstract String get(String name);
/**
* Returns the values of headers with the specified name
*
* @param name The name of the headers to search
* @return A {@link List} of header values which will be empty if no values
* are found
*/
public abstract List<String> getAll(String name);
/**
* Returns the all headers that this message contains.
*
* @return A {@link List} of the header name-value entries, which will be
* empty if no pairs are found
*/
public abstract List<Map.Entry<String, String>> entries();
/**
* Checks to see if there is a header with the specified name
*
* @param name The name of the header to search for
* @return True if at least one header is found
*/
public abstract boolean contains(String name);
/**
* Checks if no header exists.
*/
public abstract boolean isEmpty();
/**
* Gets a {@link Set} of all header names that this message contains
*
* @return A {@link Set} of all header names
*/
public abstract Set<String> names();
/**
* Adds a new header with the specified name and value.
*
* If the specified value is not a {@link String}, it is converted
* into a {@link String} by {@link Object#toString()}, except in the cases
* of {@link Date} and {@link Calendar}, which are formatted to the date
* format defined in <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
*
* @param name The name of the header being added
* @param value The value of the header being added
*/
public abstract void add(String name, Object value);
/**
* Adds a new header with the specified name and values.
*
* This method can be represented approximately as the following code:
* <pre>
* for (Object v: values) {
* if (v == null) {
* break;
* }
* headers.add(name, v);
* }
* </pre>
*
* @param name The name of the headers being set
* @param values The values of the headers being set
*/
public abstract void add(String name, Iterable<?> values);
public void add(HttpHeaders headers) {
if (headers == null) {
throw new NullPointerException("headers");
} }
for (Map.Entry<String, String> e: headers) {
if (h > 0) { add(e.getKey(), e.getValue());
return h;
} else if (h == Integer.MIN_VALUE) {
return Integer.MAX_VALUE;
} else {
return -h;
} }
} }
private static boolean eq(String name1, String name2) { /**
int nameLen = name1.length(); * Sets a header with the specified name and value.
if (nameLen != name2.length()) { *
return false; * If there is an existing header with the same name, it is removed.
* If the specified value is not a {@link String}, it is converted into a
* {@link String} by {@link Object#toString()}, except for {@link Date}
* and {@link Calendar}, which are formatted to the date format defined in
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
*
* @param name The name of the header being set
* @param value The value of the header being set
*/
public abstract void set(String name, Object value);
/**
* Sets a header with the specified name and values.
*
* If there is an existing header with the same name, it is removed.
* This method can be represented approximately as the following code:
* <pre>
* headers.remove(name);
* for (Object v: values) {
* if (v == null) {
* break;
* }
* headers.add(name, v);
* }
* </pre>
*
* @param name The name of the headers being set
* @param values The values of the headers being set
*/
public abstract void set(String name, Iterable<?> values);
public void set(HttpHeaders headers) {
if (headers == null) {
throw new NullPointerException("headers");
} }
clear();
for (int i = nameLen - 1; i >= 0; i --) { for (Map.Entry<String, String> e: headers) {
char c1 = name1.charAt(i); add(e.getKey(), e.getValue());
char c2 = name2.charAt(i);
if (c1 != c2) {
if (c1 >= 'A' && c1 <= 'Z') {
c1 += 32;
}
if (c2 >= 'A' && c2 <= 'Z') {
c2 += 32;
}
if (c1 != c2) {
return false;
}
}
}
return true;
}
private static int index(int hash) {
return hash % BUCKET_SIZE;
}
private final HeaderEntry[] entries = new HeaderEntry[BUCKET_SIZE];
private final HeaderEntry head = new HeaderEntry(-1, null, null);
HttpHeaders() {
head.before = head.after = head;
}
void validateHeaderName0(String headerName) {
validateHeaderName(headerName);
}
void addHeader(final String name, final Object value) {
validateHeaderName0(name);
String strVal = toString(value);
validateHeaderValue(strVal);
int h = hash(name);
int i = index(h);
addHeader0(h, i, name, strVal);
}
private void addHeader0(int h, int i, final String name, final String value) {
// Update the hash table.
HeaderEntry e = entries[i];
HeaderEntry newEntry;
entries[i] = newEntry = new HeaderEntry(h, name, value);
newEntry.next = e;
// Update the linked list.
newEntry.addBefore(head);
}
void removeHeader(final String name) {
if (name == null) {
throw new NullPointerException("name");
}
int h = hash(name);
int i = index(h);
removeHeader0(h, i, name);
}
private void removeHeader0(int h, int i, String name) {
HeaderEntry e = entries[i];
if (e == null) {
return;
}
for (;;) {
if (e.hash == h && eq(name, e.key)) {
e.remove();
HeaderEntry next = e.next;
if (next != null) {
entries[i] = next;
e = next;
} else {
entries[i] = null;
return;
}
} else {
break;
}
}
for (;;) {
HeaderEntry next = e.next;
if (next == null) {
break;
}
if (next.hash == h && eq(name, next.key)) {
e.next = next.next;
next.remove();
} else {
e = next;
}
} }
} }
void setHeader(final String name, final Object value) { /**
validateHeaderName0(name); * Removes the header with the specified name.
String strVal = toString(value); *
validateHeaderValue(strVal); * @param name The name of the header to remove
int h = hash(name); */
int i = index(h); public abstract void remove(String name);
removeHeader0(h, i, name);
addHeader0(h, i, name, strVal);
}
void setHeader(final String name, final Iterable<?> values) { /**
if (values == null) { * Removes all headers from this {@link HttpMessage}.
throw new NullPointerException("values"); */
} public abstract void clear();
validateHeaderName0(name);
int h = hash(name);
int i = index(h);
removeHeader0(h, i, name);
for (Object v: values) {
if (v == null) {
break;
}
String strVal = toString(v);
validateHeaderValue(strVal);
addHeader0(h, i, name, strVal);
}
}
void clearHeaders() {
for (int i = 0; i < entries.length; i ++) {
entries[i] = null;
}
head.before = head.after = head;
}
String getHeader(final String name) {
if (name == null) {
throw new NullPointerException("name");
}
int h = hash(name);
int i = index(h);
HeaderEntry e = entries[i];
while (e != null) {
if (e.hash == h && eq(name, e.key)) {
return e.value;
}
e = e.next;
}
return null;
}
List<String> getHeaders(final String name) {
if (name == null) {
throw new NullPointerException("name");
}
LinkedList<String> values = new LinkedList<String>();
int h = hash(name);
int i = index(h);
HeaderEntry e = entries[i];
while (e != null) {
if (e.hash == h && eq(name, e.key)) {
values.addFirst(e.value);
}
e = e.next;
}
return values;
}
List<Map.Entry<String, String>> getHeaders() {
List<Map.Entry<String, String>> all =
new LinkedList<Map.Entry<String, String>>();
HeaderEntry e = head.after;
while (e != head) {
all.add(e);
e = e.after;
}
return all;
}
boolean containsHeader(String name) {
return getHeader(name) != null;
}
Set<String> getHeaderNames() {
Set<String> names = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
HeaderEntry e = head.after;
while (e != head) {
names.add(e.key);
e = e.after;
}
return names;
}
private static String toString(Object value) {
if (value == null) {
return null;
}
if (value instanceof String) {
return (String) value;
}
if (value instanceof Number) {
return value.toString();
}
if (value instanceof Date) {
return new HttpHeaderDateFormat().format((Date) value);
}
if (value instanceof Calendar) {
return new HttpHeaderDateFormat().format(((Calendar) value).getTime());
}
return value.toString();
}
private static final class HeaderEntry implements Map.Entry<String, String> {
final int hash;
final String key;
String value;
HeaderEntry next;
HeaderEntry before, after;
HeaderEntry(int hash, String key, String value) {
this.hash = hash;
this.key = key;
this.value = value;
}
void remove() {
before.after = after;
after.before = before;
}
void addBefore(HeaderEntry e) {
after = e;
before = e.before;
before.after = this;
after.before = this;
}
@Override
public String getKey() {
return key;
}
@Override
public String getValue() {
return value;
}
@Override
public String setValue(String value) {
if (value == null) {
throw new NullPointerException("value");
}
validateHeaderValue(value);
String oldValue = this.value;
this.value = value;
return oldValue;
}
@Override
public String toString() {
return key + '=' + value;
}
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2013 The Netty Project * Copyright 2012 The Netty Project
* *
* The Netty Project licenses this file to you under the Apache License, * The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance * version 2.0 (the "License"); you may not use this file except in compliance
@ -15,9 +15,28 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
/** /**
* Combines {@link HttpMessage} and {@link LastHttpContent} into one * An interface that defines a HTTP message, providing common properties for
* message. So it represent a <i>complete</i> http message. * {@link HttpRequest} and {@link HttpResponse}.
* @see HttpResponse
* @see HttpRequest
* @see HttpHeaders
*
* @apiviz.landmark
* @apiviz.has io.netty.handler.codec.http.HttpChunk oneway - - is followed by
*/ */
public interface HttpMessage extends HttpHeader, LastHttpContent { public interface HttpMessage extends HttpObject {
/**
* Returns the protocol version of this {@link HttpMessage}
*
* @return The protocol version
*/
HttpVersion protocolVersion();
/**
* Returns the headers of this message.
*/
HttpHeaders headers();
} }

View File

@ -154,13 +154,13 @@ public class HttpMethod implements Comparable<HttpMethod> {
/** /**
* Returns the name of this method. * Returns the name of this method.
*/ */
public String getName() { public String name() {
return name; return name;
} }
@Override @Override
public int hashCode() { public int hashCode() {
return getName().hashCode(); return name().hashCode();
} }
@Override @Override
@ -170,16 +170,16 @@ public class HttpMethod implements Comparable<HttpMethod> {
} }
HttpMethod that = (HttpMethod) o; HttpMethod that = (HttpMethod) o;
return getName().equals(that.getName()); return name().equals(that.name());
} }
@Override @Override
public String toString() { public String toString() {
return getName(); return name();
} }
@Override @Override
public int compareTo(HttpMethod o) { public int compareTo(HttpMethod o) {
return getName().compareTo(o.getName()); return name().compareTo(o.name());
} }
} }

View File

@ -18,6 +18,14 @@ package io.netty.handler.codec.http;
import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.DecoderResult;
public interface HttpObject { public interface HttpObject {
DecoderResult getDecoderResult(); /**
void setDecoderResult(DecoderResult result); * Returns the result of decoding this message.
*/
DecoderResult decoderResult();
/**
* Updates the result of decoding this message. This method is supposed to be invoked by {@link HttpObjectDecoder}.
* Do not call this method unless you know what you are doing.
*/
void updateDecoderResult(DecoderResult result);
} }

View File

@ -15,7 +15,6 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import static io.netty.handler.codec.http.HttpHeaders.*;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
@ -29,9 +28,11 @@ import io.netty.util.CharsetUtil;
import java.util.Map.Entry; import java.util.Map.Entry;
import static io.netty.handler.codec.http.HttpHeaders.*;
/** /**
* A {@link ChannelHandler} that aggregates an {@link HttpHeader} * A {@link ChannelHandler} that aggregates an {@link HttpMessage}
* and its following {@link HttpContent}s into a single {@link HttpHeader} with * and its following {@link HttpContent}s into a single {@link HttpMessage} with
* no following {@link HttpContent}s. It is useful when you don't want to take * no following {@link HttpContent}s. It is useful when you don't want to take
* care of HTTP messages whose transfer encoding is 'chunked'. Insert this * care of HTTP messages whose transfer encoding is 'chunked'. Insert this
* handler after {@link HttpObjectDecoder} in the {@link ChannelPipeline}: * handler after {@link HttpObjectDecoder} in the {@link ChannelPipeline}:
@ -53,7 +54,7 @@ public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
"HTTP/1.1 100 Continue\r\n\r\n", CharsetUtil.US_ASCII); "HTTP/1.1 100 Continue\r\n\r\n", CharsetUtil.US_ASCII);
private final int maxContentLength; private final int maxContentLength;
private HttpMessage currentMessage; private FullHttpMessage currentMessage;
private int maxCumulationBufferComponents = DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS; private int maxCumulationBufferComponents = DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS;
private ChannelHandlerContext ctx; private ChannelHandlerContext ctx;
@ -111,10 +112,12 @@ public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
@Override @Override
protected Object decode(ChannelHandlerContext ctx, HttpObject msg) throws Exception { protected Object decode(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
HttpMessage currentMessage = this.currentMessage; FullHttpMessage currentMessage = this.currentMessage;
if (msg instanceof HttpHeader) { if (msg instanceof HttpMessage) {
HttpHeader m = (HttpHeader) msg; assert currentMessage == null;
HttpMessage m = (HttpMessage) msg;
// Handle the 'Expect: 100-continue' header if necessary. // Handle the 'Expect: 100-continue' header if necessary.
// TODO: Respond with 413 Request Entity Too Large // TODO: Respond with 413 Request Entity Too Large
@ -125,41 +128,40 @@ public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
ctx.write(CONTINUE.duplicate()); ctx.write(CONTINUE.duplicate());
} }
if (!m.getDecoderResult().isSuccess()) { if (!m.decoderResult().isSuccess()) {
removeTransferEncodingChunked(m); removeTransferEncodingChunked(m);
this.currentMessage = null; this.currentMessage = null;
return m; return m;
} }
if (msg instanceof HttpRequestHeader) { if (msg instanceof HttpRequest) {
HttpRequestHeader header = (HttpRequestHeader) msg; HttpRequest header = (HttpRequest) msg;
this.currentMessage = new DefaultHttpRequest(header.getProtocolVersion(), this.currentMessage = currentMessage = new DefaultFullHttpRequest(header.protocolVersion(),
header.getMethod(), header.getUri()); header.method(), header.uri(), Unpooled.compositeBuffer(maxCumulationBufferComponents));
} else { } else if (msg instanceof HttpResponse) {
HttpResponseHeader header = (HttpResponseHeader) msg; HttpResponse header = (HttpResponse) msg;
this.currentMessage = new DefaultHttpResponse(header.getProtocolVersion(), header.getStatus()); this.currentMessage = currentMessage = new DefaultFullHttpResponse(
header.protocolVersion(), header.status(),
Unpooled.compositeBuffer(maxCumulationBufferComponents));
} else {
throw new Error();
} }
for (String name: m.getHeaderNames()) {
this.currentMessage.setHeader(name, m.getHeaders(name)); HttpHeaders headers = currentMessage.headers();
for (String name: m.headers().names()) {
headers.set(name, m.headers().get(name));
} }
// A streamed message - initialize the cumulative buffer, and wait for incoming chunks. // A streamed message - initialize the cumulative buffer, and wait for incoming chunks.
removeTransferEncodingChunked(m); removeTransferEncodingChunked(currentMessage);
this.currentMessage.setContent(Unpooled.compositeBuffer(maxCumulationBufferComponents));
return null; return null;
} else if (msg instanceof HttpContent) { } else if (msg instanceof HttpContent) {
// Sanity check assert currentMessage != null;
if (currentMessage == null) {
throw new IllegalStateException(
"received " + HttpContent.class.getSimpleName() +
" without " + HttpHeader.class.getSimpleName() +
" or last message's transfer encoding was 'SINGLE'");
}
// Merge the received chunk into the content of the current message. // Merge the received chunk into the content of the current message.
HttpContent chunk = (HttpContent) msg; HttpContent chunk = (HttpContent) msg;
ByteBuf content = currentMessage.getContent(); CompositeByteBuf content = (CompositeByteBuf) currentMessage.data();
if (content.readableBytes() > maxContentLength - chunk.getContent().readableBytes()) { if (content.readableBytes() > maxContentLength - chunk.data().readableBytes()) {
// TODO: Respond with 413 Request Entity Too Large // TODO: Respond with 413 Request Entity Too Large
// and discard the traffic or close the connection. // and discard the traffic or close the connection.
// No need to notify the upstream handlers - just log. // No need to notify the upstream handlers - just log.
@ -170,12 +172,17 @@ public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
} }
// Append the content of the chunk // Append the content of the chunk
appendToCumulation(chunk.getContent()); if (chunk.data().readable()) {
content.addComponent(chunk.data());
content.writerIndex(content.writerIndex() + chunk.data().readableBytes());
} else {
chunk.free();
}
final boolean last; final boolean last;
if (!chunk.getDecoderResult().isSuccess()) { if (!chunk.decoderResult().isSuccess()) {
currentMessage.setDecoderResult( currentMessage.updateDecoderResult(
DecoderResult.partialFailure(chunk.getDecoderResult().cause())); DecoderResult.partialFailure(chunk.decoderResult().cause()));
last = true; last = true;
} else { } else {
last = msg instanceof LastHttpContent; last = msg instanceof LastHttpContent;
@ -187,13 +194,13 @@ public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
// Merge trailing headers into the message. // Merge trailing headers into the message.
if (chunk instanceof LastHttpContent) { if (chunk instanceof LastHttpContent) {
LastHttpContent trailer = (LastHttpContent) chunk; LastHttpContent trailer = (LastHttpContent) chunk;
for (Entry<String, String> header: trailer.getHeaders()) { for (Entry<String, String> header: trailer.trailingHeaders()) {
currentMessage.setHeader(header.getKey(), header.getValue()); currentMessage.headers().add(header.getKey(), header.getValue());
} }
} }
// Set the 'Content-Length' header. // Set the 'Content-Length' header.
currentMessage.setHeader( currentMessage.headers().set(
HttpHeaders.Names.CONTENT_LENGTH, HttpHeaders.Names.CONTENT_LENGTH,
String.valueOf(content.readableBytes())); String.valueOf(content.readableBytes()));
@ -203,21 +210,12 @@ public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
return null; return null;
} }
} else { } else {
throw new IllegalStateException( throw new Error();
"Only " + HttpHeader.class.getSimpleName() + " and " +
HttpContent.class.getSimpleName() + " are accepted: " + msg.getClass().getName());
} }
} }
private void appendToCumulation(ByteBuf input) {
CompositeByteBuf cumulation = (CompositeByteBuf) currentMessage.getContent();
cumulation.addComponent(input);
cumulation.writerIndex(cumulation.capacity());
}
@Override @Override
public void beforeAdd(ChannelHandlerContext ctx) throws Exception { public void beforeAdd(ChannelHandlerContext ctx) throws Exception {
this.ctx = ctx; this.ctx = ctx;
} }
} }

View File

@ -26,7 +26,7 @@ import io.netty.handler.codec.TooLongFrameException;
import java.util.List; import java.util.List;
/** /**
* Decodes {@link ByteBuf}s into {@link HttpHeader}s and * Decodes {@link ByteBuf}s into {@link HttpMessage}s and
* {@link HttpContent}s. * {@link HttpContent}s.
* *
* <h3>Parameters that prevents excessive memory consumption</h3> * <h3>Parameters that prevents excessive memory consumption</h3>
@ -59,7 +59,7 @@ import java.util.List;
* *
* If the content of an HTTP message is greater than {@code maxChunkSize} or * If the content of an HTTP message is greater than {@code maxChunkSize} or
* the transfer encoding of the HTTP message is 'chunked', this decoder * the transfer encoding of the HTTP message is 'chunked', this decoder
* generates one {@link HttpHeader} instance and its following * generates one {@link HttpMessage} instance and its following
* {@link HttpContent}s per single HTTP message to avoid excessive memory * {@link HttpContent}s per single HTTP message to avoid excessive memory
* consumption. For example, the following HTTP message: * consumption. For example, the following HTTP message:
* <pre> * <pre>
@ -76,7 +76,7 @@ import java.util.List;
* </pre> * </pre>
* triggers {@link HttpRequestDecoder} to generate 3 objects: * triggers {@link HttpRequestDecoder} to generate 3 objects:
* <ol> * <ol>
* <li>An {@link HttpRequestHeader},</li> * <li>An {@link HttpRequest},</li>
* <li>The first {@link HttpContent} whose content is {@code 'abcdefghijklmnopqrstuvwxyz'},</li> * <li>The first {@link HttpContent} whose content is {@code 'abcdefghijklmnopqrstuvwxyz'},</li>
* <li>The second {@link LastHttpContent} whose content is {@code '1234567890abcdef'}, which marks * <li>The second {@link LastHttpContent} whose content is {@code '1234567890abcdef'}, which marks
* the end of the content.</li> * the end of the content.</li>
@ -103,7 +103,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
private final int maxHeaderSize; private final int maxHeaderSize;
private final int maxChunkSize; private final int maxChunkSize;
private ByteBuf content; private ByteBuf content;
private HttpHeader message; private HttpMessage message;
private long chunkSize; private long chunkSize;
private int headerSize; private int headerSize;
private int contentRead; private int contentRead;
@ -211,7 +211,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
switch (nextState) { switch (nextState) {
case READ_FIXED_LENGTH_CONTENT: case READ_FIXED_LENGTH_CONTENT:
if (contentLength > maxChunkSize || HttpHeaders.is100ContinueExpected(message)) { if (contentLength > maxChunkSize || HttpHeaders.is100ContinueExpected(message)) {
// Generate HttpMessage first. HttpChunks will follow. // Generate FullHttpMessage first. HttpChunks will follow.
checkpoint(State.READ_FIXED_LENGTH_CONTENT_AS_CHUNKS); checkpoint(State.READ_FIXED_LENGTH_CONTENT_AS_CHUNKS);
// chunkSize will be decreased as the READ_FIXED_LENGTH_CONTENT_AS_CHUNKS // chunkSize will be decreased as the READ_FIXED_LENGTH_CONTENT_AS_CHUNKS
// state reads data chunk by chunk. // state reads data chunk by chunk.
@ -221,7 +221,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
break; break;
case READ_VARIABLE_LENGTH_CONTENT: case READ_VARIABLE_LENGTH_CONTENT:
if (buffer.readableBytes() > maxChunkSize || HttpHeaders.is100ContinueExpected(message)) { if (buffer.readableBytes() > maxChunkSize || HttpHeaders.is100ContinueExpected(message)) {
// Generate HttpMessage first. HttpChunks will follow. // Generate FullHttpMessage first. HttpChunks will follow.
checkpoint(State.READ_VARIABLE_LENGTH_CONTENT_AS_CHUNKS); checkpoint(State.READ_VARIABLE_LENGTH_CONTENT_AS_CHUNKS);
return message; return message;
} }
@ -396,10 +396,10 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
} }
} }
protected boolean isContentAlwaysEmpty(HttpHeader msg) { protected boolean isContentAlwaysEmpty(HttpMessage msg) {
if (msg instanceof HttpResponseHeader) { if (msg instanceof HttpResponse) {
HttpResponseHeader res = (HttpResponseHeader) msg; HttpResponse res = (HttpResponse) msg;
int code = res.getStatus().getCode(); int code = res.status().code();
// Correctly handle return codes of 1xx. // Correctly handle return codes of 1xx.
// //
@ -407,7 +407,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
// - http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html Section 4.4 // - http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html Section 4.4
// - https://github.com/netty/netty/issues/222 // - https://github.com/netty/netty/issues/222
if (code >= 100 && code < 200) { if (code >= 100 && code < 200) {
if (code == 101 && !res.containsHeader(HttpHeaders.Names.SEC_WEBSOCKET_ACCEPT)) { if (code == 101 && !res.headers().contains(HttpHeaders.Names.SEC_WEBSOCKET_ACCEPT)) {
// It's Hixie 76 websocket handshake response // It's Hixie 76 websocket handshake response
return false; return false;
} }
@ -423,12 +423,12 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
} }
private Object reset() { private Object reset() {
HttpHeader message = this.message; HttpMessage message = this.message;
ByteBuf content = this.content; ByteBuf content = this.content;
LastHttpContent httpContent; LastHttpContent httpContent;
if (content == null || !content.readable()) { if (content == null || !content.readable()) {
httpContent = HttpContent.LAST_CONTENT; httpContent = LastHttpContent.EMPTY_LAST_CONTENT;
} else { } else {
httpContent = new DefaultLastHttpContent(content); httpContent = new DefaultLastHttpContent(content);
} }
@ -441,13 +441,13 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
return messages; return messages;
} }
private HttpHeader invalidMessage(Exception cause) { private HttpMessage invalidMessage(Exception cause) {
checkpoint(State.BAD_MESSAGE); checkpoint(State.BAD_MESSAGE);
if (message != null) { if (message != null) {
message.setDecoderResult(DecoderResult.partialFailure(cause)); message.updateDecoderResult(DecoderResult.partialFailure(cause));
} else { } else {
message = createInvalidMessage(); message = createInvalidMessage();
message.setDecoderResult(DecoderResult.failure(cause)); message.updateDecoderResult(DecoderResult.failure(cause));
} }
return message; return message;
} }
@ -455,7 +455,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
private HttpContent invalidChunk(Exception cause) { private HttpContent invalidChunk(Exception cause) {
checkpoint(State.BAD_MESSAGE); checkpoint(State.BAD_MESSAGE);
HttpContent chunk = new DefaultHttpContent(Unpooled.EMPTY_BUFFER); HttpContent chunk = new DefaultHttpContent(Unpooled.EMPTY_BUFFER);
chunk.setDecoderResult(DecoderResult.failure(cause)); chunk.updateDecoderResult(DecoderResult.failure(cause));
return chunk; return chunk;
} }
@ -492,19 +492,19 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
private State readHeaders(ByteBuf buffer) { private State readHeaders(ByteBuf buffer) {
headerSize = 0; headerSize = 0;
final HttpHeader message = this.message; final HttpMessage message = this.message;
String line = readHeader(buffer); String line = readHeader(buffer);
String name = null; String name = null;
String value = null; String value = null;
if (!line.isEmpty()) { if (!line.isEmpty()) {
message.clearHeaders(); message.headers().clear();
do { do {
char firstChar = line.charAt(0); char firstChar = line.charAt(0);
if (name != null && (firstChar == ' ' || firstChar == '\t')) { if (name != null && (firstChar == ' ' || firstChar == '\t')) {
value = value + ' ' + line.trim(); value = value + ' ' + line.trim();
} else { } else {
if (name != null) { if (name != null) {
message.addHeader(name, value); message.headers().add(name, value);
} }
String[] header = splitHeader(line); String[] header = splitHeader(line);
name = header[0]; name = header[0];
@ -516,7 +516,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
// Add the last header. // Add the last header.
if (name != null) { if (name != null) {
message.addHeader(name, value); message.headers().add(name, value);
} }
} }
@ -544,7 +544,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
do { do {
char firstChar = line.charAt(0); char firstChar = line.charAt(0);
if (lastHeader != null && (firstChar == ' ' || firstChar == '\t')) { if (lastHeader != null && (firstChar == ' ' || firstChar == '\t')) {
List<String> current = trailer.getHeaders(lastHeader); List<String> current = trailer.trailingHeaders().getAll(lastHeader);
if (!current.isEmpty()) { if (!current.isEmpty()) {
int lastPos = current.size() - 1; int lastPos = current.size() - 1;
String newString = current.get(lastPos) + line.trim(); String newString = current.get(lastPos) + line.trim();
@ -558,7 +558,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
if (!name.equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH) && if (!name.equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH) &&
!name.equalsIgnoreCase(HttpHeaders.Names.TRANSFER_ENCODING) && !name.equalsIgnoreCase(HttpHeaders.Names.TRANSFER_ENCODING) &&
!name.equalsIgnoreCase(HttpHeaders.Names.TRAILER)) { !name.equalsIgnoreCase(HttpHeaders.Names.TRAILER)) {
trailer.addHeader(name, header[1]); trailer.trailingHeaders().add(name, header[1]);
} }
lastHeader = name; lastHeader = name;
} }
@ -569,7 +569,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
return trailer; return trailer;
} }
return HttpContent.LAST_CONTENT; return LastHttpContent.EMPTY_LAST_CONTENT;
} }
private String readHeader(ByteBuf buffer) { private String readHeader(ByteBuf buffer) {
@ -612,8 +612,8 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
} }
protected abstract boolean isDecodingRequest(); protected abstract boolean isDecodingRequest();
protected abstract HttpHeader createMessage(String[] initialLine) throws Exception; protected abstract HttpMessage createMessage(String[] initialLine) throws Exception;
protected abstract HttpHeader createInvalidMessage(); protected abstract HttpMessage createInvalidMessage();
private static int getChunkSize(String hex) { private static int getChunkSize(String hex) {
hex = hex.trim(); hex = hex.trim();

View File

@ -15,8 +15,6 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import static io.netty.buffer.Unpooled.*;
import static io.netty.handler.codec.http.HttpConstants.*;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.MessageToByteEncoder;
@ -24,8 +22,11 @@ import io.netty.util.CharsetUtil;
import java.util.Map; import java.util.Map;
import static io.netty.buffer.Unpooled.*;
import static io.netty.handler.codec.http.HttpConstants.*;
/** /**
* Encodes an {@link HttpHeader} or an {@link HttpContent} into * Encodes an {@link HttpMessage} or an {@link HttpContent} into
* a {@link ByteBuf}. * a {@link ByteBuf}.
* *
* <h3>Extensibility</h3> * <h3>Extensibility</h3>
@ -38,9 +39,14 @@ import java.util.Map;
* implement all abstract methods properly. * implement all abstract methods properly.
* @apiviz.landmark * @apiviz.landmark
*/ */
public abstract class HttpObjectEncoder<H extends HttpHeader> extends MessageToByteEncoder<Object> { public abstract class HttpObjectEncoder<H extends HttpMessage> extends MessageToByteEncoder<Object> {
private boolean chunked; private static final int ST_INIT = 0;
private static final int ST_CONTENT_NON_CHUNK = 1;
private static final int ST_CONTENT_CHUNK = 2;
@SuppressWarnings("RedundantFieldInitialization")
private int state = ST_INIT;
/** /**
* Creates a new instance. * Creates a new instance.
@ -49,38 +55,44 @@ public abstract class HttpObjectEncoder<H extends HttpHeader> extends MessageToB
super(HttpObject.class); super(HttpObject.class);
} }
@SuppressWarnings("unchecked")
@Override @Override
@SuppressWarnings("unchecked")
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception { protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
if (msg instanceof HttpHeader) { if (msg instanceof HttpMessage) {
HttpHeader m = (HttpHeader) msg; if (state != ST_INIT) {
chunked = HttpHeaders.isTransferEncodingChunked(m); throw new IllegalStateException("unexpected message type: " + msg.getClass().getSimpleName());
// Encode the message. }
out.markWriterIndex();
HttpMessage m = (HttpMessage) msg;
// Encode the message.
encodeInitialLine(out, (H) m); encodeInitialLine(out, (H) m);
encodeHeaders(out, m); encodeHeaders(out, m);
out.writeByte(CR); out.writeByte(CR);
out.writeByte(LF); out.writeByte(LF);
state = HttpHeaders.isTransferEncodingChunked(m) ? ST_CONTENT_CHUNK : ST_CONTENT_NON_CHUNK;
} }
if (msg instanceof HttpContent) { if (msg instanceof HttpContent) {
HttpContent chunk = (HttpContent) msg; if (state == ST_INIT) {
throw new IllegalStateException("unexpected message type: " + msg.getClass().getSimpleName());
}
HttpContent chunk = (HttpContent) msg;
ByteBuf content = chunk.data();
int contentLength = content.readableBytes();
if (state == ST_CONTENT_NON_CHUNK) {
if (contentLength > 0) {
out.writeBytes(content, content.readerIndex(), content.readableBytes());
}
if (!chunked) {
ByteBuf content = chunk.getContent();
out.writeBytes(content, content.readerIndex(), content.readableBytes());
} else {
if (chunk instanceof LastHttpContent) { if (chunk instanceof LastHttpContent) {
out.writeByte((byte) '0'); state = ST_INIT;
out.writeByte(CR); }
out.writeByte(LF); } else if (state == ST_CONTENT_CHUNK) {
encodeTrailingHeaders(out, (LastHttpContent) chunk); if (contentLength > 0) {
out.writeByte(CR);
out.writeByte(LF);
} else {
ByteBuf content = chunk.getContent();
int contentLength = content.readableBytes();
out.writeBytes(copiedBuffer(Integer.toHexString(contentLength), CharsetUtil.US_ASCII)); out.writeBytes(copiedBuffer(Integer.toHexString(contentLength), CharsetUtil.US_ASCII));
out.writeByte(CR); out.writeByte(CR);
out.writeByte(LF); out.writeByte(LF);
@ -88,18 +100,30 @@ public abstract class HttpObjectEncoder<H extends HttpHeader> extends MessageToB
out.writeByte(CR); out.writeByte(CR);
out.writeByte(LF); out.writeByte(LF);
} }
if (chunk instanceof LastHttpContent) {
out.writeByte((byte) '0');
out.writeByte(CR);
out.writeByte(LF);
encodeTrailingHeaders(out, (LastHttpContent) chunk);
out.writeByte(CR);
out.writeByte(LF);
state = ST_INIT;
}
} else {
throw new Error();
} }
} }
} }
private static void encodeHeaders(ByteBuf buf, HttpHeader message) { private static void encodeHeaders(ByteBuf buf, HttpMessage message) {
for (Map.Entry<String, String> h: message.getHeaders()) { for (Map.Entry<String, String> h: message.headers()) {
encodeHeader(buf, h.getKey(), h.getValue()); encodeHeader(buf, h.getKey(), h.getValue());
} }
} }
private static void encodeTrailingHeaders(ByteBuf buf, LastHttpContent trailer) { private static void encodeTrailingHeaders(ByteBuf buf, LastHttpContent trailer) {
for (Map.Entry<String, String> h: trailer.getHeaders()) { for (Map.Entry<String, String> h: trailer.trailingHeaders()) {
encodeHeader(buf, h.getKey(), h.getValue()); encodeHeader(buf, h.getKey(), h.getValue());
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2013 The Netty Project * Copyright 2012 The Netty Project
* *
* The Netty Project licenses this file to you under the Apache License, * The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance * version 2.0 (the "License"); you may not use this file except in compliance
@ -15,9 +15,35 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
/** /**
* Combinate the {@link HttpRequestHeader} and {@link HttpMessage}, so the request is a <i>complete</i> HTTP * An HTTP request.
* request. *
* <h3>Accessing Query Parameters and Cookie</h3>
* <p>
* Unlike the Servlet API, a query string is constructed and decomposed by
* {@link QueryStringEncoder} and {@link QueryStringDecoder}. {@link Cookie}
* support is also provided separately via {@link CookieDecoder}, {@link ClientCookieEncoder},
* and {@link @ServerCookieEncoder}.
*
* @see HttpResponse
* @see ClientCookieEncoder
* @see ServerCookieEncoder
* @see CookieDecoder
*/ */
public interface HttpRequest extends HttpRequestHeader, HttpMessage { public interface HttpRequest extends HttpMessage {
/**
* Returns the {@link HttpMethod} of this {@link HttpRequest}.
*
* @return The {@link HttpMethod} of this {@link HttpRequest}
*/
HttpMethod method();
/**
* Returns the requested URI (or alternatively, path)
*
* @return The URI being requested
*/
String uri();
} }

View File

@ -21,7 +21,7 @@ import io.netty.handler.codec.TooLongFrameException;
/** /**
* Decodes {@link ByteBuf}s into {@link HttpRequestHeader}s and {@link HttpContent}s. * Decodes {@link ByteBuf}s into {@link HttpRequest}s and {@link HttpContent}s.
* *
* <h3>Parameters that prevents excessive memory consumption</h3> * <h3>Parameters that prevents excessive memory consumption</h3>
* <table border="1"> * <table border="1">
@ -71,14 +71,14 @@ public class HttpRequestDecoder extends HttpObjectDecoder {
} }
@Override @Override
protected HttpHeader createMessage(String[] initialLine) throws Exception { protected HttpMessage createMessage(String[] initialLine) throws Exception {
return new DefaultHttpRequestHeader( return new DefaultHttpRequest(
HttpVersion.valueOf(initialLine[2]), HttpMethod.valueOf(initialLine[0]), initialLine[1]); HttpVersion.valueOf(initialLine[2]), HttpMethod.valueOf(initialLine[0]), initialLine[1]);
} }
@Override @Override
protected HttpHeader createInvalidMessage() { protected HttpMessage createInvalidMessage() {
return new DefaultHttpRequestHeader(HttpVersion.HTTP_1_0, HttpMethod.GET, "/bad-request"); return new DefaultHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.GET, "/bad-request");
} }
@Override @Override

View File

@ -15,25 +15,31 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import static io.netty.handler.codec.http.HttpConstants.*;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import static io.netty.handler.codec.http.HttpConstants.*;
/** /**
* Encodes an {@link HttpRequestHeader} or an {@link HttpContent} into * Encodes an {@link HttpRequest} or an {@link HttpContent} into
* a {@link ByteBuf}. * a {@link ByteBuf}.
*/ */
public class HttpRequestEncoder extends HttpObjectEncoder<HttpRequestHeader> { public class HttpRequestEncoder extends HttpObjectEncoder<HttpRequest> {
private static final char SLASH = '/'; private static final char SLASH = '/';
@Override @Override
protected void encodeInitialLine(ByteBuf buf, HttpRequestHeader request) throws Exception { public boolean isEncodable(Object msg) throws Exception {
buf.writeBytes(request.getMethod().toString().getBytes(CharsetUtil.US_ASCII)); return super.isEncodable(msg) && !(msg instanceof HttpResponse);
}
@Override
protected void encodeInitialLine(ByteBuf buf, HttpRequest request) throws Exception {
buf.writeBytes(request.method().toString().getBytes(CharsetUtil.US_ASCII));
buf.writeByte(SP); buf.writeByte(SP);
// Add / as absolute path if no is present. // Add / as absolute path if no is present.
// See http://tools.ietf.org/html/rfc2616#section-5.1.2 // See http://tools.ietf.org/html/rfc2616#section-5.1.2
String uri = request.getUri(); String uri = request.uri();
int start = uri.indexOf("://"); int start = uri.indexOf("://");
if (start != -1) { if (start != -1) {
int startIndex = start + 3; int startIndex = start + 3;
@ -44,7 +50,7 @@ public class HttpRequestEncoder extends HttpObjectEncoder<HttpRequestHeader> {
buf.writeBytes(uri.getBytes("UTF-8")); buf.writeBytes(uri.getBytes("UTF-8"));
buf.writeByte(SP); buf.writeByte(SP);
buf.writeBytes(request.getProtocolVersion().toString().getBytes(CharsetUtil.US_ASCII)); buf.writeBytes(request.protocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
buf.writeByte(CR); buf.writeByte(CR);
buf.writeByte(LF); buf.writeByte(LF);
} }

View File

@ -1,63 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http;
/**
* An HTTP request.
*
* <h3>Accessing Query Parameters and Cookie</h3>
* <p>
* Unlike the Servlet API, a query string is constructed and decomposed by
* {@link QueryStringEncoder} and {@link QueryStringDecoder}. {@link Cookie}
* support is also provided separately via {@link CookieDecoder}, {@link ClientCookieEncoder},
* and {@link @ServerCookieEncoder}.
*
* @see HttpResponseHeader
* @see ClientCookieEncoder
* @see ServerCookieEncoder
* @see CookieDecoder
*/
public interface HttpRequestHeader extends HttpHeader {
/**
* Returns the {@link HttpMethod} of this {@link HttpRequestHeader}.
*
* @return The {@link HttpMethod} of this {@link HttpRequestHeader}
*/
HttpMethod getMethod();
/**
* Sets the {@link HttpMethod} of this {@link HttpRequestHeader}.
*
* @param method The {@link HttpMethod} to set
*/
void setMethod(HttpMethod method);
/**
* Returns the requested URI (or alternatively, path)
*
* @return The URI being requested
*/
String getUri();
/**
* Sets the URI (or alternatively, path) being requested.
*
* @param uri The URI being requested
*/
void setUri(String uri);
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2013 The Netty Project * Copyright 2012 The Netty Project
* *
* The Netty Project licenses this file to you under the Apache License, * The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance * version 2.0 (the "License"); you may not use this file except in compliance
@ -15,9 +15,26 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
/** /**
* Combination of a {@link HttpResponseHeader} and {@link HttpMessage}. * An HTTP response.
* So it represent a <i>complete</i> http response. *
* <h3>Accessing Cookies</h3>
* <p>
* Unlike the Servlet API, {@link Cookie} support is provided separately via {@link CookieDecoder},
* {@link ClientCookieEncoder}, and {@link ServerCookieEncoder}.
*
* @see HttpRequest
* @see CookieDecoder
* @see ClientCookieEncoder
* @see ServerCookieEncoder
*/ */
public interface HttpResponse extends HttpResponseHeader, HttpMessage { public interface HttpResponse extends HttpMessage {
/**
* Returns the status of this {@link HttpResponse}.
*
* @return The {@link HttpResponseStatus} of this {@link HttpResponse}
*/
HttpResponseStatus status();
} }

View File

@ -21,7 +21,7 @@ import io.netty.handler.codec.TooLongFrameException;
/** /**
* Decodes {@link ByteBuf}s into {@link HttpResponseHeader}s and * Decodes {@link ByteBuf}s into {@link HttpResponse}s and
* {@link HttpContent}s. * {@link HttpContent}s.
* *
* <h3>Parameters that prevents excessive memory consumption</h3> * <h3>Parameters that prevents excessive memory consumption</h3>
@ -59,7 +59,7 @@ import io.netty.handler.codec.TooLongFrameException;
* request does not have any content even if there is <tt>Content-Length</tt> * request does not have any content even if there is <tt>Content-Length</tt>
* header. Because {@link HttpResponseDecoder} is not able to determine if the * header. Because {@link HttpResponseDecoder} is not able to determine if the
* response currently being decoded is associated with a <tt>HEAD</tt> request, * response currently being decoded is associated with a <tt>HEAD</tt> request,
* you must override {@link #isContentAlwaysEmpty(HttpHeader)} to return * you must override {@link #isContentAlwaysEmpty(HttpMessage)} to return
* <tt>true</tt> for the response of the <tt>HEAD</tt> request. * <tt>true</tt> for the response of the <tt>HEAD</tt> request.
* </p><p> * </p><p>
* If you are writing an HTTP client that issues a <tt>HEAD</tt> request, * If you are writing an HTTP client that issues a <tt>HEAD</tt> request,
@ -102,15 +102,15 @@ public class HttpResponseDecoder extends HttpObjectDecoder {
} }
@Override @Override
protected HttpHeader createMessage(String[] initialLine) { protected HttpMessage createMessage(String[] initialLine) {
return new DefaultHttpResponseHeader( return new DefaultHttpResponse(
HttpVersion.valueOf(initialLine[0]), HttpVersion.valueOf(initialLine[0]),
new HttpResponseStatus(Integer.valueOf(initialLine[1]), initialLine[2])); new HttpResponseStatus(Integer.valueOf(initialLine[1]), initialLine[2]));
} }
@Override @Override
protected HttpHeader createInvalidMessage() { protected HttpMessage createInvalidMessage() {
return new DefaultHttpResponseHeader(HttpVersion.HTTP_1_0, UNKNOWN_STATUS); return new DefaultHttpResponse(HttpVersion.HTTP_1_0, UNKNOWN_STATUS);
} }
@Override @Override

View File

@ -15,23 +15,29 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import static io.netty.handler.codec.http.HttpConstants.*;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import static io.netty.handler.codec.http.HttpConstants.*;
/** /**
* Encodes an {@link HttpResponseHeader} or an {@link HttpContent} into * Encodes an {@link HttpResponse} or an {@link HttpContent} into
* a {@link ByteBuf}. * a {@link ByteBuf}.
*/ */
public class HttpResponseEncoder extends HttpObjectEncoder<HttpResponseHeader> { public class HttpResponseEncoder extends HttpObjectEncoder<HttpResponse> {
@Override @Override
protected void encodeInitialLine(ByteBuf buf, HttpResponseHeader response) throws Exception { public boolean isEncodable(Object msg) throws Exception {
buf.writeBytes(response.getProtocolVersion().toString().getBytes(CharsetUtil.US_ASCII)); return super.isEncodable(msg) && !(msg instanceof HttpRequest);
}
@Override
protected void encodeInitialLine(ByteBuf buf, HttpResponse response) throws Exception {
buf.writeBytes(response.protocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
buf.writeByte(SP); buf.writeByte(SP);
buf.writeBytes(String.valueOf(response.getStatus().getCode()).getBytes(CharsetUtil.US_ASCII)); buf.writeBytes(String.valueOf(response.status().code()).getBytes(CharsetUtil.US_ASCII));
buf.writeByte(SP); buf.writeByte(SP);
buf.writeBytes(String.valueOf(response.getStatus().getReasonPhrase()).getBytes(CharsetUtil.US_ASCII)); buf.writeBytes(String.valueOf(response.status().reasonPhrase()).getBytes(CharsetUtil.US_ASCII));
buf.writeByte(CR); buf.writeByte(CR);
buf.writeByte(LF); buf.writeByte(LF);
} }

View File

@ -1,47 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http;
/**
* An HTTP response.
*
* <h3>Accessing Cookies</h3>
* <p>
* Unlike the Servlet API, {@link Cookie} support is provided separately via {@link CookieDecoder},
* {@link ClientCookieEncoder}, and {@link ServerCookieEncoder}.
*
* @see HttpRequestHeader
* @see CookieDecoder
* @see ClientCookieEncoder
* @see ServerCookieEncoder
*/
public interface HttpResponseHeader extends HttpHeader {
/**
* Returns the status of this {@link HttpResponseHeader}.
*
* @return The {@link HttpResponseStatus} of this {@link HttpResponseHeader}
*/
HttpResponseStatus getStatus();
/**
* Sets the status of this {@link HttpResponseHeader}
*
* @param status The {@link HttpResponseStatus} to use
*/
void setStatus(HttpResponseStatus status);
}

View File

@ -447,20 +447,20 @@ public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
/** /**
* Returns the code of this status. * Returns the code of this status.
*/ */
public int getCode() { public int code() {
return code; return code;
} }
/** /**
* Returns the reason phrase of this status. * Returns the reason phrase of this status.
*/ */
public String getReasonPhrase() { public String reasonPhrase() {
return reasonPhrase; return reasonPhrase;
} }
@Override @Override
public int hashCode() { public int hashCode() {
return getCode(); return code();
} }
@Override @Override
@ -469,12 +469,12 @@ public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
return false; return false;
} }
return getCode() == ((HttpResponseStatus) o).getCode(); return code() == ((HttpResponseStatus) o).code();
} }
@Override @Override
public int compareTo(HttpResponseStatus o) { public int compareTo(HttpResponseStatus o) {
return getCode() - o.getCode(); return code() - o.code();
} }
@Override @Override

View File

@ -15,9 +15,9 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import java.util.List; import io.netty.buffer.ByteBuf;
import java.util.Map; import io.netty.buffer.Unpooled;
import java.util.Set; import io.netty.handler.codec.DecoderResult;
/** /**
* The last {@link HttpContent} which has trailing headers. * The last {@link HttpContent} which has trailing headers.
@ -25,68 +25,48 @@ import java.util.Set;
public interface LastHttpContent extends HttpContent { public interface LastHttpContent extends HttpContent {
/** /**
* Returns the trailing header value with the specified header name. * The 'end of content' marker in chunked encoding.
* If there are more than one trailing header value for the specified
* header name, the first value is returned.
*
* @return the header value or {@code null} if there is no such header
*/ */
String getHeader(String name); LastHttpContent EMPTY_LAST_CONTENT = new LastHttpContent() {
/** @Override
* Returns the trailing header values with the specified header name. public ByteBuf data() {
* return Unpooled.EMPTY_BUFFER;
* @return the {@link List} of header values. An empty list if there is no }
* such header.
*/
List<String> getHeaders(String name);
/** @Override
* Returns the all header names and values that this trailer contains. public LastHttpContent copy() {
* return EMPTY_LAST_CONTENT;
* @return the {@link List} of the header name-value pairs. An empty list }
* if there is no header in this trailer.
*/
List<Map.Entry<String, String>> getHeaders();
/** @Override
* Returns {@code true} if and only if there is a trailing header with public HttpHeaders trailingHeaders() {
* the specified header name. return HttpHeaders.EMPTY_HEADERS;
*/ }
boolean containsHeader(String name);
/** @Override
* Returns the {@link Set} of all trailing header names that this trailer public DecoderResult decoderResult() {
* contains. return DecoderResult.SUCCESS;
*/ }
Set<String> getHeaderNames();
/** @Override
* Adds a new trailing header with the specified name and value. public void updateDecoderResult(DecoderResult result) {
*/ throw new UnsupportedOperationException("read only");
void addHeader(String name, Object value); }
/** @Override
* Sets a new trailing header with the specified name and value. public boolean isFreed() {
* If there is an existing trailing header with the same name, the existing return false;
* one is removed. }
*/
void setHeader(String name, Object value);
/** @Override
* Sets a new trailing header with the specified name and values. public void free() {
* If there is an existing trailing header with the same name, the existing // NOOP
* one is removed. }
*/ };
void setHeader(String name, Iterable<?> values);
/** HttpHeaders trailingHeaders();
* Removes the trailing header with the specified name.
*/
void removeHeader(String name);
/** @Override
* Removes all trailing headers from this trailer. LastHttpContent copy();
*/
void clearHeaders();
} }

View File

@ -54,7 +54,7 @@ import java.util.Map;
* @see QueryStringEncoder * @see QueryStringEncoder
* *
* @apiviz.stereotype utility * @apiviz.stereotype utility
* @apiviz.has io.netty.handler.codec.http.HttpRequest oneway - - decodes * @apiviz.has io.netty.handler.codec.http.FullHttpRequest oneway - - decodes
*/ */
public class QueryStringDecoder { public class QueryStringDecoder {

View File

@ -36,7 +36,7 @@ import java.util.List;
* @see QueryStringDecoder * @see QueryStringDecoder
* *
* @apiviz.stereotype utility * @apiviz.stereotype utility
* @apiviz.has io.netty.handler.codec.http.HttpRequest oneway - - encodes * @apiviz.has io.netty.handler.codec.http.FullHttpRequest oneway - - encodes
*/ */
public class QueryStringEncoder { public class QueryStringEncoder {

View File

@ -15,19 +15,19 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import static io.netty.handler.codec.http.CookieEncoderUtil.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import static io.netty.handler.codec.http.CookieEncoderUtil.*;
/** /**
* Encodes server-side {@link Cookie}s into HTTP header values. This encoder can encode * Encodes server-side {@link Cookie}s into HTTP header values. This encoder can encode
* the HTTP cookie version 0, 1, and 2. * the HTTP cookie version 0, 1, and 2.
* <pre> * <pre>
* // Example * // Example
* {@link HttpRequestHeader} req = ...; * {@link HttpRequest} req = ...;
* res.setHeader("Set-Cookie", {@link ServerCookieEncoder}.encode("JSESSIONID", "1234")); * res.setHeader("Set-Cookie", {@link ServerCookieEncoder}.encode("JSESSIONID", "1234"));
* </pre> * </pre>
* *

View File

@ -15,7 +15,7 @@
*/ */
package io.netty.handler.codec.http.multipart; package io.netty.handler.codec.http.multipart;
import io.netty.handler.codec.http.HttpRequestHeader; import io.netty.handler.codec.http.HttpRequest;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
@ -46,8 +46,8 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
/** /**
* Keep all HttpDatas until cleanAllHttpDatas() is called. * Keep all HttpDatas until cleanAllHttpDatas() is called.
*/ */
private final ConcurrentHashMap<HttpRequestHeader, List<HttpData>> requestFileDeleteMap = private final ConcurrentHashMap<HttpRequest, List<HttpData>> requestFileDeleteMap =
new ConcurrentHashMap<HttpRequestHeader, List<HttpData>>(); new ConcurrentHashMap<HttpRequest, List<HttpData>>();
/** /**
* HttpData will be in memory if less than default size (16KB). * HttpData will be in memory if less than default size (16KB).
* The type will be Mixed. * The type will be Mixed.
@ -79,7 +79,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
/** /**
* @return the associated list of Files for the request * @return the associated list of Files for the request
*/ */
private List<HttpData> getList(HttpRequestHeader request) { private List<HttpData> getList(HttpRequest request) {
List<HttpData> list = requestFileDeleteMap.get(request); List<HttpData> list = requestFileDeleteMap.get(request);
if (list == null) { if (list == null) {
list = new ArrayList<HttpData>(); list = new ArrayList<HttpData>();
@ -89,7 +89,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
} }
@Override @Override
public Attribute createAttribute(HttpRequestHeader request, String name) { public Attribute createAttribute(HttpRequest request, String name) {
if (useDisk) { if (useDisk) {
Attribute attribute = new DiskAttribute(name); Attribute attribute = new DiskAttribute(name);
List<HttpData> fileToDelete = getList(request); List<HttpData> fileToDelete = getList(request);
@ -106,7 +106,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
} }
@Override @Override
public Attribute createAttribute(HttpRequestHeader request, String name, String value) { public Attribute createAttribute(HttpRequest request, String name, String value) {
if (useDisk) { if (useDisk) {
Attribute attribute; Attribute attribute;
try { try {
@ -133,7 +133,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
} }
@Override @Override
public FileUpload createFileUpload(HttpRequestHeader request, String name, String filename, public FileUpload createFileUpload(HttpRequest request, String name, String filename,
String contentType, String contentTransferEncoding, Charset charset, String contentType, String contentTransferEncoding, Charset charset,
long size) { long size) {
if (useDisk) { if (useDisk) {
@ -155,7 +155,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
} }
@Override @Override
public void removeHttpDataFromClean(HttpRequestHeader request, InterfaceHttpData data) { public void removeHttpDataFromClean(HttpRequest request, InterfaceHttpData data) {
if (data instanceof HttpData) { if (data instanceof HttpData) {
List<HttpData> fileToDelete = getList(request); List<HttpData> fileToDelete = getList(request);
fileToDelete.remove(data); fileToDelete.remove(data);
@ -163,7 +163,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
} }
@Override @Override
public void cleanRequestHttpDatas(HttpRequestHeader request) { public void cleanRequestHttpDatas(HttpRequest request) {
List<HttpData> fileToDelete = requestFileDeleteMap.remove(request); List<HttpData> fileToDelete = requestFileDeleteMap.remove(request);
if (fileToDelete != null) { if (fileToDelete != null) {
for (HttpData data: fileToDelete) { for (HttpData data: fileToDelete) {
@ -175,7 +175,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
@Override @Override
public void cleanAllHttpDatas() { public void cleanAllHttpDatas() {
for (HttpRequestHeader request : requestFileDeleteMap.keySet()) { for (HttpRequest request : requestFileDeleteMap.keySet()) {
List<HttpData> fileToDelete = requestFileDeleteMap.get(request); List<HttpData> fileToDelete = requestFileDeleteMap.get(request);
if (fileToDelete != null) { if (fileToDelete != null) {
for (HttpData data: fileToDelete) { for (HttpData data: fileToDelete) {

View File

@ -15,7 +15,7 @@
*/ */
package io.netty.handler.codec.http.multipart; package io.netty.handler.codec.http.multipart;
import io.netty.handler.codec.http.HttpRequestHeader; import io.netty.handler.codec.http.HttpRequest;
import java.nio.charset.Charset; import java.nio.charset.Charset;
@ -28,20 +28,20 @@ public interface HttpDataFactory {
* @param request associated request * @param request associated request
* @return a new Attribute with no value * @return a new Attribute with no value
*/ */
Attribute createAttribute(HttpRequestHeader request, String name); Attribute createAttribute(HttpRequest request, String name);
/** /**
* @param request associated request * @param request associated request
* @return a new Attribute * @return a new Attribute
*/ */
Attribute createAttribute(HttpRequestHeader request, String name, String value); Attribute createAttribute(HttpRequest request, String name, String value);
/** /**
* @param request associated request * @param request associated request
* @param size the size of the Uploaded file * @param size the size of the Uploaded file
* @return a new FileUpload * @return a new FileUpload
*/ */
FileUpload createFileUpload(HttpRequestHeader request, String name, String filename, FileUpload createFileUpload(HttpRequest request, String name, String filename,
String contentType, String contentTransferEncoding, Charset charset, String contentType, String contentTransferEncoding, Charset charset,
long size); long size);
@ -50,14 +50,14 @@ public interface HttpDataFactory {
* is still a temporary one as setup at construction) * is still a temporary one as setup at construction)
* @param request associated request * @param request associated request
*/ */
void removeHttpDataFromClean(HttpRequestHeader request, InterfaceHttpData data); void removeHttpDataFromClean(HttpRequest request, InterfaceHttpData data);
/** /**
* Remove all InterfaceHttpData from virtual File storage from clean list for the request * Remove all InterfaceHttpData from virtual File storage from clean list for the request
* *
* @param request associated request * @param request associated request
*/ */
void cleanRequestHttpDatas(HttpRequestHeader request); void cleanRequestHttpDatas(HttpRequest request);
/** /**
* Remove all InterfaceHttpData from virtual File storage from clean list for all requests * Remove all InterfaceHttpData from virtual File storage from clean list for all requests

View File

@ -16,11 +16,11 @@
package io.netty.handler.codec.http.multipart; package io.netty.handler.codec.http.multipart;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpConstants; import io.netty.handler.codec.http.HttpConstants;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequestHeader; import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadNoBackArrayException; import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadNoBackArrayException;
import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadOptimize; import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadOptimize;
@ -50,7 +50,7 @@ public class HttpPostRequestDecoder {
/** /**
* Request to decode * Request to decode
*/ */
private final HttpRequestHeader request; private final HttpRequest request;
/** /**
* Default charset to use * Default charset to use
@ -136,7 +136,7 @@ public class HttpPostRequestDecoder {
* if the default charset was wrong when decoding or other * if the default charset was wrong when decoding or other
* errors * errors
*/ */
public HttpPostRequestDecoder(HttpRequestHeader request) throws ErrorDataDecoderException, public HttpPostRequestDecoder(HttpRequest request) throws ErrorDataDecoderException,
IncompatibleDataDecoderException { IncompatibleDataDecoderException {
this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, HttpConstants.DEFAULT_CHARSET); this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, HttpConstants.DEFAULT_CHARSET);
} }
@ -155,7 +155,7 @@ public class HttpPostRequestDecoder {
* if the default charset was wrong when decoding or other * if the default charset was wrong when decoding or other
* errors * errors
*/ */
public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequestHeader request) throws ErrorDataDecoderException, public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request) throws ErrorDataDecoderException,
IncompatibleDataDecoderException { IncompatibleDataDecoderException {
this(factory, request, HttpConstants.DEFAULT_CHARSET); this(factory, request, HttpConstants.DEFAULT_CHARSET);
} }
@ -176,7 +176,7 @@ public class HttpPostRequestDecoder {
* if the default charset was wrong when decoding or other * if the default charset was wrong when decoding or other
* errors * errors
*/ */
public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequestHeader request, Charset charset) public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request, Charset charset)
throws ErrorDataDecoderException, IncompatibleDataDecoderException { throws ErrorDataDecoderException, IncompatibleDataDecoderException {
if (factory == null) { if (factory == null) {
throw new NullPointerException("factory"); throw new NullPointerException("factory");
@ -188,15 +188,15 @@ public class HttpPostRequestDecoder {
throw new NullPointerException("charset"); throw new NullPointerException("charset");
} }
this.request = request; this.request = request;
HttpMethod method = request.getMethod(); HttpMethod method = request.method();
if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT) || method.equals(HttpMethod.PATCH)) { if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT) || method.equals(HttpMethod.PATCH)) {
bodyToDecode = true; bodyToDecode = true;
} }
this.charset = charset; this.charset = charset;
this.factory = factory; this.factory = factory;
// Fill default values // Fill default values
if (this.request.containsHeader(HttpHeaders.Names.CONTENT_TYPE)) { if (this.request.headers().contains(HttpHeaders.Names.CONTENT_TYPE)) {
checkMultipart(this.request.getHeader(HttpHeaders.Names.CONTENT_TYPE)); checkMultipart(this.request.headers().get(HttpHeaders.Names.CONTENT_TYPE));
} else { } else {
isMultipart = false; isMultipart = false;
} }
@ -341,7 +341,7 @@ public class HttpPostRequestDecoder {
* errors * errors
*/ */
public void offer(HttpContent content) throws ErrorDataDecoderException { public void offer(HttpContent content) throws ErrorDataDecoderException {
ByteBuf chunked = content.getContent(); ByteBuf chunked = content.data();
if (undecodedChunk == null) { if (undecodedChunk == null) {
undecodedChunk = chunked; undecodedChunk = chunked;
} else { } else {

View File

@ -18,11 +18,11 @@ package io.netty.handler.codec.http.multipart;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf; import io.netty.buffer.MessageBuf;
import io.netty.handler.codec.http.DefaultHttpContent; import io.netty.handler.codec.http.DefaultHttpContent;
import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpConstants; import io.netty.handler.codec.http.HttpConstants;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.stream.ChunkedMessageInput; import io.netty.handler.stream.ChunkedMessageInput;
import java.io.File; import java.io.File;
@ -49,7 +49,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
/** /**
* Request to encode * Request to encode
*/ */
private final HttpRequest request; private final FullHttpRequest request;
/** /**
* Default charset to use * Default charset to use
@ -100,7 +100,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
* @throws ErrorDataEncoderException * @throws ErrorDataEncoderException
* if the request is not a POST * if the request is not a POST
*/ */
public HttpPostRequestEncoder(HttpRequest request, boolean multipart) throws ErrorDataEncoderException { public HttpPostRequestEncoder(FullHttpRequest request, boolean multipart) throws ErrorDataEncoderException {
this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, multipart, this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, multipart,
HttpConstants.DEFAULT_CHARSET); HttpConstants.DEFAULT_CHARSET);
} }
@ -118,7 +118,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
* @throws ErrorDataEncoderException * @throws ErrorDataEncoderException
* if the request is not a POST * if the request is not a POST
*/ */
public HttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request, boolean multipart) public HttpPostRequestEncoder(HttpDataFactory factory, FullHttpRequest request, boolean multipart)
throws ErrorDataEncoderException { throws ErrorDataEncoderException {
this(factory, request, multipart, HttpConstants.DEFAULT_CHARSET); this(factory, request, multipart, HttpConstants.DEFAULT_CHARSET);
} }
@ -138,7 +138,8 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
* @throws ErrorDataEncoderException * @throws ErrorDataEncoderException
* if the request is not a POST * if the request is not a POST
*/ */
public HttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request, boolean multipart, Charset charset) public HttpPostRequestEncoder(
HttpDataFactory factory, FullHttpRequest request, boolean multipart, Charset charset)
throws ErrorDataEncoderException { throws ErrorDataEncoderException {
if (factory == null) { if (factory == null) {
throw new NullPointerException("factory"); throw new NullPointerException("factory");
@ -149,7 +150,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
if (charset == null) { if (charset == null) {
throw new NullPointerException("charset"); throw new NullPointerException("charset");
} }
if (request.getMethod() != HttpMethod.POST) { if (request.method() != HttpMethod.POST) {
throw new ErrorDataEncoderException("Cannot create a Encoder if not a POST"); throw new ErrorDataEncoderException("Cannot create a Encoder if not a POST");
} }
this.request = request; this.request = request;
@ -598,7 +599,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
* @throws ErrorDataEncoderException * @throws ErrorDataEncoderException
* if the encoding is in error or if the finalize were already done * if the encoding is in error or if the finalize were already done
*/ */
public HttpRequest finalizeRequest() throws ErrorDataEncoderException { public FullHttpRequest finalizeRequest() throws ErrorDataEncoderException {
// Finalize the multipartHttpDatas // Finalize the multipartHttpDatas
if (!headerFinalized) { if (!headerFinalized) {
if (isMultipart) { if (isMultipart) {
@ -617,10 +618,10 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
} else { } else {
throw new ErrorDataEncoderException("Header already encoded"); throw new ErrorDataEncoderException("Header already encoded");
} }
List<String> contentTypes = request.getHeaders(HttpHeaders.Names.CONTENT_TYPE); List<String> contentTypes = request.headers().getAll(HttpHeaders.Names.CONTENT_TYPE);
List<String> transferEncoding = request.getHeaders(HttpHeaders.Names.TRANSFER_ENCODING); List<String> transferEncoding = request.headers().getAll(HttpHeaders.Names.TRANSFER_ENCODING);
if (contentTypes != null) { if (contentTypes != null) {
request.removeHeader(HttpHeaders.Names.CONTENT_TYPE); request.headers().remove(HttpHeaders.Names.CONTENT_TYPE);
for (String contentType : contentTypes) { for (String contentType : contentTypes) {
// "multipart/form-data; boundary=--89421926422648" // "multipart/form-data; boundary=--89421926422648"
if (contentType.toLowerCase().startsWith(HttpHeaders.Values.MULTIPART_FORM_DATA)) { if (contentType.toLowerCase().startsWith(HttpHeaders.Values.MULTIPART_FORM_DATA)) {
@ -628,17 +629,17 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
} else if (contentType.toLowerCase().startsWith(HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED)) { } else if (contentType.toLowerCase().startsWith(HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED)) {
// ignore // ignore
} else { } else {
request.addHeader(HttpHeaders.Names.CONTENT_TYPE, contentType); request.headers().add(HttpHeaders.Names.CONTENT_TYPE, contentType);
} }
} }
} }
if (isMultipart) { if (isMultipart) {
String value = HttpHeaders.Values.MULTIPART_FORM_DATA + "; " + HttpHeaders.Values.BOUNDARY + '=' String value = HttpHeaders.Values.MULTIPART_FORM_DATA + "; " + HttpHeaders.Values.BOUNDARY + '='
+ multipartDataBoundary; + multipartDataBoundary;
request.addHeader(HttpHeaders.Names.CONTENT_TYPE, value); request.headers().add(HttpHeaders.Names.CONTENT_TYPE, value);
} else { } else {
// Not multipart // Not multipart
request.addHeader(HttpHeaders.Names.CONTENT_TYPE, HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED); request.headers().add(HttpHeaders.Names.CONTENT_TYPE, HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED);
} }
// Now consider size for chunk or not // Now consider size for chunk or not
long realSize = globalBodySize; long realSize = globalBodySize;
@ -648,25 +649,25 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
realSize -= 1; // last '&' removed realSize -= 1; // last '&' removed
iterator = multipartHttpDatas.listIterator(); iterator = multipartHttpDatas.listIterator();
} }
request.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(realSize)); request.headers().set(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(realSize));
if (realSize > HttpPostBodyUtil.chunkSize || isMultipart) { if (realSize > HttpPostBodyUtil.chunkSize || isMultipart) {
isChunked = true; isChunked = true;
if (transferEncoding != null) { if (transferEncoding != null) {
request.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING); request.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
for (String v : transferEncoding) { for (String v : transferEncoding) {
if (v.equalsIgnoreCase(HttpHeaders.Values.CHUNKED)) { if (v.equalsIgnoreCase(HttpHeaders.Values.CHUNKED)) {
// ignore // ignore
} else { } else {
request.addHeader(HttpHeaders.Names.TRANSFER_ENCODING, v); request.headers().add(HttpHeaders.Names.TRANSFER_ENCODING, v);
} }
} }
} }
HttpHeaders.setTransferEncodingChunked(request); HttpHeaders.setTransferEncodingChunked(request);
request.setContent(EMPTY_BUFFER); request.data().clear();
} else { } else {
// get the only one body and set it to the request // get the only one body and set it to the request
HttpContent chunk = nextChunk(); HttpContent chunk = nextChunk();
request.setContent(chunk.getContent()); request.data().clear().writeBytes(chunk.data());
} }
return request; return request;
} }

View File

@ -18,7 +18,7 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import java.net.URI; import java.net.URI;
import java.util.Map; import java.util.Map;
@ -148,5 +148,5 @@ public abstract class WebSocketClientHandshaker {
* @param response * @param response
* HTTP response containing the closing handshake details * HTTP response containing the closing handshake details
*/ */
public abstract void finishHandshake(Channel channel, HttpResponse response); public abstract void finishHandshake(Channel channel, FullHttpResponse response);
} }

View File

@ -15,19 +15,18 @@
*/ */
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.HttpHeaders.Values; import io.netty.handler.codec.http.HttpHeaders.Values;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpRequestEncoder; import io.netty.handler.codec.http.HttpRequestEncoder;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseDecoder; import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
@ -142,10 +141,10 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
} }
// Format request // Format request
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
request.addHeader(Names.UPGRADE, Values.WEBSOCKET); request.headers().add(Names.UPGRADE, Values.WEBSOCKET);
request.addHeader(Names.CONNECTION, Values.UPGRADE); request.headers().add(Names.CONNECTION, Values.UPGRADE);
request.addHeader(Names.HOST, wsURL.getHost()); request.headers().add(Names.HOST, wsURL.getHost());
int wsPort = wsURL.getPort(); int wsPort = wsURL.getPort();
String originValue = "http://" + wsURL.getHost(); String originValue = "http://" + wsURL.getHost();
@ -155,24 +154,24 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
originValue = originValue + ':' + wsPort; originValue = originValue + ':' + wsPort;
} }
request.addHeader(Names.ORIGIN, originValue); request.headers().add(Names.ORIGIN, originValue);
request.addHeader(Names.SEC_WEBSOCKET_KEY1, key1); request.headers().add(Names.SEC_WEBSOCKET_KEY1, key1);
request.addHeader(Names.SEC_WEBSOCKET_KEY2, key2); request.headers().add(Names.SEC_WEBSOCKET_KEY2, key2);
String expectedSubprotocol = getExpectedSubprotocol(); String expectedSubprotocol = getExpectedSubprotocol();
if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) { if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) {
request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); request.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
} }
if (customHeaders != null) { if (customHeaders != null) {
for (Map.Entry<String, String> e : customHeaders.entrySet()) { for (Map.Entry<String, String> e : customHeaders.entrySet()) {
request.addHeader(e.getKey(), e.getValue()); request.headers().add(e.getKey(), e.getValue());
} }
} }
// Set Content-Length to workaround some known defect. // Set Content-Length to workaround some known defect.
// See also: http://www.ietf.org/mail-archive/web/hybi/current/msg02149.html // See also: http://www.ietf.org/mail-archive/web/hybi/current/msg02149.html
request.setHeader(Names.CONTENT_LENGTH, key3.length); request.headers().set(Names.CONTENT_LENGTH, key3.length);
request.setContent(Unpooled.copiedBuffer(key3)); request.data().writeBytes(key3);
ChannelFuture future = channel.write(request); ChannelFuture future = channel.write(request);
future.addListener(new ChannelFutureListener() { future.addListener(new ChannelFutureListener() {
@ -217,31 +216,31 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
* @throws WebSocketHandshakeException * @throws WebSocketHandshakeException
*/ */
@Override @Override
public void finishHandshake(Channel channel, HttpResponse response) { public void finishHandshake(Channel channel, FullHttpResponse response) {
final HttpResponseStatus status = new HttpResponseStatus(101, "WebSocket Protocol Handshake"); final HttpResponseStatus status = new HttpResponseStatus(101, "WebSocket Protocol Handshake");
if (!response.getStatus().equals(status)) { if (!response.status().equals(status)) {
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus()); throw new WebSocketHandshakeException("Invalid handshake response status: " + response.status());
} }
String upgrade = response.getHeader(Names.UPGRADE); String upgrade = response.headers().get(Names.UPGRADE);
if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) { if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: " throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
+ upgrade); + upgrade);
} }
String connection = response.getHeader(Names.CONNECTION); String connection = response.headers().get(Names.CONNECTION);
if (!Values.UPGRADE.equalsIgnoreCase(connection)) { if (!Values.UPGRADE.equalsIgnoreCase(connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: " throw new WebSocketHandshakeException("Invalid handshake response connection: "
+ connection); + connection);
} }
byte[] challenge = response.getContent().array(); byte[] challenge = response.data().array();
if (!Arrays.equals(challenge, expectedChallengeResponseBytes)) { if (!Arrays.equals(challenge, expectedChallengeResponseBytes)) {
throw new WebSocketHandshakeException("Invalid challenge"); throw new WebSocketHandshakeException("Invalid challenge");
} }
String subprotocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); String subprotocol = response.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
setActualSubprotocol(subprotocol); setActualSubprotocol(subprotocol);
setHandshakeComplete(); setHandshakeComplete();

View File

@ -20,13 +20,13 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.HttpHeaders.Values; import io.netty.handler.codec.http.HttpHeaders.Values;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpRequestEncoder; import io.netty.handler.codec.http.HttpRequestEncoder;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseDecoder; import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
@ -124,11 +124,11 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
} }
// Format request // Format request
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
request.addHeader(Names.UPGRADE, Values.WEBSOCKET.toLowerCase()); request.headers().add(Names.UPGRADE, Values.WEBSOCKET.toLowerCase());
request.addHeader(Names.CONNECTION, Values.UPGRADE); request.headers().add(Names.CONNECTION, Values.UPGRADE);
request.addHeader(Names.SEC_WEBSOCKET_KEY, key); request.headers().add(Names.SEC_WEBSOCKET_KEY, key);
request.addHeader(Names.HOST, wsURL.getHost()); request.headers().add(Names.HOST, wsURL.getHost());
int wsPort = wsURL.getPort(); int wsPort = wsURL.getPort();
String originValue = "http://" + wsURL.getHost(); String originValue = "http://" + wsURL.getHost();
@ -137,18 +137,18 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
// See http://tools.ietf.org/html/rfc6454#section-6.2 // See http://tools.ietf.org/html/rfc6454#section-6.2
originValue = originValue + ':' + wsPort; originValue = originValue + ':' + wsPort;
} }
request.addHeader(Names.SEC_WEBSOCKET_ORIGIN, originValue); request.headers().add(Names.SEC_WEBSOCKET_ORIGIN, originValue);
String expectedSubprotocol = getExpectedSubprotocol(); String expectedSubprotocol = getExpectedSubprotocol();
if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) { if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) {
request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); request.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
} }
request.addHeader(Names.SEC_WEBSOCKET_VERSION, "7"); request.headers().add(Names.SEC_WEBSOCKET_VERSION, "7");
if (customHeaders != null) { if (customHeaders != null) {
for (Map.Entry<String, String> e : customHeaders.entrySet()) { for (Map.Entry<String, String> e : customHeaders.entrySet()) {
request.addHeader(e.getKey(), e.getValue()); request.headers().add(e.getKey(), e.getValue());
} }
} }
@ -192,32 +192,32 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
* @throws WebSocketHandshakeException * @throws WebSocketHandshakeException
*/ */
@Override @Override
public void finishHandshake(Channel channel, HttpResponse response) { public void finishHandshake(Channel channel, FullHttpResponse response) {
final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS; final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
if (!response.getStatus().equals(status)) { if (!response.status().equals(status)) {
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus()); throw new WebSocketHandshakeException("Invalid handshake response status: " + response.status());
} }
String upgrade = response.getHeader(Names.UPGRADE); String upgrade = response.headers().get(Names.UPGRADE);
if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) { if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: " throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
+ response.getHeader(Names.UPGRADE)); + response.headers().get(Names.UPGRADE));
} }
String connection = response.getHeader(Names.CONNECTION); String connection = response.headers().get(Names.CONNECTION);
if (!Values.UPGRADE.equalsIgnoreCase(connection)) { if (!Values.UPGRADE.equalsIgnoreCase(connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: " throw new WebSocketHandshakeException("Invalid handshake response connection: "
+ response.getHeader(Names.CONNECTION)); + response.headers().get(Names.CONNECTION));
} }
String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT); String accept = response.headers().get(Names.SEC_WEBSOCKET_ACCEPT);
if (accept == null || !accept.equals(expectedChallengeResponseString)) { if (accept == null || !accept.equals(expectedChallengeResponseString)) {
throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept, throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept,
expectedChallengeResponseString)); expectedChallengeResponseString));
} }
String subprotocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); String subprotocol = response.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
setActualSubprotocol(subprotocol); setActualSubprotocol(subprotocol);
setHandshakeComplete(); setHandshakeComplete();

View File

@ -20,13 +20,13 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.HttpHeaders.Values; import io.netty.handler.codec.http.HttpHeaders.Values;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpRequestEncoder; import io.netty.handler.codec.http.HttpRequestEncoder;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseDecoder; import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
@ -124,11 +124,11 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
} }
// Format request // Format request
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
request.addHeader(Names.UPGRADE, Values.WEBSOCKET.toLowerCase()); request.headers().add(Names.UPGRADE, Values.WEBSOCKET.toLowerCase());
request.addHeader(Names.CONNECTION, Values.UPGRADE); request.headers().add(Names.CONNECTION, Values.UPGRADE);
request.addHeader(Names.SEC_WEBSOCKET_KEY, key); request.headers().add(Names.SEC_WEBSOCKET_KEY, key);
request.addHeader(Names.HOST, wsURL.getHost()); request.headers().add(Names.HOST, wsURL.getHost());
int wsPort = wsURL.getPort(); int wsPort = wsURL.getPort();
String originValue = "http://" + wsURL.getHost(); String originValue = "http://" + wsURL.getHost();
@ -137,18 +137,18 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
// See http://tools.ietf.org/html/rfc6454#section-6.2 // See http://tools.ietf.org/html/rfc6454#section-6.2
originValue = originValue + ':' + wsPort; originValue = originValue + ':' + wsPort;
} }
request.addHeader(Names.SEC_WEBSOCKET_ORIGIN, originValue); request.headers().add(Names.SEC_WEBSOCKET_ORIGIN, originValue);
String expectedSubprotocol = getExpectedSubprotocol(); String expectedSubprotocol = getExpectedSubprotocol();
if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) { if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) {
request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); request.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
} }
request.addHeader(Names.SEC_WEBSOCKET_VERSION, "8"); request.headers().add(Names.SEC_WEBSOCKET_VERSION, "8");
if (customHeaders != null) { if (customHeaders != null) {
for (Map.Entry<String, String> e : customHeaders.entrySet()) { for (Map.Entry<String, String> e : customHeaders.entrySet()) {
request.addHeader(e.getKey(), e.getValue()); request.headers().add(e.getKey(), e.getValue());
} }
} }
@ -192,32 +192,32 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
* @throws WebSocketHandshakeException * @throws WebSocketHandshakeException
*/ */
@Override @Override
public void finishHandshake(Channel channel, HttpResponse response) { public void finishHandshake(Channel channel, FullHttpResponse response) {
final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS; final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
if (!response.getStatus().equals(status)) { if (!response.status().equals(status)) {
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus()); throw new WebSocketHandshakeException("Invalid handshake response status: " + response.status());
} }
String upgrade = response.getHeader(Names.UPGRADE); String upgrade = response.headers().get(Names.UPGRADE);
if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) { if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: " throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
+ response.getHeader(Names.UPGRADE)); + response.headers().get(Names.UPGRADE));
} }
String connection = response.getHeader(Names.CONNECTION); String connection = response.headers().get(Names.CONNECTION);
if (!Values.UPGRADE.equalsIgnoreCase(connection)) { if (!Values.UPGRADE.equalsIgnoreCase(connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: " throw new WebSocketHandshakeException("Invalid handshake response connection: "
+ response.getHeader(Names.CONNECTION)); + response.headers().get(Names.CONNECTION));
} }
String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT); String accept = response.headers().get(Names.SEC_WEBSOCKET_ACCEPT);
if (accept == null || !accept.equals(expectedChallengeResponseString)) { if (accept == null || !accept.equals(expectedChallengeResponseString)) {
throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept, throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept,
expectedChallengeResponseString)); expectedChallengeResponseString));
} }
String subprotocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); String subprotocol = response.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
setActualSubprotocol(subprotocol); setActualSubprotocol(subprotocol);
setHandshakeComplete(); setHandshakeComplete();

View File

@ -20,13 +20,13 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.DefaultHttpRequestHeader; import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.HttpHeaders.Values; import io.netty.handler.codec.http.HttpHeaders.Values;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequestHeader; import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpRequestEncoder; import io.netty.handler.codec.http.HttpRequestEncoder;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseDecoder; import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
@ -124,11 +124,11 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
} }
// Format request // Format request
HttpRequestHeader request = new DefaultHttpRequestHeader(HttpVersion.HTTP_1_1, HttpMethod.GET, path); HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
request.addHeader(Names.UPGRADE, Values.WEBSOCKET.toLowerCase()); request.headers().add(Names.UPGRADE, Values.WEBSOCKET.toLowerCase());
request.addHeader(Names.CONNECTION, Values.UPGRADE); request.headers().add(Names.CONNECTION, Values.UPGRADE);
request.addHeader(Names.SEC_WEBSOCKET_KEY, key); request.headers().add(Names.SEC_WEBSOCKET_KEY, key);
request.addHeader(Names.HOST, wsURL.getHost()); request.headers().add(Names.HOST, wsURL.getHost());
int wsPort = wsURL.getPort(); int wsPort = wsURL.getPort();
String originValue = "http://" + wsURL.getHost(); String originValue = "http://" + wsURL.getHost();
@ -137,18 +137,18 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
// See http://tools.ietf.org/html/rfc6454#section-6.2 // See http://tools.ietf.org/html/rfc6454#section-6.2
originValue = originValue + ':' + wsPort; originValue = originValue + ':' + wsPort;
} }
request.addHeader(Names.SEC_WEBSOCKET_ORIGIN, originValue); request.headers().add(Names.SEC_WEBSOCKET_ORIGIN, originValue);
String expectedSubprotocol = getExpectedSubprotocol(); String expectedSubprotocol = getExpectedSubprotocol();
if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) { if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) {
request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); request.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
} }
request.addHeader(Names.SEC_WEBSOCKET_VERSION, "13"); request.headers().add(Names.SEC_WEBSOCKET_VERSION, "13");
if (customHeaders != null) { if (customHeaders != null) {
for (Map.Entry<String, String> e: customHeaders.entrySet()) { for (Map.Entry<String, String> e: customHeaders.entrySet()) {
request.addHeader(e.getKey(), e.getValue()); request.headers().add(e.getKey(), e.getValue());
} }
} }
@ -191,32 +191,32 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
* @throws WebSocketHandshakeException * @throws WebSocketHandshakeException
*/ */
@Override @Override
public void finishHandshake(Channel channel, HttpResponse response) { public void finishHandshake(Channel channel, FullHttpResponse response) {
final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS; final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
if (!response.getStatus().equals(status)) { if (!response.status().equals(status)) {
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus()); throw new WebSocketHandshakeException("Invalid handshake response status: " + response.status());
} }
String upgrade = response.getHeader(Names.UPGRADE); String upgrade = response.headers().get(Names.UPGRADE);
if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) { if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: " throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
+ response.getHeader(Names.UPGRADE)); + response.headers().get(Names.UPGRADE));
} }
String connection = response.getHeader(Names.CONNECTION); String connection = response.headers().get(Names.CONNECTION);
if (!Values.UPGRADE.equalsIgnoreCase(connection)) { if (!Values.UPGRADE.equalsIgnoreCase(connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: " throw new WebSocketHandshakeException("Invalid handshake response connection: "
+ response.getHeader(Names.CONNECTION)); + response.headers().get(Names.CONNECTION));
} }
String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT); String accept = response.headers().get(Names.SEC_WEBSOCKET_ACCEPT);
if (accept == null || !accept.equals(expectedChallengeResponseString)) { if (accept == null || !accept.equals(expectedChallengeResponseString)) {
throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept, throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept,
expectedChallengeResponseString)); expectedChallengeResponseString));
} }
String subprotocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); String subprotocol = response.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
setActualSubprotocol(subprotocol); setActualSubprotocol(subprotocol);
setHandshakeComplete(); setHandshakeComplete();

View File

@ -18,7 +18,7 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
import java.util.Collections; import java.util.Collections;
@ -112,7 +112,7 @@ public abstract class WebSocketServerHandshaker {
* @param req * @param req
* HTTP Request * HTTP Request
*/ */
public ChannelFuture handshake(Channel channel, HttpRequest req) { public ChannelFuture handshake(Channel channel, FullHttpRequest req) {
if (channel == null) { if (channel == null) {
throw new NullPointerException("channel"); throw new NullPointerException("channel");
} }
@ -129,7 +129,7 @@ public abstract class WebSocketServerHandshaker {
* @param promise * @param promise
* the {@link ChannelPromise} to be notified when the opening handshake is done * the {@link ChannelPromise} to be notified when the opening handshake is done
*/ */
public abstract ChannelFuture handshake(Channel channel, HttpRequest req, ChannelPromise promise); public abstract ChannelFuture handshake(Channel channel, FullHttpRequest req, ChannelPromise promise);
/** /**
* Performs the closing handshake * Performs the closing handshake

View File

@ -22,13 +22,13 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.HttpHeaders.Values; import io.netty.handler.codec.http.HttpHeaders.Values;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.logging.InternalLogger; import io.netty.logging.InternalLogger;
@ -119,64 +119,63 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
* HTTP request * HTTP request
*/ */
@Override @Override
public ChannelFuture handshake(Channel channel, HttpRequest req, ChannelPromise promise) { public ChannelFuture handshake(Channel channel, FullHttpRequest req, ChannelPromise promise) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug(String.format("Channel %s WS Version 00 server handshake", channel.id())); logger.debug(String.format("Channel %s WS Version 00 server handshake", channel.id()));
} }
// Serve the WebSocket handshake request. // Serve the WebSocket handshake request.
if (!Values.UPGRADE.equalsIgnoreCase(req.getHeader(CONNECTION)) if (!Values.UPGRADE.equalsIgnoreCase(req.headers().get(CONNECTION))
|| !WEBSOCKET.equalsIgnoreCase(req.getHeader(Names.UPGRADE))) { || !WEBSOCKET.equalsIgnoreCase(req.headers().get(Names.UPGRADE))) {
throw new WebSocketHandshakeException("not a WebSocket handshake request: missing upgrade"); throw new WebSocketHandshakeException("not a WebSocket handshake request: missing upgrade");
} }
// Hixie 75 does not contain these headers while Hixie 76 does // Hixie 75 does not contain these headers while Hixie 76 does
boolean isHixie76 = req.containsHeader(SEC_WEBSOCKET_KEY1) && req.containsHeader(SEC_WEBSOCKET_KEY2); boolean isHixie76 = req.headers().contains(SEC_WEBSOCKET_KEY1) && req.headers().contains(SEC_WEBSOCKET_KEY2);
// Create the WebSocket handshake response. // Create the WebSocket handshake response.
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, new HttpResponseStatus(101, FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, new HttpResponseStatus(101,
isHixie76 ? "WebSocket Protocol Handshake" : "Web Socket Protocol Handshake")); isHixie76 ? "WebSocket Protocol Handshake" : "Web Socket Protocol Handshake"));
res.addHeader(Names.UPGRADE, WEBSOCKET); res.headers().add(Names.UPGRADE, WEBSOCKET);
res.addHeader(CONNECTION, Values.UPGRADE); res.headers().add(CONNECTION, Values.UPGRADE);
// Fill in the headers and contents depending on handshake method. // Fill in the headers and contents depending on handshake method.
if (isHixie76) { if (isHixie76) {
// New handshake method with a challenge: // New handshake method with a challenge:
res.addHeader(SEC_WEBSOCKET_ORIGIN, req.getHeader(ORIGIN)); res.headers().add(SEC_WEBSOCKET_ORIGIN, req.headers().get(ORIGIN));
res.addHeader(SEC_WEBSOCKET_LOCATION, getWebSocketUrl()); res.headers().add(SEC_WEBSOCKET_LOCATION, getWebSocketUrl());
String subprotocols = req.getHeader(SEC_WEBSOCKET_PROTOCOL); String subprotocols = req.headers().get(SEC_WEBSOCKET_PROTOCOL);
if (subprotocols != null) { if (subprotocols != null) {
String selectedSubprotocol = selectSubprotocol(subprotocols); String selectedSubprotocol = selectSubprotocol(subprotocols);
if (selectedSubprotocol == null) { if (selectedSubprotocol == null) {
throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols); throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols);
} else { } else {
res.addHeader(SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol); res.headers().add(SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
setSelectedSubprotocol(selectedSubprotocol); setSelectedSubprotocol(selectedSubprotocol);
} }
} }
// Calculate the answer of the challenge. // Calculate the answer of the challenge.
String key1 = req.getHeader(SEC_WEBSOCKET_KEY1); String key1 = req.headers().get(SEC_WEBSOCKET_KEY1);
String key2 = req.getHeader(SEC_WEBSOCKET_KEY2); String key2 = req.headers().get(SEC_WEBSOCKET_KEY2);
int a = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key1).replaceAll("")) / int a = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key1).replaceAll("")) /
BEGINNING_SPACE.matcher(key1).replaceAll("").length()); BEGINNING_SPACE.matcher(key1).replaceAll("").length());
int b = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key2).replaceAll("")) / int b = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key2).replaceAll("")) /
BEGINNING_SPACE.matcher(key2).replaceAll("").length()); BEGINNING_SPACE.matcher(key2).replaceAll("").length());
long c = req.getContent().readLong(); long c = req.data().readLong();
ByteBuf input = Unpooled.buffer(16); ByteBuf input = Unpooled.buffer(16);
input.writeInt(a); input.writeInt(a);
input.writeInt(b); input.writeInt(b);
input.writeLong(c); input.writeLong(c);
ByteBuf output = Unpooled.wrappedBuffer(WebSocketUtil.md5(input.array())); res.data().writeBytes(WebSocketUtil.md5(input.array()));
res.setContent(output);
} else { } else {
// Old Hixie 75 handshake method with no challenge: // Old Hixie 75 handshake method with no challenge:
res.addHeader(WEBSOCKET_ORIGIN, req.getHeader(ORIGIN)); res.headers().add(WEBSOCKET_ORIGIN, req.headers().get(ORIGIN));
res.addHeader(WEBSOCKET_LOCATION, getWebSocketUrl()); res.headers().add(WEBSOCKET_LOCATION, getWebSocketUrl());
String protocol = req.getHeader(WEBSOCKET_PROTOCOL); String protocol = req.headers().get(WEBSOCKET_PROTOCOL);
if (protocol != null) { if (protocol != null) {
res.addHeader(WEBSOCKET_PROTOCOL, selectSubprotocol(protocol)); res.headers().add(WEBSOCKET_PROTOCOL, selectSubprotocol(protocol));
} }
} }

View File

@ -20,12 +20,12 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.logging.InternalLogger; import io.netty.logging.InternalLogger;
@ -109,15 +109,16 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker {
* HTTP request * HTTP request
*/ */
@Override @Override
public ChannelFuture handshake(Channel channel, HttpRequest req, ChannelPromise promise) { public ChannelFuture handshake(Channel channel, FullHttpRequest req, ChannelPromise promise) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug(String.format("Channel %s WS Version 7 server handshake", channel.id())); logger.debug(String.format("Channel %s WS Version 7 server handshake", channel.id()));
} }
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS); FullHttpResponse res =
new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS);
String key = req.getHeader(Names.SEC_WEBSOCKET_KEY); String key = req.headers().get(Names.SEC_WEBSOCKET_KEY);
if (key == null) { if (key == null) {
throw new WebSocketHandshakeException("not a WebSocket request: missing key"); throw new WebSocketHandshakeException("not a WebSocket request: missing key");
} }
@ -129,17 +130,16 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker {
logger.debug(String.format("WS Version 7 Server Handshake key: %s. Response: %s.", key, accept)); logger.debug(String.format("WS Version 7 Server Handshake key: %s. Response: %s.", key, accept));
} }
res.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS); res.headers().add(Names.UPGRADE, WEBSOCKET.toLowerCase());
res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase()); res.headers().add(Names.CONNECTION, Names.UPGRADE);
res.addHeader(Names.CONNECTION, Names.UPGRADE); res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept);
res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept); String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
String subprotocols = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
if (subprotocols != null) { if (subprotocols != null) {
String selectedSubprotocol = selectSubprotocol(subprotocols); String selectedSubprotocol = selectSubprotocol(subprotocols);
if (selectedSubprotocol == null) { if (selectedSubprotocol == null) {
throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols); throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols);
} else { } else {
res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol); res.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
setSelectedSubprotocol(selectedSubprotocol); setSelectedSubprotocol(selectedSubprotocol);
} }
} }

View File

@ -20,12 +20,12 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.DefaultHttpResponseHeader; import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseHeader; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.logging.InternalLogger; import io.netty.logging.InternalLogger;
@ -110,15 +110,15 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
* HTTP request * HTTP request
*/ */
@Override @Override
public ChannelFuture handshake(Channel channel, HttpRequest req, ChannelPromise promise) { public ChannelFuture handshake(Channel channel, FullHttpRequest req, ChannelPromise promise) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug(String.format("Channel %s WS Version 8 server handshake", channel.id())); logger.debug(String.format("Channel %s WS Version 8 server handshake", channel.id()));
} }
HttpResponseHeader res = new DefaultHttpResponseHeader(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS); HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS);
String key = req.getHeader(Names.SEC_WEBSOCKET_KEY); String key = req.headers().get(Names.SEC_WEBSOCKET_KEY);
if (key == null) { if (key == null) {
throw new WebSocketHandshakeException("not a WebSocket request: missing key"); throw new WebSocketHandshakeException("not a WebSocket request: missing key");
} }
@ -130,17 +130,16 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
logger.debug(String.format("WS Version 8 Server Handshake key: %s. Response: %s.", key, accept)); logger.debug(String.format("WS Version 8 Server Handshake key: %s. Response: %s.", key, accept));
} }
res.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS); res.headers().add(Names.UPGRADE, WEBSOCKET.toLowerCase());
res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase()); res.headers().add(Names.CONNECTION, Names.UPGRADE);
res.addHeader(Names.CONNECTION, Names.UPGRADE); res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept);
res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept); String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
String subprotocols = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
if (subprotocols != null) { if (subprotocols != null) {
String selectedSubprotocol = selectSubprotocol(subprotocols); String selectedSubprotocol = selectSubprotocol(subprotocols);
if (selectedSubprotocol == null) { if (selectedSubprotocol == null) {
throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols); throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols);
} else { } else {
res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol); res.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
setSelectedSubprotocol(selectedSubprotocol); setSelectedSubprotocol(selectedSubprotocol);
} }
} }

View File

@ -20,12 +20,12 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.DefaultHttpResponseHeader; import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseHeader; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.logging.InternalLogger; import io.netty.logging.InternalLogger;
@ -109,15 +109,15 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
* HTTP request * HTTP request
*/ */
@Override @Override
public ChannelFuture handshake(Channel channel, HttpRequest req, ChannelPromise promise) { public ChannelFuture handshake(Channel channel, FullHttpRequest req, ChannelPromise promise) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug(String.format("Channel %s WS Version 13 server handshake", channel.id())); logger.debug(String.format("Channel %s WS Version 13 server handshake", channel.id()));
} }
HttpResponseHeader res = new DefaultHttpResponseHeader(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS); HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS);
String key = req.getHeader(Names.SEC_WEBSOCKET_KEY); String key = req.headers().get(Names.SEC_WEBSOCKET_KEY);
if (key == null) { if (key == null) {
throw new WebSocketHandshakeException("not a WebSocket request: missing key"); throw new WebSocketHandshakeException("not a WebSocket request: missing key");
} }
@ -129,18 +129,17 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
logger.debug(String.format("WS Version 13 Server Handshake key: %s. Response: %s.", key, accept)); logger.debug(String.format("WS Version 13 Server Handshake key: %s. Response: %s.", key, accept));
} }
res.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS); res.headers().add(Names.UPGRADE, WEBSOCKET.toLowerCase());
res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase()); res.headers().add(Names.CONNECTION, Names.UPGRADE);
res.addHeader(Names.CONNECTION, Names.UPGRADE); res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept);
res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept); String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
String subprotocols = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
if (subprotocols != null) { if (subprotocols != null) {
String selectedSubprotocol = selectSubprotocol(subprotocols); String selectedSubprotocol = selectSubprotocol(subprotocols);
if (selectedSubprotocol == null) { if (selectedSubprotocol == null) {
throw new WebSocketHandshakeException( throw new WebSocketHandshakeException(
"Requested subprotocol(s) not supported: " + subprotocols); "Requested subprotocol(s) not supported: " + subprotocols);
} else { } else {
res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol); res.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
setSelectedSubprotocol(selectedSubprotocol); setSelectedSubprotocol(selectedSubprotocol);
} }
} }

View File

@ -16,10 +16,10 @@
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.handler.codec.http.DefaultHttpResponseHeader; import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.HttpRequestHeader; import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseHeader; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
@ -81,9 +81,9 @@ public class WebSocketServerHandshakerFactory {
* @return A new WebSocketServerHandshaker for the requested web socket version. Null if web * @return A new WebSocketServerHandshaker for the requested web socket version. Null if web
* socket version is not supported. * socket version is not supported.
*/ */
public WebSocketServerHandshaker newHandshaker(HttpRequestHeader req) { public WebSocketServerHandshaker newHandshaker(HttpRequest req) {
String version = req.getHeader(Names.SEC_WEBSOCKET_VERSION); String version = req.headers().get(Names.SEC_WEBSOCKET_VERSION);
if (version != null) { if (version != null) {
if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) { if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) {
// Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification). // Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification).
@ -113,11 +113,10 @@ public class WebSocketServerHandshakerFactory {
* Channel * Channel
*/ */
public static void sendUnsupportedWebSocketVersionResponse(Channel channel) { public static void sendUnsupportedWebSocketVersionResponse(Channel channel) {
HttpResponseHeader res = new DefaultHttpResponseHeader( HttpResponse res = new DefaultHttpResponse(
HttpVersion.HTTP_1_1, HttpVersion.HTTP_1_1,
HttpResponseStatus.SWITCHING_PROTOCOLS); HttpResponseStatus.UPGRADE_REQUIRED);
res.setStatus(HttpResponseStatus.UPGRADE_REQUIRED); res.headers().set(Names.SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue());
res.setHeader(Names.SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue());
channel.write(res); channel.write(res);
} }
} }

View File

@ -21,8 +21,8 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter; import io.netty.channel.ChannelInboundMessageHandlerAdapter;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
@ -92,8 +92,8 @@ public class WebSocketServerProtocolHandler extends ChannelInboundMessageHandler
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (cause instanceof WebSocketHandshakeException) { if (cause instanceof WebSocketHandshakeException) {
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.BAD_REQUEST); FullHttpResponse response = new DefaultFullHttpResponse(
response.setContent(Unpooled.wrappedBuffer(cause.getMessage().getBytes())); HTTP_1_1, HttpResponseStatus.BAD_REQUEST, Unpooled.wrappedBuffer(cause.getMessage().getBytes()));
ctx.channel().write(response).addListener(ChannelFutureListener.CLOSE); ctx.channel().write(response).addListener(ChannelFutureListener.CLOSE);
} else { } else {
ctx.close(); ctx.close();
@ -113,7 +113,8 @@ public class WebSocketServerProtocolHandler extends ChannelInboundMessageHandler
@Override @Override
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
if (!(msg instanceof WebSocketFrame)) { if (!(msg instanceof WebSocketFrame)) {
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.FORBIDDEN); FullHttpResponse response =
new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.FORBIDDEN);
ctx.channel().write(response); ctx.channel().write(response);
} else { } else {
ctx.nextInboundMessageBuffer().add(msg); ctx.nextInboundMessageBuffer().add(msg);

View File

@ -15,26 +15,28 @@
*/ */
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import static io.netty.handler.codec.http.HttpHeaders.isKeepAlive;
import static io.netty.handler.codec.http.HttpMethod.GET;
import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter; import io.netty.channel.ChannelInboundMessageHandlerAdapter;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpRequestHeader; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseHeader;
import io.netty.handler.ssl.SslHandler; import io.netty.handler.ssl.SslHandler;
import static io.netty.handler.codec.http.HttpHeaders.*;
import static io.netty.handler.codec.http.HttpMethod.*;
import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static io.netty.handler.codec.http.HttpVersion.*;
/** /**
* Handles the HTTP handshake (the HTTP Upgrade request) for {@link WebSocketServerProtocolHandler}. * Handles the HTTP handshake (the HTTP Upgrade request) for {@link WebSocketServerProtocolHandler}.
*/ */
public class WebSocketServerProtocolHandshakeHandler extends ChannelInboundMessageHandlerAdapter<HttpRequest> { public class WebSocketServerProtocolHandshakeHandler
extends ChannelInboundMessageHandlerAdapter<FullHttpRequest> {
private final String websocketPath; private final String websocketPath;
private final String subprotocols; private final String subprotocols;
@ -48,9 +50,9 @@ public class WebSocketServerProtocolHandshakeHandler extends ChannelInboundMessa
} }
@Override @Override
public void messageReceived(final ChannelHandlerContext ctx, HttpRequest req) throws Exception { public void messageReceived(final ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
if (req.getMethod() != GET) { if (req.method() != GET) {
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN)); sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
return; return;
} }
@ -75,20 +77,20 @@ public class WebSocketServerProtocolHandshakeHandler extends ChannelInboundMessa
} }
} }
private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequestHeader req, HttpResponseHeader res) { private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) {
ChannelFuture f = ctx.channel().write(res); ChannelFuture f = ctx.channel().write(res);
if (!isKeepAlive(req) || res.getStatus().getCode() != 200) { if (!isKeepAlive(req) || res.status().code() != 200) {
f.addListener(ChannelFutureListener.CLOSE); f.addListener(ChannelFutureListener.CLOSE);
} }
} }
private static String getWebSocketLocation(ChannelPipeline cp, HttpRequestHeader req, String path) { private static String getWebSocketLocation(ChannelPipeline cp, HttpRequest req, String path) {
String protocol = "ws"; String protocol = "ws";
if (cp.get(SslHandler.class) != null) { if (cp.get(SslHandler.class) != null) {
// SSL in use so use Secure WebSockets // SSL in use so use Secure WebSockets
protocol = "wss"; protocol = "wss";
} }
return protocol + "://" + req.getHeader(HttpHeaders.Names.HOST) + path; return protocol + "://" + req.headers().get(HttpHeaders.Names.HOST) + path;
} }
} }

View File

@ -19,13 +19,13 @@ import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.embedded.EmbeddedMessageChannel; import io.netty.channel.embedded.EmbeddedMessageChannel;
import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.TooLongFrameException;
import io.netty.handler.codec.http.HttpHeader; import io.netty.handler.codec.http.HttpMessage;
import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpObjectDecoder; import io.netty.handler.codec.http.HttpObjectDecoder;
/** /**
* Decodes {@link ByteBuf}s into RTSP messages represented in * Decodes {@link ByteBuf}s into RTSP messages represented in
* {@link io.netty.handler.codec.http.HttpHeader}s. * {@link io.netty.handler.codec.http.HttpMessage}s.
* <p> * <p>
* <h3>Parameters that prevents excessive memory consumption</h3> * <h3>Parameters that prevents excessive memory consumption</h3>
* <table border="1"> * <table border="1">
@ -84,14 +84,14 @@ public abstract class RtspObjectDecoder extends HttpObjectDecoder {
} }
@Override @Override
protected boolean isContentAlwaysEmpty(HttpHeader msg) { protected boolean isContentAlwaysEmpty(HttpMessage msg) {
// Unlike HTTP, RTSP always assumes zero-length body if Content-Length // Unlike HTTP, RTSP always assumes zero-length body if Content-Length
// header is absent. // header is absent.
boolean empty = super.isContentAlwaysEmpty(msg); boolean empty = super.isContentAlwaysEmpty(msg);
if (empty) { if (empty) {
return true; return true;
} }
if (!msg.containsHeader(RtspHeaders.Names.CONTENT_LENGTH)) { if (!msg.headers().contains(RtspHeaders.Names.CONTENT_LENGTH)) {
return true; return true;
} }
return empty; return empty;

View File

@ -17,20 +17,19 @@ package io.netty.handler.codec.rtsp;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.FullHttpMessage;
import io.netty.handler.codec.UnsupportedMessageTypeException; import io.netty.handler.codec.http.HttpMessage;
import io.netty.handler.codec.http.HttpHeader;
import io.netty.handler.codec.http.HttpObjectEncoder; import io.netty.handler.codec.http.HttpObjectEncoder;
/** /**
* Encodes an RTSP message represented in {@link HttpHeader} into * Encodes an RTSP message represented in {@link FullHttpMessage} into
* a {@link ByteBuf}. * a {@link ByteBuf}.
* *
* @apiviz.landmark * @apiviz.landmark
*/ */
@Sharable @Sharable
public abstract class RtspObjectEncoder<H extends HttpHeader> extends HttpObjectEncoder<H> { public abstract class RtspObjectEncoder<H extends HttpMessage> extends HttpObjectEncoder<H> {
/** /**
* Creates a new instance. * Creates a new instance.
@ -39,13 +38,7 @@ public abstract class RtspObjectEncoder<H extends HttpHeader> extends HttpObject
} }
@Override @Override
protected void encode(ChannelHandlerContext ctx, Object msg, public boolean isEncodable(Object msg) throws Exception {
ByteBuf out) throws Exception { return msg instanceof FullHttpMessage;
// Ignore unrelated message types such as HttpChunk.
if (!(msg instanceof HttpHeader)) {
throw new UnsupportedMessageTypeException(msg, HttpHeader.class);
}
super.encode(ctx, msg, out);
} }
} }

View File

@ -17,12 +17,12 @@ package io.netty.handler.codec.rtsp;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.TooLongFrameException;
import io.netty.handler.codec.http.DefaultHttpRequestHeader; import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.HttpHeader; import io.netty.handler.codec.http.HttpMessage;
/** /**
* Decodes {@link ByteBuf}s into RTSP requests represented in * Decodes {@link ByteBuf}s into RTSP requests represented in
* {@link io.netty.handler.codec.http.HttpRequestHeader}s. * {@link io.netty.handler.codec.http.HttpRequest}s.
* <p> * <p>
* <h3>Parameters that prevents excessive memory consumption</h3> * <h3>Parameters that prevents excessive memory consumption</h3>
* <table border="1"> * <table border="1">
@ -65,14 +65,14 @@ public class RtspRequestDecoder extends RtspObjectDecoder {
} }
@Override @Override
protected HttpHeader createMessage(String[] initialLine) throws Exception { protected HttpMessage createMessage(String[] initialLine) throws Exception {
return new DefaultHttpRequestHeader(RtspVersions.valueOf(initialLine[2]), return new DefaultHttpRequest(RtspVersions.valueOf(initialLine[2]),
RtspMethods.valueOf(initialLine[0]), initialLine[1]); RtspMethods.valueOf(initialLine[0]), initialLine[1]);
} }
@Override @Override
protected HttpHeader createInvalidMessage() { protected HttpMessage createInvalidMessage() {
return new DefaultHttpRequestHeader(RtspVersions.RTSP_1_0, RtspMethods.OPTIONS, "/bad-request"); return new DefaultHttpRequest(RtspVersions.RTSP_1_0, RtspMethods.OPTIONS, "/bad-request");
} }
@Override @Override

View File

@ -16,24 +16,30 @@
package io.netty.handler.codec.rtsp; package io.netty.handler.codec.rtsp;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.http.HttpRequestHeader; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
/** /**
* Encodes an RTSP request represented in {@link HttpRequestHeader} into * Encodes an RTSP request represented in {@link FullHttpRequest} into
* a {@link ByteBuf}. * a {@link ByteBuf}.
*/ */
public class RtspRequestEncoder extends RtspObjectEncoder<HttpRequestHeader> { public class RtspRequestEncoder extends RtspObjectEncoder<HttpRequest> {
@Override @Override
protected void encodeInitialLine(ByteBuf buf, HttpRequestHeader request) public boolean isEncodable(Object msg) throws Exception {
return msg instanceof FullHttpRequest;
}
@Override
protected void encodeInitialLine(ByteBuf buf, HttpRequest request)
throws Exception { throws Exception {
buf.writeBytes(request.getMethod().toString().getBytes(CharsetUtil.US_ASCII)); buf.writeBytes(request.method().toString().getBytes(CharsetUtil.US_ASCII));
buf.writeByte((byte) ' '); buf.writeByte((byte) ' ');
buf.writeBytes(request.getUri().getBytes(CharsetUtil.UTF_8)); buf.writeBytes(request.uri().getBytes(CharsetUtil.UTF_8));
buf.writeByte((byte) ' '); buf.writeByte((byte) ' ');
buf.writeBytes(request.getProtocolVersion().toString().getBytes(CharsetUtil.US_ASCII)); buf.writeBytes(request.protocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
buf.writeByte((byte) '\r'); buf.writeByte((byte) '\r');
buf.writeByte((byte) '\n'); buf.writeByte((byte) '\n');
} }

View File

@ -17,13 +17,13 @@ package io.netty.handler.codec.rtsp;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.TooLongFrameException;
import io.netty.handler.codec.http.DefaultHttpResponseHeader; import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.HttpHeader; import io.netty.handler.codec.http.HttpMessage;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
/** /**
* Decodes {@link ByteBuf}s into RTSP responses represented in * Decodes {@link ByteBuf}s into RTSP responses represented in
* {@link io.netty.handler.codec.http.HttpResponseHeader}s. * {@link io.netty.handler.codec.http.HttpResponse}s.
* <p> * <p>
* <h3>Parameters that prevents excessive memory consumption</h3> * <h3>Parameters that prevents excessive memory consumption</h3>
* <table border="1"> * <table border="1">
@ -69,15 +69,15 @@ public class RtspResponseDecoder extends RtspObjectDecoder {
} }
@Override @Override
protected HttpHeader createMessage(String[] initialLine) throws Exception { protected HttpMessage createMessage(String[] initialLine) throws Exception {
return new DefaultHttpResponseHeader( return new DefaultHttpResponse(
RtspVersions.valueOf(initialLine[0]), RtspVersions.valueOf(initialLine[0]),
new HttpResponseStatus(Integer.valueOf(initialLine[1]), initialLine[2])); new HttpResponseStatus(Integer.valueOf(initialLine[1]), initialLine[2]));
} }
@Override @Override
protected HttpHeader createInvalidMessage() { protected HttpMessage createInvalidMessage() {
return new DefaultHttpResponseHeader(RtspVersions.RTSP_1_0, UNKNOWN_STATUS); return new DefaultHttpResponse(RtspVersions.RTSP_1_0, UNKNOWN_STATUS);
} }
@Override @Override

View File

@ -16,25 +16,30 @@
package io.netty.handler.codec.rtsp; package io.netty.handler.codec.rtsp;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.http.HttpHeader; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpResponseHeader; import io.netty.handler.codec.http.HttpResponse;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
/** /**
* Encodes an RTSP response represented in {@link HttpResponseHeader} into * Encodes an RTSP response represented in {@link FullHttpResponse} into
* a {@link ByteBuf}. * a {@link ByteBuf}.
*/ */
public class RtspResponseEncoder extends RtspObjectEncoder<HttpResponseHeader> { public class RtspResponseEncoder extends RtspObjectEncoder<HttpResponse> {
@Override @Override
protected void encodeInitialLine(ByteBuf buf, HttpResponseHeader response) public boolean isEncodable(Object msg) throws Exception {
return msg instanceof FullHttpResponse;
}
@Override
protected void encodeInitialLine(ByteBuf buf, HttpResponse response)
throws Exception { throws Exception {
buf.writeBytes(response.getProtocolVersion().toString().getBytes(CharsetUtil.US_ASCII)); buf.writeBytes(response.protocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
buf.writeByte((byte) ' '); buf.writeByte((byte) ' ');
buf.writeBytes(String.valueOf(response.getStatus().getCode()).getBytes(CharsetUtil.US_ASCII)); buf.writeBytes(String.valueOf(response.status().code()).getBytes(CharsetUtil.US_ASCII));
buf.writeByte((byte) ' '); buf.writeByte((byte) ' ');
buf.writeBytes(String.valueOf(response.getStatus().getReasonPhrase()).getBytes(CharsetUtil.US_ASCII)); buf.writeBytes(String.valueOf(response.status().reasonPhrase()).getBytes(CharsetUtil.US_ASCII));
buf.writeByte((byte) '\r'); buf.writeByte((byte) '\r');
buf.writeByte((byte) '\n'); buf.writeByte((byte) '\n');
} }

View File

@ -196,9 +196,9 @@ public class SpdyHeaders {
*/ */
public static void setMethod(int spdyVersion, SpdyHeaderBlock block, HttpMethod method) { public static void setMethod(int spdyVersion, SpdyHeaderBlock block, HttpMethod method) {
if (spdyVersion < 3) { if (spdyVersion < 3) {
block.setHeader(Spdy2HttpNames.METHOD, method.getName()); block.setHeader(Spdy2HttpNames.METHOD, method.name());
} else { } else {
block.setHeader(HttpNames.METHOD, method.getName()); block.setHeader(HttpNames.METHOD, method.name());
} }
} }
@ -264,7 +264,7 @@ public class SpdyHeaders {
int code = Integer.parseInt(status.substring(0, space)); int code = Integer.parseInt(status.substring(0, space));
String reasonPhrase = status.substring(space + 1); String reasonPhrase = status.substring(space + 1);
HttpResponseStatus responseStatus = HttpResponseStatus.valueOf(code); HttpResponseStatus responseStatus = HttpResponseStatus.valueOf(code);
if (responseStatus.getReasonPhrase().equals(reasonPhrase)) { if (responseStatus.reasonPhrase().equals(reasonPhrase)) {
return responseStatus; return responseStatus;
} else { } else {
return new HttpResponseStatus(code, reasonPhrase); return new HttpResponseStatus(code, reasonPhrase);

View File

@ -16,18 +16,17 @@
package io.netty.handler.codec.spdy; package io.netty.handler.codec.spdy;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.TooLongFrameException;
import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpHeader; import io.netty.handler.codec.http.FullHttpMessage;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMessage; import io.netty.handler.codec.http.HttpMessage;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
@ -36,13 +35,13 @@ import java.util.Map;
/** /**
* Decodes {@link SpdySynStreamFrame}s, {@link SpdySynReplyFrame}s, * Decodes {@link SpdySynStreamFrame}s, {@link SpdySynReplyFrame}s,
* and {@link SpdyDataFrame}s into {@link HttpRequest}s and {@link HttpResponse}s. * and {@link SpdyDataFrame}s into {@link FullHttpRequest}s and {@link FullHttpResponse}s.
*/ */
public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> { public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
private final int spdyVersion; private final int spdyVersion;
private final int maxContentLength; private final int maxContentLength;
private final Map<Integer, HttpMessage> messageMap = new HashMap<Integer, HttpMessage>(); private final Map<Integer, FullHttpMessage> messageMap = new HashMap<Integer, FullHttpMessage>();
/** /**
* Creates a new instance. * Creates a new instance.
@ -98,20 +97,21 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
} }
try { try {
HttpResponse httpResponse = createHttpResponse(spdyVersion, spdySynStreamFrame); FullHttpResponse httpResponseWithEntity =
createHttpResponse(spdyVersion, spdySynStreamFrame);
// Set the Stream-ID, Associated-To-Stream-ID, Priority, and URL as headers // Set the Stream-ID, Associated-To-Stream-ID, Priority, and URL as headers
SpdyHttpHeaders.setStreamId(httpResponse, streamID); SpdyHttpHeaders.setStreamId(httpResponseWithEntity, streamID);
SpdyHttpHeaders.setAssociatedToStreamId(httpResponse, associatedToStreamId); SpdyHttpHeaders.setAssociatedToStreamId(httpResponseWithEntity, associatedToStreamId);
SpdyHttpHeaders.setPriority(httpResponse, spdySynStreamFrame.getPriority()); SpdyHttpHeaders.setPriority(httpResponseWithEntity, spdySynStreamFrame.getPriority());
SpdyHttpHeaders.setUrl(httpResponse, URL); SpdyHttpHeaders.setUrl(httpResponseWithEntity, URL);
if (spdySynStreamFrame.isLast()) { if (spdySynStreamFrame.isLast()) {
HttpHeaders.setContentLength(httpResponse, 0); HttpHeaders.setContentLength(httpResponseWithEntity, 0);
return httpResponse; return httpResponseWithEntity;
} else { } else {
// Response body will follow in a series of Data Frames // Response body will follow in a series of Data Frames
messageMap.put(Integer.valueOf(streamID), httpResponse); messageMap.put(Integer.valueOf(streamID), httpResponseWithEntity);
} }
} catch (Exception e) { } catch (Exception e) {
SpdyRstStreamFrame spdyRstStreamFrame = SpdyRstStreamFrame spdyRstStreamFrame =
@ -121,16 +121,16 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
} else { } else {
// SYN_STREAM frames initiated by the client are HTTP requests // SYN_STREAM frames initiated by the client are HTTP requests
try { try {
HttpRequest httpRequest = createHttpRequest(spdyVersion, spdySynStreamFrame); FullHttpRequest httpRequestWithEntity = createHttpRequest(spdyVersion, spdySynStreamFrame);
// Set the Stream-ID as a header // Set the Stream-ID as a header
SpdyHttpHeaders.setStreamId(httpRequest, streamID); SpdyHttpHeaders.setStreamId(httpRequestWithEntity, streamID);
if (spdySynStreamFrame.isLast()) { if (spdySynStreamFrame.isLast()) {
return httpRequest; return httpRequestWithEntity;
} else { } else {
// Request body will follow in a series of Data Frames // Request body will follow in a series of Data Frames
messageMap.put(Integer.valueOf(streamID), httpRequest); messageMap.put(Integer.valueOf(streamID), httpRequestWithEntity);
} }
} catch (Exception e) { } catch (Exception e) {
// If a client sends a SYN_STREAM without all of the method, url (host and path), // If a client sends a SYN_STREAM without all of the method, url (host and path),
@ -150,17 +150,17 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
int streamID = spdySynReplyFrame.getStreamId(); int streamID = spdySynReplyFrame.getStreamId();
try { try {
HttpResponse httpResponse = createHttpResponse(spdyVersion, spdySynReplyFrame); FullHttpResponse httpResponseWithEntity = createHttpResponse(spdyVersion, spdySynReplyFrame);
// Set the Stream-ID as a header // Set the Stream-ID as a header
SpdyHttpHeaders.setStreamId(httpResponse, streamID); SpdyHttpHeaders.setStreamId(httpResponseWithEntity, streamID);
if (spdySynReplyFrame.isLast()) { if (spdySynReplyFrame.isLast()) {
HttpHeaders.setContentLength(httpResponse, 0); HttpHeaders.setContentLength(httpResponseWithEntity, 0);
return httpResponse; return httpResponseWithEntity;
} else { } else {
// Response body will follow in a series of Data Frames // Response body will follow in a series of Data Frames
messageMap.put(Integer.valueOf(streamID), httpResponse); messageMap.put(Integer.valueOf(streamID), httpResponseWithEntity);
} }
} catch (Exception e) { } catch (Exception e) {
// If a client receives a SYN_REPLY without valid status and version headers // If a client receives a SYN_REPLY without valid status and version headers
@ -174,7 +174,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg; SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
Integer streamID = Integer.valueOf(spdyHeadersFrame.getStreamId()); Integer streamID = Integer.valueOf(spdyHeadersFrame.getStreamId());
HttpHeader httpMessage = messageMap.get(streamID); HttpMessage httpMessage = messageMap.get(streamID);
// If message is not in map discard HEADERS frame. // If message is not in map discard HEADERS frame.
// SpdySessionHandler should prevent this from happening. // SpdySessionHandler should prevent this from happening.
@ -183,22 +183,22 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
} }
for (Map.Entry<String, String> e: spdyHeadersFrame.getHeaders()) { for (Map.Entry<String, String> e: spdyHeadersFrame.getHeaders()) {
httpMessage.addHeader(e.getKey(), e.getValue()); httpMessage.headers().add(e.getKey(), e.getValue());
} }
} else if (msg instanceof SpdyDataFrame) { } else if (msg instanceof SpdyDataFrame) {
SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg; SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
Integer streamID = Integer.valueOf(spdyDataFrame.getStreamId()); Integer streamID = Integer.valueOf(spdyDataFrame.getStreamId());
HttpMessage httpMessage = messageMap.get(streamID); FullHttpMessage fullHttpMessage = messageMap.get(streamID);
// If message is not in map discard Data Frame. // If message is not in map discard Data Frame.
// SpdySessionHandler should prevent this from happening. // SpdySessionHandler should prevent this from happening.
if (httpMessage == null) { if (fullHttpMessage == null) {
return null; return null;
} }
ByteBuf content = httpMessage.getContent(); ByteBuf content = fullHttpMessage.data();
if (content.readableBytes() > maxContentLength - spdyDataFrame.getData().readableBytes()) { if (content.readableBytes() > maxContentLength - spdyDataFrame.getData().readableBytes()) {
messageMap.remove(streamID); messageMap.remove(streamID);
throw new TooLongFrameException( throw new TooLongFrameException(
@ -207,18 +207,12 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
ByteBuf spdyDataFrameData = spdyDataFrame.getData(); ByteBuf spdyDataFrameData = spdyDataFrame.getData();
int spdyDataFrameDataLen = spdyDataFrameData.readableBytes(); int spdyDataFrameDataLen = spdyDataFrameData.readableBytes();
if (content == Unpooled.EMPTY_BUFFER) { content.writeBytes(spdyDataFrameData, spdyDataFrameData.readerIndex(), spdyDataFrameDataLen);
content = Unpooled.buffer(spdyDataFrameDataLen);
content.writeBytes(spdyDataFrameData, spdyDataFrameData.readerIndex(), spdyDataFrameDataLen);
httpMessage.setContent(content);
} else {
content.writeBytes(spdyDataFrameData, spdyDataFrameData.readerIndex(), spdyDataFrameDataLen);
}
if (spdyDataFrame.isLast()) { if (spdyDataFrame.isLast()) {
HttpHeaders.setContentLength(httpMessage, content.readableBytes()); HttpHeaders.setContentLength(fullHttpMessage, content.readableBytes());
messageMap.remove(streamID); messageMap.remove(streamID);
return httpMessage; return fullHttpMessage;
} }
} else if (msg instanceof SpdyRstStreamFrame) { } else if (msg instanceof SpdyRstStreamFrame) {
@ -231,7 +225,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
return null; return null;
} }
private static HttpRequest createHttpRequest(int spdyVersion, SpdyHeaderBlock requestFrame) private static FullHttpRequest createHttpRequest(int spdyVersion, SpdyHeaderBlock requestFrame)
throws Exception { throws Exception {
// Create the first line of the request from the name/value pairs // Create the first line of the request from the name/value pairs
HttpMethod method = SpdyHeaders.getMethod(spdyVersion, requestFrame); HttpMethod method = SpdyHeaders.getMethod(spdyVersion, requestFrame);
@ -241,7 +235,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
SpdyHeaders.removeUrl(spdyVersion, requestFrame); SpdyHeaders.removeUrl(spdyVersion, requestFrame);
SpdyHeaders.removeVersion(spdyVersion, requestFrame); SpdyHeaders.removeVersion(spdyVersion, requestFrame);
HttpRequest httpRequest = new DefaultHttpRequest(httpVersion, method, url); FullHttpRequest httpRequestWithEntity = new DefaultFullHttpRequest(httpVersion, method, url);
// Remove the scheme header // Remove the scheme header
SpdyHeaders.removeScheme(spdyVersion, requestFrame); SpdyHeaders.removeScheme(spdyVersion, requestFrame);
@ -250,23 +244,23 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
// Replace the SPDY host header with the HTTP host header // Replace the SPDY host header with the HTTP host header
String host = SpdyHeaders.getHost(requestFrame); String host = SpdyHeaders.getHost(requestFrame);
SpdyHeaders.removeHost(requestFrame); SpdyHeaders.removeHost(requestFrame);
HttpHeaders.setHost(httpRequest, host); HttpHeaders.setHost(httpRequestWithEntity, host);
} }
for (Map.Entry<String, String> e: requestFrame.getHeaders()) { for (Map.Entry<String, String> e: requestFrame.getHeaders()) {
httpRequest.addHeader(e.getKey(), e.getValue()); httpRequestWithEntity.headers().add(e.getKey(), e.getValue());
} }
// The Connection and Keep-Alive headers are no longer valid // The Connection and Keep-Alive headers are no longer valid
HttpHeaders.setKeepAlive(httpRequest, true); HttpHeaders.setKeepAlive(httpRequestWithEntity, true);
// Transfer-Encoding header is not valid // Transfer-Encoding header is not valid
httpRequest.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING); httpRequestWithEntity.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
return httpRequest; return httpRequestWithEntity;
} }
private static HttpResponse createHttpResponse(int spdyVersion, SpdyHeaderBlock responseFrame) private static FullHttpResponse createHttpResponse(int spdyVersion, SpdyHeaderBlock responseFrame)
throws Exception { throws Exception {
// Create the first line of the response from the name/value pairs // Create the first line of the response from the name/value pairs
HttpResponseStatus status = SpdyHeaders.getStatus(spdyVersion, responseFrame); HttpResponseStatus status = SpdyHeaders.getStatus(spdyVersion, responseFrame);
@ -274,18 +268,18 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
SpdyHeaders.removeStatus(spdyVersion, responseFrame); SpdyHeaders.removeStatus(spdyVersion, responseFrame);
SpdyHeaders.removeVersion(spdyVersion, responseFrame); SpdyHeaders.removeVersion(spdyVersion, responseFrame);
HttpResponse httpResponse = new DefaultHttpResponse(version, status); FullHttpResponse httpResponseWithEntity = new DefaultFullHttpResponse(version, status);
for (Map.Entry<String, String> e: responseFrame.getHeaders()) { for (Map.Entry<String, String> e: responseFrame.getHeaders()) {
httpResponse.addHeader(e.getKey(), e.getValue()); httpResponseWithEntity.headers().add(e.getKey(), e.getValue());
} }
// The Connection and Keep-Alive headers are no longer valid // The Connection and Keep-Alive headers are no longer valid
HttpHeaders.setKeepAlive(httpResponse, true); HttpHeaders.setKeepAlive(httpResponseWithEntity, true);
// Transfer-Encoding header is not valid // Transfer-Encoding header is not valid
httpResponse.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING); httpResponseWithEntity.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
httpResponse.removeHeader(HttpHeaders.Names.TRAILER); httpResponseWithEntity.headers().remove(HttpHeaders.Names.TRAILER);
return httpResponse; return httpResponseWithEntity;
} }
} }

View File

@ -18,26 +18,26 @@ package io.netty.handler.codec.spdy;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.handler.codec.UnsupportedMessageTypeException; import io.netty.handler.codec.UnsupportedMessageTypeException;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeader;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpMessage;
import io.netty.handler.codec.http.HttpRequestHeader;
import io.netty.handler.codec.http.HttpResponseHeader;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.LastHttpContent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* Encodes {@link HttpRequestHeader}s, {@link HttpResponseHeader}s, and {@link HttpContent}s * Encodes {@link HttpRequest}s, {@link HttpResponse}s, and {@link HttpContent}s
* into {@link SpdySynStreamFrame}s and {@link SpdySynReplyFrame}s. * into {@link SpdySynStreamFrame}s and {@link SpdySynReplyFrame}s.
* *
* <h3>Request Annotations</h3> * <h3>Request Annotations</h3>
* *
* SPDY specific headers must be added to {@link HttpRequestHeader}s: * SPDY specific headers must be added to {@link HttpRequest}s:
* <table border=1> * <table border=1>
* <tr> * <tr>
* <th>Header Name</th><th>Header Value</th> * <th>Header Name</th><th>Header Value</th>
@ -58,7 +58,7 @@ import java.util.Map;
* *
* <h3>Response Annotations</h3> * <h3>Response Annotations</h3>
* *
* SPDY specific headers must be added to {@link HttpResponseHeader}s: * SPDY specific headers must be added to {@link HttpResponse}s:
* <table border=1> * <table border=1>
* <tr> * <tr>
* <th>Header Name</th><th>Header Value</th> * <th>Header Name</th><th>Header Value</th>
@ -71,7 +71,7 @@ import java.util.Map;
* *
* <h3>Pushed Resource Annotations</h3> * <h3>Pushed Resource Annotations</h3>
* *
* SPDY specific headers must be added to pushed {@link HttpResponseHeader}s: * SPDY specific headers must be added to pushed {@link HttpResponse}s:
* <table border=1> * <table border=1>
* <tr> * <tr>
* <th>Header Name</th><th>Header Value</th> * <th>Header Name</th><th>Header Value</th>
@ -112,8 +112,8 @@ import java.util.Map;
* <h3>Chunked Content</h3> * <h3>Chunked Content</h3>
* *
* This encoder associates all {@link HttpContent}s that it receives * This encoder associates all {@link HttpContent}s that it receives
* with the most recently received 'chunked' {@link HttpRequestHeader} * with the most recently received 'chunked' {@link HttpRequest}
* or {@link HttpResponseHeader}. * or {@link HttpResponse}.
* *
* <h3>Pushed Resources</h3> * <h3>Pushed Resources</h3>
* *
@ -144,16 +144,16 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
public Object encode(ChannelHandlerContext ctx, Object msg) throws Exception { public Object encode(ChannelHandlerContext ctx, Object msg) throws Exception {
List<Object> out = new ArrayList<Object>(); List<Object> out = new ArrayList<Object>();
if (msg instanceof HttpRequestHeader) { if (msg instanceof HttpRequest) {
HttpRequestHeader httpRequest = (HttpRequestHeader) msg; HttpRequest httpRequest = (HttpRequest) msg;
SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpRequest); SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpRequest);
out.add(spdySynStreamFrame); out.add(spdySynStreamFrame);
} }
if (msg instanceof HttpResponseHeader) { if (msg instanceof HttpResponse) {
HttpResponseHeader httpResponse = (HttpResponseHeader) msg; HttpResponse httpResponse = (HttpResponse) msg;
if (httpResponse.containsHeader(SpdyHttpHeaders.Names.ASSOCIATED_TO_STREAM_ID)) { if (httpResponse.headers().contains(SpdyHttpHeaders.Names.ASSOCIATED_TO_STREAM_ID)) {
SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpResponse); SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpResponse);
out.add(spdySynStreamFrame); out.add(spdySynStreamFrame);
} else { } else {
@ -165,12 +165,12 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
HttpContent chunk = (HttpContent) msg; HttpContent chunk = (HttpContent) msg;
SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(currentStreamId); SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(currentStreamId);
spdyDataFrame.setData(chunk.getContent()); spdyDataFrame.setData(chunk.data());
spdyDataFrame.setLast(chunk instanceof LastHttpContent); spdyDataFrame.setLast(chunk instanceof LastHttpContent);
if (chunk instanceof LastHttpContent) { if (chunk instanceof LastHttpContent) {
LastHttpContent trailer = (LastHttpContent) chunk; LastHttpContent trailer = (LastHttpContent) chunk;
List<Map.Entry<String, String>> trailers = trailer.getHeaders(); List<Map.Entry<String, String>> trailers = trailer.trailingHeaders().entries();
if (trailers.isEmpty()) { if (trailers.isEmpty()) {
out.add(spdyDataFrame); out.add(spdyDataFrame);
} else { } else {
@ -194,7 +194,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
return out.toArray(); return out.toArray();
} }
private SpdySynStreamFrame createSynStreamFrame(HttpHeader httpMessage) private SpdySynStreamFrame createSynStreamFrame(HttpMessage httpMessage)
throws Exception { throws Exception {
// Get the Stream-ID, Associated-To-Stream-ID, Priority, URL, and scheme from the headers // Get the Stream-ID, Associated-To-Stream-ID, Priority, URL, and scheme from the headers
int streamID = SpdyHttpHeaders.getStreamId(httpMessage); int streamID = SpdyHttpHeaders.getStreamId(httpMessage);
@ -210,33 +210,33 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
// The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding // The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding
// headers are not valid and MUST not be sent. // headers are not valid and MUST not be sent.
httpMessage.removeHeader(HttpHeaders.Names.CONNECTION); httpMessage.headers().remove(HttpHeaders.Names.CONNECTION);
httpMessage.removeHeader("Keep-Alive"); httpMessage.headers().remove("Keep-Alive");
httpMessage.removeHeader("Proxy-Connection"); httpMessage.headers().remove("Proxy-Connection");
httpMessage.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING); httpMessage.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
SpdySynStreamFrame spdySynStreamFrame = SpdySynStreamFrame spdySynStreamFrame =
new DefaultSpdySynStreamFrame(streamID, associatedToStreamId, priority); new DefaultSpdySynStreamFrame(streamID, associatedToStreamId, priority);
// Unfold the first line of the message into name/value pairs // Unfold the first line of the message into name/value pairs
if (httpMessage instanceof HttpRequest) { if (httpMessage instanceof FullHttpRequest) {
HttpRequestHeader httpRequest = (HttpRequestHeader) httpMessage; HttpRequest httpRequest = (HttpRequest) httpMessage;
SpdyHeaders.setMethod(spdyVersion, spdySynStreamFrame, httpRequest.getMethod()); SpdyHeaders.setMethod(spdyVersion, spdySynStreamFrame, httpRequest.method());
SpdyHeaders.setUrl(spdyVersion, spdySynStreamFrame, httpRequest.getUri()); SpdyHeaders.setUrl(spdyVersion, spdySynStreamFrame, httpRequest.uri());
SpdyHeaders.setVersion(spdyVersion, spdySynStreamFrame, httpMessage.getProtocolVersion()); SpdyHeaders.setVersion(spdyVersion, spdySynStreamFrame, httpMessage.protocolVersion());
} }
if (httpMessage instanceof HttpResponseHeader) { if (httpMessage instanceof HttpResponse) {
HttpResponseHeader httpResponse = (HttpResponseHeader) httpMessage; HttpResponse httpResponse = (HttpResponse) httpMessage;
SpdyHeaders.setStatus(spdyVersion, spdySynStreamFrame, httpResponse.getStatus()); SpdyHeaders.setStatus(spdyVersion, spdySynStreamFrame, httpResponse.status());
SpdyHeaders.setUrl(spdyVersion, spdySynStreamFrame, URL); SpdyHeaders.setUrl(spdyVersion, spdySynStreamFrame, URL);
SpdyHeaders.setVersion(spdyVersion, spdySynStreamFrame, httpMessage.getProtocolVersion()); SpdyHeaders.setVersion(spdyVersion, spdySynStreamFrame, httpMessage.protocolVersion());
spdySynStreamFrame.setUnidirectional(true); spdySynStreamFrame.setUnidirectional(true);
} }
// Replace the HTTP host header with the SPDY host header // Replace the HTTP host header with the SPDY host header
if (spdyVersion >= 3) { if (spdyVersion >= 3) {
String host = HttpHeaders.getHost(httpMessage); String host = HttpHeaders.getHost(httpMessage);
httpMessage.removeHeader(HttpHeaders.Names.HOST); httpMessage.headers().remove(HttpHeaders.Names.HOST);
SpdyHeaders.setHost(spdySynStreamFrame, host); SpdyHeaders.setHost(spdySynStreamFrame, host);
} }
@ -247,7 +247,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
SpdyHeaders.setScheme(spdyVersion, spdySynStreamFrame, scheme); SpdyHeaders.setScheme(spdyVersion, spdySynStreamFrame, scheme);
// Transfer the remaining HTTP headers // Transfer the remaining HTTP headers
for (Map.Entry<String, String> entry: httpMessage.getHeaders()) { for (Map.Entry<String, String> entry: httpMessage.headers()) {
spdySynStreamFrame.addHeader(entry.getKey(), entry.getValue()); spdySynStreamFrame.addHeader(entry.getKey(), entry.getValue());
} }
currentStreamId = spdySynStreamFrame.getStreamId(); currentStreamId = spdySynStreamFrame.getStreamId();
@ -255,7 +255,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
return spdySynStreamFrame; return spdySynStreamFrame;
} }
private SpdySynReplyFrame createSynReplyFrame(HttpResponseHeader httpResponse) private SpdySynReplyFrame createSynReplyFrame(HttpResponse httpResponse)
throws Exception { throws Exception {
boolean chunked = HttpHeaders.isTransferEncodingChunked(httpResponse); boolean chunked = HttpHeaders.isTransferEncodingChunked(httpResponse);
@ -265,19 +265,19 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
// The Connection, Keep-Alive, Proxy-Connection, and Transfer-ENcoding // The Connection, Keep-Alive, Proxy-Connection, and Transfer-ENcoding
// headers are not valid and MUST not be sent. // headers are not valid and MUST not be sent.
httpResponse.removeHeader(HttpHeaders.Names.CONNECTION); httpResponse.headers().remove(HttpHeaders.Names.CONNECTION);
httpResponse.removeHeader("Keep-Alive"); httpResponse.headers().remove("Keep-Alive");
httpResponse.removeHeader("Proxy-Connection"); httpResponse.headers().remove("Proxy-Connection");
httpResponse.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING); httpResponse.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID); SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);
// Unfold the first line of the response into name/value pairs // Unfold the first line of the response into name/value pairs
SpdyHeaders.setStatus(spdyVersion, spdySynReplyFrame, httpResponse.getStatus()); SpdyHeaders.setStatus(spdyVersion, spdySynReplyFrame, httpResponse.status());
SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, httpResponse.getProtocolVersion()); SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, httpResponse.protocolVersion());
// Transfer the remaining HTTP headers // Transfer the remaining HTTP headers
for (Map.Entry<String, String> entry: httpResponse.getHeaders()) { for (Map.Entry<String, String> entry: httpResponse.headers()) {
spdySynReplyFrame.addHeader(entry.getKey(), entry.getValue()); spdySynReplyFrame.addHeader(entry.getKey(), entry.getValue());
} }

View File

@ -15,8 +15,8 @@
*/ */
package io.netty.handler.codec.spdy; package io.netty.handler.codec.spdy;
import io.netty.handler.codec.http.HttpHeader;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMessage;
/** /**
* Provides the constants for the header names and the utility methods * Provides the constants for the header names and the utility methods
@ -60,29 +60,29 @@ public final class SpdyHttpHeaders {
/** /**
* Removes the {@code "X-SPDY-Stream-ID"} header. * Removes the {@code "X-SPDY-Stream-ID"} header.
*/ */
public static void removeStreamId(HttpHeader message) { public static void removeStreamId(HttpMessage message) {
message.removeHeader(Names.STREAM_ID); message.headers().remove(Names.STREAM_ID);
} }
/** /**
* Returns the value of the {@code "X-SPDY-Stream-ID"} header. * Returns the value of the {@code "X-SPDY-Stream-ID"} header.
*/ */
public static int getStreamId(HttpHeader message) { public static int getStreamId(HttpMessage message) {
return HttpHeaders.getIntHeader(message, Names.STREAM_ID); return HttpHeaders.getIntHeader(message, Names.STREAM_ID);
} }
/** /**
* Sets the {@code "X-SPDY-Stream-ID"} header. * Sets the {@code "X-SPDY-Stream-ID"} header.
*/ */
public static void setStreamId(HttpHeader message, int streamId) { public static void setStreamId(HttpMessage message, int streamId) {
HttpHeaders.setIntHeader(message, Names.STREAM_ID, streamId); HttpHeaders.setIntHeader(message, Names.STREAM_ID, streamId);
} }
/** /**
* Removes the {@code "X-SPDY-Associated-To-Stream-ID"} header. * Removes the {@code "X-SPDY-Associated-To-Stream-ID"} header.
*/ */
public static void removeAssociatedToStreamId(HttpHeader message) { public static void removeAssociatedToStreamId(HttpMessage message) {
message.removeHeader(Names.ASSOCIATED_TO_STREAM_ID); message.headers().remove(Names.ASSOCIATED_TO_STREAM_ID);
} }
/** /**
@ -91,22 +91,22 @@ public final class SpdyHttpHeaders {
* @return the header value or {@code 0} if there is no such header or * @return the header value or {@code 0} if there is no such header or
* if the header value is not a number * if the header value is not a number
*/ */
public static int getAssociatedToStreamId(HttpHeader message) { public static int getAssociatedToStreamId(HttpMessage message) {
return HttpHeaders.getIntHeader(message, Names.ASSOCIATED_TO_STREAM_ID, 0); return HttpHeaders.getIntHeader(message, Names.ASSOCIATED_TO_STREAM_ID, 0);
} }
/** /**
* Sets the {@code "X-SPDY-Associated-To-Stream-ID"} header. * Sets the {@code "X-SPDY-Associated-To-Stream-ID"} header.
*/ */
public static void setAssociatedToStreamId(HttpHeader message, int associatedToStreamId) { public static void setAssociatedToStreamId(HttpMessage message, int associatedToStreamId) {
HttpHeaders.setIntHeader(message, Names.ASSOCIATED_TO_STREAM_ID, associatedToStreamId); HttpHeaders.setIntHeader(message, Names.ASSOCIATED_TO_STREAM_ID, associatedToStreamId);
} }
/** /**
* Removes the {@code "X-SPDY-Priority"} header. * Removes the {@code "X-SPDY-Priority"} header.
*/ */
public static void removePriority(HttpHeader message) { public static void removePriority(HttpMessage message) {
message.removeHeader(Names.PRIORITY); message.headers().remove(Names.PRIORITY);
} }
/** /**
@ -115,56 +115,56 @@ public final class SpdyHttpHeaders {
* @return the header value or {@code 0} if there is no such header or * @return the header value or {@code 0} if there is no such header or
* if the header value is not a number * if the header value is not a number
*/ */
public static byte getPriority(HttpHeader message) { public static byte getPriority(HttpMessage message) {
return (byte) HttpHeaders.getIntHeader(message, Names.PRIORITY, 0); return (byte) HttpHeaders.getIntHeader(message, Names.PRIORITY, 0);
} }
/** /**
* Sets the {@code "X-SPDY-Priority"} header. * Sets the {@code "X-SPDY-Priority"} header.
*/ */
public static void setPriority(HttpHeader message, byte priority) { public static void setPriority(HttpMessage message, byte priority) {
HttpHeaders.setIntHeader(message, Names.PRIORITY, priority); HttpHeaders.setIntHeader(message, Names.PRIORITY, priority);
} }
/** /**
* Removes the {@code "X-SPDY-URL"} header. * Removes the {@code "X-SPDY-URL"} header.
*/ */
public static void removeUrl(HttpHeader message) { public static void removeUrl(HttpMessage message) {
message.removeHeader(Names.URL); message.headers().remove(Names.URL);
} }
/** /**
* Returns the value of the {@code "X-SPDY-URL"} header. * Returns the value of the {@code "X-SPDY-URL"} header.
*/ */
public static String getUrl(HttpHeader message) { public static String getUrl(HttpMessage message) {
return message.getHeader(Names.URL); return message.headers().get(Names.URL);
} }
/** /**
* Sets the {@code "X-SPDY-URL"} header. * Sets the {@code "X-SPDY-URL"} header.
*/ */
public static void setUrl(HttpHeader message, String url) { public static void setUrl(HttpMessage message, String url) {
message.setHeader(Names.URL, url); message.headers().set(Names.URL, url);
} }
/** /**
* Removes the {@code "X-SPDY-Scheme"} header. * Removes the {@code "X-SPDY-Scheme"} header.
*/ */
public static void removeScheme(HttpHeader message) { public static void removeScheme(HttpMessage message) {
message.removeHeader(Names.SCHEME); message.headers().remove(Names.SCHEME);
} }
/** /**
* Returns the value of the {@code "X-SPDY-Scheme"} header. * Returns the value of the {@code "X-SPDY-Scheme"} header.
*/ */
public static String getScheme(HttpHeader message) { public static String getScheme(HttpMessage message) {
return message.getHeader(Names.SCHEME); return message.headers().get(Names.SCHEME);
} }
/** /**
* Sets the {@code "X-SPDY-Scheme"} header. * Sets the {@code "X-SPDY-Scheme"} header.
*/ */
public static void setScheme(HttpHeader message, String scheme) { public static void setScheme(HttpMessage message, String scheme) {
message.setHeader(Names.SCHEME, scheme); message.headers().set(Names.SCHEME, scheme);
} }
} }

View File

@ -15,31 +15,31 @@
*/ */
package io.netty.handler.codec.spdy; package io.netty.handler.codec.spdy;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import io.netty.handler.codec.http.HttpMessage;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Queue; import java.util.Queue;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import io.netty.handler.codec.http.HttpHeader;
/** /**
* {@link MessageToMessageCodec} that takes care of adding the right {@link SpdyHttpHeaders.Names#STREAM_ID} to the * {@link MessageToMessageCodec} that takes care of adding the right {@link SpdyHttpHeaders.Names#STREAM_ID} to the
* {@link HttpHeader} if one is not present. This makes it possible to just re-use plan handlers current used * {@link HttpMessage} if one is not present. This makes it possible to just re-use plan handlers current used
* for HTTP. * for HTTP.
*/ */
public class SpdyHttpResponseStreamIdHandler extends public class SpdyHttpResponseStreamIdHandler extends
MessageToMessageCodec<Object, HttpHeader> { MessageToMessageCodec<Object, HttpMessage> {
private static final Integer NO_ID = -1; private static final Integer NO_ID = -1;
private final Queue<Integer> ids = new LinkedList<Integer>(); private final Queue<Integer> ids = new LinkedList<Integer>();
public SpdyHttpResponseStreamIdHandler() { public SpdyHttpResponseStreamIdHandler() {
super(new Class<?>[] { HttpHeader.class, SpdyRstStreamFrame.class }, new Class<?>[] { HttpHeader.class }); super(new Class<?>[] { HttpMessage.class, SpdyRstStreamFrame.class }, new Class<?>[] { HttpMessage.class });
} }
@Override @Override
protected Object encode(ChannelHandlerContext ctx, HttpHeader msg) throws Exception { protected Object encode(ChannelHandlerContext ctx, HttpMessage msg) throws Exception {
Integer id = ids.poll(); Integer id = ids.poll();
if (id != null && id.intValue() != NO_ID && !msg.containsHeader(SpdyHttpHeaders.Names.STREAM_ID)) { if (id != null && id.intValue() != NO_ID && !msg.headers().contains(SpdyHttpHeaders.Names.STREAM_ID)) {
SpdyHttpHeaders.setStreamId(msg, id); SpdyHttpHeaders.setStreamId(msg, id);
} }
return msg; return msg;
@ -47,12 +47,12 @@ public class SpdyHttpResponseStreamIdHandler extends
@Override @Override
protected Object decode(ChannelHandlerContext ctx, Object msg) throws Exception { protected Object decode(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpHeader) { if (msg instanceof HttpMessage) {
boolean contains = ((HttpHeader) msg).containsHeader(SpdyHttpHeaders.Names.STREAM_ID); boolean contains = ((HttpMessage) msg).headers().contains(SpdyHttpHeaders.Names.STREAM_ID);
if (!contains) { if (!contains) {
ids.add(NO_ID); ids.add(NO_ID);
} else { } else {
ids.add(SpdyHttpHeaders.getStreamId((HttpHeader) msg)); ids.add(SpdyHttpHeaders.getStreamId((HttpMessage) msg));
} }
} else if (msg instanceof SpdyRstStreamFrame) { } else if (msg instanceof SpdyRstStreamFrame) {
ids.remove(((SpdyRstStreamFrame) msg).getStreamId()); ids.remove(((SpdyRstStreamFrame) msg).getStreamId());

View File

@ -22,25 +22,25 @@ public class DefaultHttpRequestTest {
@Test @Test
public void testHeaderRemoval() { public void testHeaderRemoval() {
HttpHeader m = new DefaultHttpRequestHeader( HttpMessage m = new DefaultHttpRequest(
HttpVersion.HTTP_1_1, HttpMethod.GET, "/"); HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
// Insert sample keys. // Insert sample keys.
for (int i = 0; i < 1000; i ++) { for (int i = 0; i < 1000; i ++) {
m.setHeader(String.valueOf(i), ""); m.headers().set(String.valueOf(i), "");
} }
// Remove in reversed order. // Remove in reversed order.
for (int i = 999; i >= 0; i --) { for (int i = 999; i >= 0; i --) {
m.removeHeader(String.valueOf(i)); m.headers().remove(String.valueOf(i));
} }
// Check if random access returns nothing. // Check if random access returns nothing.
for (int i = 0; i < 1000; i ++) { for (int i = 0; i < 1000; i ++) {
Assert.assertNull(m.getHeader(String.valueOf(i))); Assert.assertNull(m.headers().get(String.valueOf(i)));
} }
// Check if sequential access returns nothing. // Check if sequential access returns nothing.
Assert.assertTrue(m.getHeaders().isEmpty()); Assert.assertTrue(m.headers().isEmpty());
} }
} }

View File

@ -15,7 +15,6 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import static org.junit.Assert.*;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
@ -23,20 +22,21 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.embedded.EmbeddedMessageChannel; import io.netty.channel.embedded.EmbeddedMessageChannel;
import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.TooLongFrameException;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import java.util.List;
import org.easymock.EasyMock; import org.easymock.EasyMock;
import org.junit.Test; import org.junit.Test;
public class HttpObjectAggregatorTest { import java.util.List;
import static org.junit.Assert.*;
public class FullHttpMessageDecoderTest {
@Test @Test
public void testAggregate() { public void testAggregate() {
HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024); HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024);
EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr); EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr);
HttpRequestHeader message = new DefaultHttpRequestHeader(HttpVersion.HTTP_1_1, HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
HttpMethod.GET, "http://localhost"); HttpMethod.GET, "http://localhost");
HttpHeaders.setHeader(message, "X-Test", true); HttpHeaders.setHeader(message, "X-Test", true);
HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII));
@ -49,18 +49,18 @@ public class HttpObjectAggregatorTest {
// this should trigger a messageReceived event so return true // this should trigger a messageReceived event so return true
assertTrue(embedder.writeInbound(chunk3)); assertTrue(embedder.writeInbound(chunk3));
assertTrue(embedder.finish()); assertTrue(embedder.finish());
DefaultHttpRequest aggratedMessage = (DefaultHttpRequest) embedder.readInbound(); DefaultFullHttpRequest aggratedMessage = (DefaultFullHttpRequest) embedder.readInbound();
assertNotNull(aggratedMessage); assertNotNull(aggratedMessage);
assertEquals(chunk1.getContent().readableBytes() + chunk2.getContent().readableBytes(), HttpHeaders.getContentLength(aggratedMessage)); assertEquals(chunk1.data().readableBytes() + chunk2.data().readableBytes(), HttpHeaders.getContentLength(aggratedMessage));
assertEquals(aggratedMessage.getHeader("X-Test"), Boolean.TRUE.toString()); assertEquals(aggratedMessage.headers().get("X-Test"), Boolean.TRUE.toString());
checkContentBuffer(aggratedMessage); checkContentBuffer(aggratedMessage);
assertNull(embedder.readInbound()); assertNull(embedder.readInbound());
} }
private static void checkContentBuffer(DefaultHttpRequest aggregatedMessage) { private static void checkContentBuffer(DefaultFullHttpRequest aggregatedMessage) {
CompositeByteBuf buffer = (CompositeByteBuf) aggregatedMessage.getContent(); CompositeByteBuf buffer = (CompositeByteBuf) aggregatedMessage.data();
assertEquals(2, buffer.numComponents()); assertEquals(2, buffer.numComponents());
List<ByteBuf> buffers = buffer.decompose(0, buffer.capacity()); List<ByteBuf> buffers = buffer.decompose(0, buffer.capacity());
assertEquals(2, buffers.size()); assertEquals(2, buffers.size());
@ -74,14 +74,14 @@ public class HttpObjectAggregatorTest {
public void testAggregateWithTrailer() { public void testAggregateWithTrailer() {
HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024); HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024);
EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr); EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr);
HttpRequestHeader message = new DefaultHttpRequestHeader(HttpVersion.HTTP_1_1, HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
HttpMethod.GET, "http://localhost"); HttpMethod.GET, "http://localhost");
HttpHeaders.setHeader(message, "X-Test", true); HttpHeaders.setHeader(message, "X-Test", true);
HttpHeaders.setTransferEncodingChunked(message); HttpHeaders.setTransferEncodingChunked(message);
HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII));
HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII)); HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII));
LastHttpContent trailer = new DefaultLastHttpContent(); LastHttpContent trailer = new DefaultLastHttpContent();
trailer.setHeader("X-Trailer", true); trailer.trailingHeaders().set("X-Trailer", true);
assertFalse(embedder.writeInbound(message)); assertFalse(embedder.writeInbound(message));
assertFalse(embedder.writeInbound(chunk1)); assertFalse(embedder.writeInbound(chunk1));
@ -90,12 +90,12 @@ public class HttpObjectAggregatorTest {
// this should trigger a messageReceived event so return true // this should trigger a messageReceived event so return true
assertTrue(embedder.writeInbound(trailer)); assertTrue(embedder.writeInbound(trailer));
assertTrue(embedder.finish()); assertTrue(embedder.finish());
DefaultHttpRequest aggratedMessage = (DefaultHttpRequest) embedder.readInbound(); DefaultFullHttpRequest aggratedMessage = (DefaultFullHttpRequest) embedder.readInbound();
assertNotNull(aggratedMessage); assertNotNull(aggratedMessage);
assertEquals(chunk1.getContent().readableBytes() + chunk2.getContent().readableBytes(), HttpHeaders.getContentLength(aggratedMessage)); assertEquals(chunk1.data().readableBytes() + chunk2.data().readableBytes(), HttpHeaders.getContentLength(aggratedMessage));
assertEquals(aggratedMessage.getHeader("X-Test"), Boolean.TRUE.toString()); assertEquals(aggratedMessage.headers().get("X-Test"), Boolean.TRUE.toString());
assertEquals(aggratedMessage.getHeader("X-Trailer"), Boolean.TRUE.toString()); assertEquals(aggratedMessage.headers().get("X-Trailer"), Boolean.TRUE.toString());
checkContentBuffer(aggratedMessage); checkContentBuffer(aggratedMessage);
assertNull(embedder.readInbound()); assertNull(embedder.readInbound());
@ -107,7 +107,7 @@ public class HttpObjectAggregatorTest {
public void testTooLongFrameException() { public void testTooLongFrameException() {
HttpObjectAggregator aggr = new HttpObjectAggregator(4); HttpObjectAggregator aggr = new HttpObjectAggregator(4);
EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr); EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr);
HttpRequestHeader message = new DefaultHttpRequestHeader(HttpVersion.HTTP_1_1, HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
HttpMethod.GET, "http://localhost"); HttpMethod.GET, "http://localhost");
HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII));
HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII)); HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII));

View File

@ -15,15 +15,15 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import static org.junit.Assert.*;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedByteChannel; import io.netty.channel.embedded.EmbeddedByteChannel;
import io.netty.handler.codec.CodecException; import io.netty.handler.codec.CodecException;
import io.netty.handler.codec.PrematureChannelClosureException; import io.netty.handler.codec.PrematureChannelClosureException;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.*;
public class HttpClientCodecTest { public class HttpClientCodecTest {
private static final String RESPONSE = "HTTP/1.0 200 OK\r\n" + "Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 28\r\n" + "\r\n" private static final String RESPONSE = "HTTP/1.0 200 OK\r\n" + "Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 28\r\n" + "\r\n"
@ -38,7 +38,7 @@ public class HttpClientCodecTest {
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true); HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true);
EmbeddedByteChannel ch = new EmbeddedByteChannel(codec); EmbeddedByteChannel ch = new EmbeddedByteChannel(codec);
ch.writeOutbound(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/")); ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/"));
ch.writeInbound(Unpooled.copiedBuffer(RESPONSE, CharsetUtil.ISO_8859_1)); ch.writeInbound(Unpooled.copiedBuffer(RESPONSE, CharsetUtil.ISO_8859_1));
ch.finish(); ch.finish();
} }
@ -48,7 +48,7 @@ public class HttpClientCodecTest {
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true); HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true);
EmbeddedByteChannel ch = new EmbeddedByteChannel(codec); EmbeddedByteChannel ch = new EmbeddedByteChannel(codec);
ch.writeOutbound(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/")); ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/"));
ch.writeInbound(Unpooled.copiedBuffer(CHUNKED_RESPONSE, CharsetUtil.ISO_8859_1)); ch.writeInbound(Unpooled.copiedBuffer(CHUNKED_RESPONSE, CharsetUtil.ISO_8859_1));
ch.finish(); ch.finish();
} }
@ -58,7 +58,7 @@ public class HttpClientCodecTest {
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true); HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true);
EmbeddedByteChannel ch = new EmbeddedByteChannel(codec); EmbeddedByteChannel ch = new EmbeddedByteChannel(codec);
assertTrue(ch.writeOutbound(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/"))); assertTrue(ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/")));
assertNotNull(ch.readOutbound()); assertNotNull(ch.readOutbound());
try { try {
@ -75,7 +75,7 @@ public class HttpClientCodecTest {
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true); HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true);
EmbeddedByteChannel ch = new EmbeddedByteChannel(codec); EmbeddedByteChannel ch = new EmbeddedByteChannel(codec);
ch.writeOutbound(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/")); ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/"));
ch.writeInbound(Unpooled.copiedBuffer(INCOMPLETE_CHUNKED_RESPONSE, CharsetUtil.ISO_8859_1)); ch.writeInbound(Unpooled.copiedBuffer(INCOMPLETE_CHUNKED_RESPONSE, CharsetUtil.ISO_8859_1));
try { try {

View File

@ -33,8 +33,8 @@ public class HttpInvalidMessageTest {
public void testRequestWithBadInitialLine() throws Exception { public void testRequestWithBadInitialLine() throws Exception {
EmbeddedByteChannel ch = new EmbeddedByteChannel(new HttpRequestDecoder()); EmbeddedByteChannel ch = new EmbeddedByteChannel(new HttpRequestDecoder());
ch.writeInbound(Unpooled.copiedBuffer("GET / HTTP/1.0 with extra\r\n", CharsetUtil.UTF_8)); ch.writeInbound(Unpooled.copiedBuffer("GET / HTTP/1.0 with extra\r\n", CharsetUtil.UTF_8));
HttpRequestHeader req = (HttpRequestHeader) ch.readInbound(); HttpRequest req = (HttpRequest) ch.readInbound();
DecoderResult dr = req.getDecoderResult(); DecoderResult dr = req.decoderResult();
Assert.assertFalse(dr.isSuccess()); Assert.assertFalse(dr.isSuccess());
Assert.assertFalse(dr.isPartialFailure()); Assert.assertFalse(dr.isPartialFailure());
ensureInboundTrafficDiscarded(ch); ensureInboundTrafficDiscarded(ch);
@ -47,12 +47,12 @@ public class HttpInvalidMessageTest {
ch.writeInbound(Unpooled.copiedBuffer("Good_Name: Good Value\r\n", CharsetUtil.UTF_8)); ch.writeInbound(Unpooled.copiedBuffer("Good_Name: Good Value\r\n", CharsetUtil.UTF_8));
ch.writeInbound(Unpooled.copiedBuffer("Bad=Name: Bad Value\r\n", CharsetUtil.UTF_8)); ch.writeInbound(Unpooled.copiedBuffer("Bad=Name: Bad Value\r\n", CharsetUtil.UTF_8));
ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.UTF_8)); ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.UTF_8));
HttpRequestHeader req = (HttpRequestHeader) ch.readInbound(); HttpRequest req = (HttpRequest) ch.readInbound();
DecoderResult dr = req.getDecoderResult(); DecoderResult dr = req.decoderResult();
Assert.assertFalse(dr.isSuccess()); Assert.assertFalse(dr.isSuccess());
Assert.assertTrue(dr.isPartialFailure()); Assert.assertTrue(dr.isPartialFailure());
Assert.assertEquals("Good Value", req.getHeader("Good_Name")); Assert.assertEquals("Good Value", req.headers().get("Good_Name"));
Assert.assertEquals("/maybe-something", req.getUri()); Assert.assertEquals("/maybe-something", req.uri());
ensureInboundTrafficDiscarded(ch); ensureInboundTrafficDiscarded(ch);
} }
@ -60,8 +60,8 @@ public class HttpInvalidMessageTest {
public void testResponseWithBadInitialLine() throws Exception { public void testResponseWithBadInitialLine() throws Exception {
EmbeddedByteChannel ch = new EmbeddedByteChannel(new HttpResponseDecoder()); EmbeddedByteChannel ch = new EmbeddedByteChannel(new HttpResponseDecoder());
ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.0 BAD_CODE Bad Server\r\n", CharsetUtil.UTF_8)); ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.0 BAD_CODE Bad Server\r\n", CharsetUtil.UTF_8));
HttpResponseHeader res = (HttpResponseHeader) ch.readInbound(); HttpResponse res = (HttpResponse) ch.readInbound();
DecoderResult dr = res.getDecoderResult(); DecoderResult dr = res.decoderResult();
Assert.assertFalse(dr.isSuccess()); Assert.assertFalse(dr.isSuccess());
Assert.assertFalse(dr.isPartialFailure()); Assert.assertFalse(dr.isPartialFailure());
ensureInboundTrafficDiscarded(ch); ensureInboundTrafficDiscarded(ch);
@ -74,12 +74,12 @@ public class HttpInvalidMessageTest {
ch.writeInbound(Unpooled.copiedBuffer("Good_Name: Good Value\r\n", CharsetUtil.UTF_8)); ch.writeInbound(Unpooled.copiedBuffer("Good_Name: Good Value\r\n", CharsetUtil.UTF_8));
ch.writeInbound(Unpooled.copiedBuffer("Bad=Name: Bad Value\r\n", CharsetUtil.UTF_8)); ch.writeInbound(Unpooled.copiedBuffer("Bad=Name: Bad Value\r\n", CharsetUtil.UTF_8));
ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.UTF_8)); ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.UTF_8));
HttpResponseHeader res = (HttpResponseHeader) ch.readInbound(); HttpResponse res = (HttpResponse) ch.readInbound();
DecoderResult dr = res.getDecoderResult(); DecoderResult dr = res.decoderResult();
Assert.assertFalse(dr.isSuccess()); Assert.assertFalse(dr.isSuccess());
Assert.assertTrue(dr.isPartialFailure()); Assert.assertTrue(dr.isPartialFailure());
Assert.assertEquals("Maybe OK", res.getStatus().getReasonPhrase()); Assert.assertEquals("Maybe OK", res.status().reasonPhrase());
Assert.assertEquals("Good Value", res.getHeader("Good_Name")); Assert.assertEquals("Good Value", res.headers().get("Good_Name"));
ensureInboundTrafficDiscarded(ch); ensureInboundTrafficDiscarded(ch);
} }
@ -90,11 +90,11 @@ public class HttpInvalidMessageTest {
ch.writeInbound(Unpooled.copiedBuffer("Transfer-Encoding: chunked\r\n\r\n", CharsetUtil.UTF_8)); ch.writeInbound(Unpooled.copiedBuffer("Transfer-Encoding: chunked\r\n\r\n", CharsetUtil.UTF_8));
ch.writeInbound(Unpooled.copiedBuffer("BAD_LENGTH\r\n", CharsetUtil.UTF_8)); ch.writeInbound(Unpooled.copiedBuffer("BAD_LENGTH\r\n", CharsetUtil.UTF_8));
HttpRequestHeader req = (HttpRequestHeader) ch.readInbound(); HttpRequest req = (HttpRequest) ch.readInbound();
Assert.assertTrue(req.getDecoderResult().isSuccess()); Assert.assertTrue(req.decoderResult().isSuccess());
HttpContent chunk = (HttpContent) ch.readInbound(); HttpContent chunk = (HttpContent) ch.readInbound();
DecoderResult dr = chunk.getDecoderResult(); DecoderResult dr = chunk.decoderResult();
Assert.assertFalse(dr.isSuccess()); Assert.assertFalse(dr.isSuccess());
Assert.assertFalse(dr.isPartialFailure()); Assert.assertFalse(dr.isPartialFailure());
ensureInboundTrafficDiscarded(ch); ensureInboundTrafficDiscarded(ch);

View File

@ -15,14 +15,14 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import static org.junit.Assert.*;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import org.junit.Test; import org.junit.Test;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import static org.junit.Assert.*;
/** /**
*/ */
public class HttpRequestEncoderTest { public class HttpRequestEncoderTest {
@ -31,7 +31,7 @@ public class HttpRequestEncoderTest {
public void testUriWithoutPath() throws Exception { public void testUriWithoutPath() throws Exception {
HttpRequestEncoder encoder = new HttpRequestEncoder(); HttpRequestEncoder encoder = new HttpRequestEncoder();
ByteBuf buffer = Unpooled.buffer(64); ByteBuf buffer = Unpooled.buffer(64);
encoder.encodeInitialLine(buffer, new DefaultHttpRequestHeader(HttpVersion.HTTP_1_1, encoder.encodeInitialLine(buffer, new DefaultHttpRequest(HttpVersion.HTTP_1_1,
HttpMethod.GET, "http://localhost")); HttpMethod.GET, "http://localhost"));
String req = buffer.toString(Charset.forName("US-ASCII")); String req = buffer.toString(Charset.forName("US-ASCII"));
assertEquals("GET http://localhost/ HTTP/1.1\r\n", req); assertEquals("GET http://localhost/ HTTP/1.1\r\n", req);
@ -42,7 +42,7 @@ public class HttpRequestEncoderTest {
public void testUriWithPath() throws Exception { public void testUriWithPath() throws Exception {
HttpRequestEncoder encoder = new HttpRequestEncoder(); HttpRequestEncoder encoder = new HttpRequestEncoder();
ByteBuf buffer = Unpooled.buffer(64); ByteBuf buffer = Unpooled.buffer(64);
encoder.encodeInitialLine(buffer, new DefaultHttpRequestHeader(HttpVersion.HTTP_1_1, encoder.encodeInitialLine(buffer, new DefaultHttpRequest(HttpVersion.HTTP_1_1,
HttpMethod.GET, "http://localhost/")); HttpMethod.GET, "http://localhost/"));
String req = buffer.toString(Charset.forName("US-ASCII")); String req = buffer.toString(Charset.forName("US-ASCII"));
assertEquals("GET http://localhost/ HTTP/1.1\r\n", req); assertEquals("GET http://localhost/ HTTP/1.1\r\n", req);

View File

@ -19,7 +19,6 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedByteChannel; import io.netty.channel.embedded.EmbeddedByteChannel;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -44,8 +43,8 @@ public class HttpServerCodecTest {
decoderEmbedder.writeInbound(prepareDataChunk(offeredContentLength)); decoderEmbedder.writeInbound(prepareDataChunk(offeredContentLength));
decoderEmbedder.finish(); decoderEmbedder.finish();
HttpHeader httpMessage = (HttpHeader) decoderEmbedder.readInbound(); HttpMessage httpMessage = (HttpMessage) decoderEmbedder.readInbound();
//Assert.assertSame(HttpTransferEncoding.STREAMED, httpMessage.getTransferEncoding()); Assert.assertNotNull(httpMessage);
boolean empty = true; boolean empty = true;
int totalBytesPolled = 0; int totalBytesPolled = 0;
@ -55,7 +54,7 @@ public class HttpServerCodecTest {
break; break;
} }
empty = false; empty = false;
totalBytesPolled += httpChunk.getContent().readableBytes(); totalBytesPolled += httpChunk.data().readableBytes();
Assert.assertFalse(httpChunk instanceof LastHttpContent); Assert.assertFalse(httpChunk instanceof LastHttpContent);
} }
Assert.assertFalse(empty); Assert.assertFalse(empty);

View File

@ -15,15 +15,15 @@
*/ */
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import static io.netty.handler.codec.http.HttpHeaders.Values.WEBSOCKET; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpRequestHeader;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.HttpHeaders.Names;
import static io.netty.handler.codec.http.HttpHeaders.Values.*;
import static io.netty.handler.codec.http.HttpVersion.*;
public class WebSocketRequestBuilder { public class WebSocketRequestBuilder {
@ -96,30 +96,30 @@ public class WebSocketRequestBuilder {
return this; return this;
} }
public HttpRequest build() { public FullHttpRequest build() {
HttpRequest req = new DefaultHttpRequest(httpVersion, method, uri); FullHttpRequest req = new DefaultFullHttpRequest(httpVersion, method, uri);
if (host != null) { if (host != null) {
req.setHeader(Names.HOST, host); req.headers().set(Names.HOST, host);
} }
if (upgrade != null) { if (upgrade != null) {
req.setHeader(Names.UPGRADE, upgrade); req.headers().set(Names.UPGRADE, upgrade);
} }
if (connection != null) { if (connection != null) {
req.setHeader(Names.CONNECTION, connection); req.headers().set(Names.CONNECTION, connection);
} }
if (key != null) { if (key != null) {
req.setHeader(Names.SEC_WEBSOCKET_KEY, key); req.headers().set(Names.SEC_WEBSOCKET_KEY, key);
} }
if (origin != null) { if (origin != null) {
req.setHeader(Names.SEC_WEBSOCKET_ORIGIN, origin); req.headers().set(Names.SEC_WEBSOCKET_ORIGIN, origin);
} }
if (version != null) { if (version != null) {
req.setHeader(Names.SEC_WEBSOCKET_VERSION, version.toHttpHeaderValue()); req.headers().set(Names.SEC_WEBSOCKET_VERSION, version.toHttpHeaderValue());
} }
return req; return req;
} }
public static HttpRequestHeader sucessful() { public static HttpRequest sucessful() {
return new WebSocketRequestBuilder().httpVersion(HTTP_1_1) return new WebSocketRequestBuilder().httpVersion(HTTP_1_1)
.method(HttpMethod.GET) .method(HttpMethod.GET)
.uri("/test") .uri("/test")

View File

@ -15,26 +15,26 @@
*/ */
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import static io.netty.handler.codec.http.HttpHeaders.Values.*;
import static io.netty.handler.codec.http.HttpVersion.*;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedByteChannel; import io.netty.channel.embedded.EmbeddedByteChannel;
import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseDecoder; import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpResponseHeader;
import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.codec.http.LastHttpContent;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import static io.netty.handler.codec.http.HttpHeaders.Values.*;
import static io.netty.handler.codec.http.HttpVersion.*;
public class WebSocketServerHandshaker00Test { public class WebSocketServerHandshaker00Test {
@Test @Test
@ -42,17 +42,16 @@ public class WebSocketServerHandshaker00Test {
EmbeddedByteChannel ch = new EmbeddedByteChannel( EmbeddedByteChannel ch = new EmbeddedByteChannel(
new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder()); new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder());
HttpRequest req = new DefaultHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat"); FullHttpRequest req = new DefaultFullHttpRequest(
req.setHeader(Names.HOST, "server.example.com"); HTTP_1_1, HttpMethod.GET, "/chat", Unpooled.copiedBuffer("^n:ds[4U", CharsetUtil.US_ASCII));
req.setHeader(Names.UPGRADE, WEBSOCKET.toLowerCase());
req.setHeader(Names.CONNECTION, "Upgrade");
req.setHeader(Names.ORIGIN, "http://example.com");
req.setHeader(Names.SEC_WEBSOCKET_KEY1, "4 @1 46546xW%0l 1 5");
req.setHeader(Names.SEC_WEBSOCKET_KEY2, "12998 5 Y3 1 .P00");
req.setHeader(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
ByteBuf buffer = Unpooled.copiedBuffer("^n:ds[4U", CharsetUtil.US_ASCII); req.headers().set(Names.HOST, "server.example.com");
req.setContent(buffer); req.headers().set(Names.UPGRADE, WEBSOCKET.toLowerCase());
req.headers().set(Names.CONNECTION, "Upgrade");
req.headers().set(Names.ORIGIN, "http://example.com");
req.headers().set(Names.SEC_WEBSOCKET_KEY1, "4 @1 46546xW%0l 1 5");
req.headers().set(Names.SEC_WEBSOCKET_KEY2, "12998 5 Y3 1 .P00");
req.headers().set(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
new WebSocketServerHandshaker00( new WebSocketServerHandshaker00(
"ws://example.com/chat", "chat", Integer.MAX_VALUE).handshake(ch, req); "ws://example.com/chat", "chat", Integer.MAX_VALUE).handshake(ch, req);
@ -61,12 +60,12 @@ public class WebSocketServerHandshaker00Test {
EmbeddedByteChannel ch2 = new EmbeddedByteChannel(new HttpResponseDecoder()); EmbeddedByteChannel ch2 = new EmbeddedByteChannel(new HttpResponseDecoder());
ch2.writeInbound(resBuf); ch2.writeInbound(resBuf);
HttpResponseHeader res = (HttpResponseHeader) ch2.readInbound(); HttpResponse res = (HttpResponse) ch2.readInbound();
Assert.assertEquals("ws://example.com/chat", res.getHeader(Names.SEC_WEBSOCKET_LOCATION)); Assert.assertEquals("ws://example.com/chat", res.headers().get(Names.SEC_WEBSOCKET_LOCATION));
Assert.assertEquals("chat", res.getHeader(Names.SEC_WEBSOCKET_PROTOCOL)); Assert.assertEquals("chat", res.headers().get(Names.SEC_WEBSOCKET_PROTOCOL));
LastHttpContent content = (LastHttpContent) ch2.readInbound(); LastHttpContent content = (LastHttpContent) ch2.readInbound();
Assert.assertEquals("8jKS'y:G*Co,Wxa-", content.getContent().toString(CharsetUtil.US_ASCII)); Assert.assertEquals("8jKS'y:G*Co,Wxa-", content.data().toString(CharsetUtil.US_ASCII));
} }
} }

View File

@ -15,23 +15,23 @@
*/ */
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import static io.netty.handler.codec.http.HttpHeaders.Values.*;
import static io.netty.handler.codec.http.HttpVersion.*;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.embedded.EmbeddedByteChannel; import io.netty.channel.embedded.EmbeddedByteChannel;
import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseHeader; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseDecoder; import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpResponseEncoder;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import static io.netty.handler.codec.http.HttpHeaders.Values.*;
import static io.netty.handler.codec.http.HttpVersion.*;
public class WebSocketServerHandshaker08Test { public class WebSocketServerHandshaker08Test {
@Test @Test
@ -39,14 +39,14 @@ public class WebSocketServerHandshaker08Test {
EmbeddedByteChannel ch = new EmbeddedByteChannel( EmbeddedByteChannel ch = new EmbeddedByteChannel(
new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder()); new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder());
HttpRequest req = new DefaultHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat"); FullHttpRequest req = new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat");
req.setHeader(Names.HOST, "server.example.com"); req.headers().set(Names.HOST, "server.example.com");
req.setHeader(Names.UPGRADE, WEBSOCKET.toLowerCase()); req.headers().set(Names.UPGRADE, WEBSOCKET.toLowerCase());
req.setHeader(Names.CONNECTION, "Upgrade"); req.headers().set(Names.CONNECTION, "Upgrade");
req.setHeader(Names.SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ=="); req.headers().set(Names.SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ==");
req.setHeader(Names.SEC_WEBSOCKET_ORIGIN, "http://example.com"); req.headers().set(Names.SEC_WEBSOCKET_ORIGIN, "http://example.com");
req.setHeader(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat"); req.headers().set(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
req.setHeader(Names.SEC_WEBSOCKET_VERSION, "8"); req.headers().set(Names.SEC_WEBSOCKET_VERSION, "8");
new WebSocketServerHandshaker08( new WebSocketServerHandshaker08(
"ws://example.com/chat", "chat", false, Integer.MAX_VALUE).handshake(ch, req); "ws://example.com/chat", "chat", false, Integer.MAX_VALUE).handshake(ch, req);
@ -55,10 +55,10 @@ public class WebSocketServerHandshaker08Test {
EmbeddedByteChannel ch2 = new EmbeddedByteChannel(new HttpResponseDecoder()); EmbeddedByteChannel ch2 = new EmbeddedByteChannel(new HttpResponseDecoder());
ch2.writeInbound(resBuf); ch2.writeInbound(resBuf);
HttpResponseHeader res = (HttpResponseHeader) ch2.readInbound(); HttpResponse res = (HttpResponse) ch2.readInbound();
Assert.assertEquals( Assert.assertEquals(
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.getHeader(Names.SEC_WEBSOCKET_ACCEPT)); "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.headers().get(Names.SEC_WEBSOCKET_ACCEPT));
Assert.assertEquals("chat", res.getHeader(Names.SEC_WEBSOCKET_PROTOCOL)); Assert.assertEquals("chat", res.headers().get(Names.SEC_WEBSOCKET_PROTOCOL));
} }
} }

View File

@ -15,23 +15,23 @@
*/ */
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import static io.netty.handler.codec.http.HttpHeaders.Values.*;
import static io.netty.handler.codec.http.HttpVersion.*;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.embedded.EmbeddedByteChannel; import io.netty.channel.embedded.EmbeddedByteChannel;
import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseHeader; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseDecoder; import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpResponseEncoder;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import static io.netty.handler.codec.http.HttpHeaders.Values.*;
import static io.netty.handler.codec.http.HttpVersion.*;
public class WebSocketServerHandshaker13Test { public class WebSocketServerHandshaker13Test {
@Test @Test
@ -39,14 +39,14 @@ public class WebSocketServerHandshaker13Test {
EmbeddedByteChannel ch = new EmbeddedByteChannel( EmbeddedByteChannel ch = new EmbeddedByteChannel(
new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder()); new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder());
HttpRequest req = new DefaultHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat"); FullHttpRequest req = new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat");
req.setHeader(Names.HOST, "server.example.com"); req.headers().set(Names.HOST, "server.example.com");
req.setHeader(Names.UPGRADE, WEBSOCKET.toLowerCase()); req.headers().set(Names.UPGRADE, WEBSOCKET.toLowerCase());
req.setHeader(Names.CONNECTION, "Upgrade"); req.headers().set(Names.CONNECTION, "Upgrade");
req.setHeader(Names.SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ=="); req.headers().set(Names.SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ==");
req.setHeader(Names.SEC_WEBSOCKET_ORIGIN, "http://example.com"); req.headers().set(Names.SEC_WEBSOCKET_ORIGIN, "http://example.com");
req.setHeader(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat"); req.headers().set(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
req.setHeader(Names.SEC_WEBSOCKET_VERSION, "13"); req.headers().set(Names.SEC_WEBSOCKET_VERSION, "13");
new WebSocketServerHandshaker13( new WebSocketServerHandshaker13(
"ws://example.com/chat", "chat", false, Integer.MAX_VALUE).handshake(ch, req); "ws://example.com/chat", "chat", false, Integer.MAX_VALUE).handshake(ch, req);
@ -55,10 +55,10 @@ public class WebSocketServerHandshaker13Test {
EmbeddedByteChannel ch2 = new EmbeddedByteChannel(new HttpResponseDecoder()); EmbeddedByteChannel ch2 = new EmbeddedByteChannel(new HttpResponseDecoder());
ch2.writeInbound(resBuf); ch2.writeInbound(resBuf);
HttpResponseHeader res = (HttpResponseHeader) ch2.readInbound(); HttpResponse res = (HttpResponse) ch2.readInbound();
Assert.assertEquals( Assert.assertEquals(
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.getHeader(Names.SEC_WEBSOCKET_ACCEPT)); "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.headers().get(Names.SEC_WEBSOCKET_ACCEPT));
Assert.assertEquals("chat", res.getHeader(Names.SEC_WEBSOCKET_PROTOCOL)); Assert.assertEquals("chat", res.headers().get(Names.SEC_WEBSOCKET_PROTOCOL));
} }
} }

View File

@ -22,13 +22,13 @@ import io.netty.channel.ChannelInboundMessageHandlerAdapter;
import io.netty.channel.ChannelOutboundMessageHandlerAdapter; import io.netty.channel.ChannelOutboundMessageHandlerAdapter;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.channel.embedded.EmbeddedMessageChannel; import io.netty.channel.embedded.EmbeddedMessageChannel;
import io.netty.handler.codec.http.DefaultHttpRequestHeader; import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpRequestHeader;
import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseHeader;
import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpResponseEncoder;
import org.junit.Test; import org.junit.Test;
@ -46,7 +46,7 @@ public class WebSocketServerProtocolHandlerTest {
writeUpgradeRequest(ch); writeUpgradeRequest(ch);
assertEquals(SWITCHING_PROTOCOLS, ((HttpResponseHeader) ch.outboundMessageBuffer().poll()).getStatus()); assertEquals(SWITCHING_PROTOCOLS, ((HttpResponse) ch.outboundMessageBuffer().poll()).status());
assertNotNull(WebSocketServerProtocolHandler.getHandshaker(handshakerCtx)); assertNotNull(WebSocketServerProtocolHandler.getHandshaker(handshakerCtx));
} }
@ -55,16 +55,16 @@ public class WebSocketServerProtocolHandlerTest {
EmbeddedMessageChannel ch = createChannel(new MockOutboundHandler()); EmbeddedMessageChannel ch = createChannel(new MockOutboundHandler());
writeUpgradeRequest(ch); writeUpgradeRequest(ch);
assertEquals(SWITCHING_PROTOCOLS, ((HttpResponseHeader) ch.outboundMessageBuffer().poll()).getStatus()); assertEquals(SWITCHING_PROTOCOLS, ((HttpResponse) ch.outboundMessageBuffer().poll()).status());
ch.writeInbound(new DefaultHttpRequestHeader(HTTP_1_1, HttpMethod.GET, "/test")); ch.writeInbound(new DefaultHttpRequest(HTTP_1_1, HttpMethod.GET, "/test"));
assertEquals(FORBIDDEN, ((HttpResponseHeader) ch.outboundMessageBuffer().poll()).getStatus()); assertEquals(FORBIDDEN, ((HttpResponse) ch.outboundMessageBuffer().poll()).status());
} }
@Test @Test
public void testHttpUpgradeRequestInvalidUpgradeHeader() { public void testHttpUpgradeRequestInvalidUpgradeHeader() {
EmbeddedMessageChannel ch = createChannel(); EmbeddedMessageChannel ch = createChannel();
HttpRequest httpRequest = new WebSocketRequestBuilder().httpVersion(HTTP_1_1) FullHttpRequest httpRequestWithEntity = new WebSocketRequestBuilder().httpVersion(HTTP_1_1)
.method(HttpMethod.GET) .method(HttpMethod.GET)
.uri("/test") .uri("/test")
.connection("Upgrade") .connection("Upgrade")
@ -72,10 +72,10 @@ public class WebSocketServerProtocolHandlerTest {
.upgrade("BogusSocket") .upgrade("BogusSocket")
.build(); .build();
ch.writeInbound(httpRequest); ch.writeInbound(httpRequestWithEntity);
HttpResponse response = getHttpResponse(ch); FullHttpResponse response = getHttpResponse(ch);
assertEquals(BAD_REQUEST, response.getStatus()); assertEquals(BAD_REQUEST, response.status());
assertEquals("not a WebSocket handshake request: missing upgrade", getResponseMessage(response)); assertEquals("not a WebSocket handshake request: missing upgrade", getResponseMessage(response));
} }
@ -83,7 +83,7 @@ public class WebSocketServerProtocolHandlerTest {
@Test @Test
public void testHttpUpgradeRequestMissingWSKeyHeader() { public void testHttpUpgradeRequestMissingWSKeyHeader() {
EmbeddedMessageChannel ch = createChannel(); EmbeddedMessageChannel ch = createChannel();
HttpRequestHeader httpRequest = new WebSocketRequestBuilder().httpVersion(HTTP_1_1) HttpRequest httpRequest = new WebSocketRequestBuilder().httpVersion(HTTP_1_1)
.method(HttpMethod.GET) .method(HttpMethod.GET)
.uri("/test") .uri("/test")
.key(null) .key(null)
@ -94,8 +94,8 @@ public class WebSocketServerProtocolHandlerTest {
ch.writeInbound(httpRequest); ch.writeInbound(httpRequest);
HttpResponse response = getHttpResponse(ch); FullHttpResponse response = getHttpResponse(ch);
assertEquals(BAD_REQUEST, response.getStatus()); assertEquals(BAD_REQUEST, response.status());
assertEquals("not a WebSocket request: missing key", getResponseMessage(response)); assertEquals("not a WebSocket request: missing key", getResponseMessage(response));
} }
@ -129,13 +129,13 @@ public class WebSocketServerProtocolHandlerTest {
ch.writeInbound(WebSocketRequestBuilder.sucessful()); ch.writeInbound(WebSocketRequestBuilder.sucessful());
} }
private static String getResponseMessage(HttpResponse response) { private static String getResponseMessage(FullHttpResponse response) {
return new String(response.getContent().array()); return new String(response.data().array());
} }
private static HttpResponse getHttpResponse(EmbeddedMessageChannel ch) { private static FullHttpResponse getHttpResponse(EmbeddedMessageChannel ch) {
MessageBuf<Object> outbound = ch.pipeline().context(MockOutboundHandler.class).outboundMessageBuffer(); MessageBuf<Object> outbound = ch.pipeline().context(MockOutboundHandler.class).outboundMessageBuffer();
return (HttpResponse) outbound.poll(); return (FullHttpResponse) outbound.poll();
} }
private static class MockOutboundHandler extends ChannelOutboundMessageHandlerAdapter<Object> { private static class MockOutboundHandler extends ChannelOutboundMessageHandlerAdapter<Object> {

View File

@ -20,10 +20,10 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter; import io.netty.channel.ChannelInboundMessageHandlerAdapter;
import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.stream.ChunkedFile; import io.netty.handler.stream.ChunkedFile;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
@ -94,7 +94,7 @@ import static io.netty.handler.codec.http.HttpVersion.*;
* *
* </pre> * </pre>
*/ */
public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAdapter<HttpRequest> { public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAdapter<FullHttpRequest> {
public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz"; public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
public static final String HTTP_DATE_GMT_TIMEZONE = "GMT"; public static final String HTTP_DATE_GMT_TIMEZONE = "GMT";
@ -102,19 +102,19 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
@Override @Override
public void messageReceived( public void messageReceived(
ChannelHandlerContext ctx, HttpRequest request) throws Exception { ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
if (!request.getDecoderResult().isSuccess()) { if (!request.decoderResult().isSuccess()) {
sendError(ctx, BAD_REQUEST); sendError(ctx, BAD_REQUEST);
return; return;
} }
if (request.getMethod() != GET) { if (request.method() != GET) {
sendError(ctx, METHOD_NOT_ALLOWED); sendError(ctx, METHOD_NOT_ALLOWED);
return; return;
} }
final String uri = request.getUri(); final String uri = request.uri();
final String path = sanitizeUri(uri); final String path = sanitizeUri(uri);
if (path == null) { if (path == null) {
sendError(ctx, FORBIDDEN); sendError(ctx, FORBIDDEN);
@ -142,7 +142,7 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
} }
// Cache Validation // Cache Validation
String ifModifiedSince = request.getHeader(IF_MODIFIED_SINCE); String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE);
if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) { if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) {
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US); SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince); Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);
@ -166,12 +166,12 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
} }
long fileLength = raf.length(); long fileLength = raf.length();
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
setContentLength(response, fileLength); setContentLength(response, fileLength);
setContentTypeHeader(response, file); setContentTypeHeader(response, file);
setDateAndCacheHeaders(response, file); setDateAndCacheHeaders(response, file);
if (isKeepAlive(request)) { if (isKeepAlive(request)) {
response.setHeader(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
} }
// Write the initial line and the header. // Write the initial line and the header.
@ -232,8 +232,8 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[A-Za-z0-9][-_A-Za-z0-9\\.]*"); private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[A-Za-z0-9][-_A-Za-z0-9\\.]*");
private static void sendListing(ChannelHandlerContext ctx, File dir) { private static void sendListing(ChannelHandlerContext ctx, File dir) {
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
response.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8"); response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
String dirPath = dir.getPath(); String dirPath = dir.getPath();
@ -270,26 +270,24 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
buf.append("</ul></body></html>\r\n"); buf.append("</ul></body></html>\r\n");
response.setContent(Unpooled.copiedBuffer(buf, CharsetUtil.UTF_8)); response.data().writeBytes(Unpooled.copiedBuffer(buf, CharsetUtil.UTF_8));
// Close the connection as soon as the error message is sent. // Close the connection as soon as the error message is sent.
ctx.write(response).addListener(ChannelFutureListener.CLOSE); ctx.write(response).addListener(ChannelFutureListener.CLOSE);
} }
private static void sendRedirect(ChannelHandlerContext ctx, String newUri) { private static void sendRedirect(ChannelHandlerContext ctx, String newUri) {
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, FOUND); FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND);
response.setHeader(LOCATION, newUri); response.headers().set(LOCATION, newUri);
// Close the connection as soon as the error message is sent. // Close the connection as soon as the error message is sent.
ctx.write(response).addListener(ChannelFutureListener.CLOSE); ctx.write(response).addListener(ChannelFutureListener.CLOSE);
} }
private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) { private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, status); FullHttpResponse response = new DefaultFullHttpResponse(
response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8"); HTTP_1_1, status, Unpooled.copiedBuffer("Failure: " + status.toString() + "\r\n", CharsetUtil.UTF_8));
response.setContent(Unpooled.copiedBuffer( response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
"Failure: " + status.toString() + "\r\n",
CharsetUtil.UTF_8));
// Close the connection as soon as the error message is sent. // Close the connection as soon as the error message is sent.
ctx.write(response).addListener(ChannelFutureListener.CLOSE); ctx.write(response).addListener(ChannelFutureListener.CLOSE);
@ -302,7 +300,7 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
* Context * Context
*/ */
private static void sendNotModified(ChannelHandlerContext ctx) { private static void sendNotModified(ChannelHandlerContext ctx) {
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, NOT_MODIFIED); FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_MODIFIED);
setDateHeader(response); setDateHeader(response);
// Close the connection as soon as the error message is sent. // Close the connection as soon as the error message is sent.
@ -315,12 +313,12 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
* @param response * @param response
* HTTP response * HTTP response
*/ */
private static void setDateHeader(HttpResponse response) { private static void setDateHeader(FullHttpResponse response) {
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US); SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE)); dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));
Calendar time = new GregorianCalendar(); Calendar time = new GregorianCalendar();
response.setHeader(DATE, dateFormatter.format(time.getTime())); response.headers().set(DATE, dateFormatter.format(time.getTime()));
} }
/** /**
@ -331,19 +329,19 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
* @param fileToCache * @param fileToCache
* file to extract content type * file to extract content type
*/ */
private static void setDateAndCacheHeaders(HttpResponse response, File fileToCache) { private static void setDateAndCacheHeaders(FullHttpResponse response, File fileToCache) {
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US); SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE)); dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));
// Date header // Date header
Calendar time = new GregorianCalendar(); Calendar time = new GregorianCalendar();
response.setHeader(DATE, dateFormatter.format(time.getTime())); response.headers().set(DATE, dateFormatter.format(time.getTime()));
// Add cache headers // Add cache headers
time.add(Calendar.SECOND, HTTP_CACHE_SECONDS); time.add(Calendar.SECOND, HTTP_CACHE_SECONDS);
response.setHeader(EXPIRES, dateFormatter.format(time.getTime())); response.headers().set(EXPIRES, dateFormatter.format(time.getTime()));
response.setHeader(CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS); response.headers().set(CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);
response.setHeader( response.headers().set(
LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified()))); LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified())));
} }
@ -355,9 +353,9 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
* @param file * @param file
* file to extract content type * file to extract content type
*/ */
private static void setContentTypeHeader(HttpResponse response, File file) { private static void setContentTypeHeader(FullHttpResponse response, File file) {
MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap(); MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
response.setHeader(CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath())); response.headers().set(CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath()));
} }
} }

View File

@ -21,10 +21,10 @@ import io.netty.channel.socket.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.ClientCookieEncoder; import io.netty.handler.codec.http.ClientCookieEncoder;
import io.netty.handler.codec.http.DefaultCookie; import io.netty.handler.codec.http.DefaultCookie;
import io.netty.handler.codec.http.DefaultHttpRequestHeader; import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequestHeader; import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
@ -73,14 +73,14 @@ public class HttpSnoopClient {
Channel ch = b.connect().sync().channel(); Channel ch = b.connect().sync().channel();
// Prepare the HTTP request. // Prepare the HTTP request.
HttpRequestHeader request = new DefaultHttpRequestHeader( HttpRequest request = new DefaultHttpRequest(
HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getRawPath()); HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getRawPath());
request.setHeader(HttpHeaders.Names.HOST, host); request.headers().set(HttpHeaders.Names.HOST, host);
request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE); request.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP); request.headers().set(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
// Set some example cookies. // Set some example cookies.
request.setHeader( request.headers().set(
HttpHeaders.Names.COOKIE, HttpHeaders.Names.COOKIE,
ClientCookieEncoder.encode( ClientCookieEncoder.encode(
new DefaultCookie("my-cookie", "foo"), new DefaultCookie("my-cookie", "foo"),

View File

@ -19,7 +19,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter; import io.netty.channel.ChannelInboundMessageHandlerAdapter;
import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseHeader; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.codec.http.LastHttpContent;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
@ -27,16 +27,16 @@ public class HttpSnoopClientHandler extends ChannelInboundMessageHandlerAdapter<
@Override @Override
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpResponseHeader) { if (msg instanceof HttpResponse) {
HttpResponseHeader response = (HttpResponseHeader) msg; HttpResponse response = (HttpResponse) msg;
System.out.println("STATUS: " + response.getStatus()); System.out.println("STATUS: " + response.status());
System.out.println("VERSION: " + response.getProtocolVersion()); System.out.println("VERSION: " + response.protocolVersion());
System.out.println(); System.out.println();
if (!response.getHeaderNames().isEmpty()) { if (!response.headers().isEmpty()) {
for (String name: response.getHeaderNames()) { for (String name: response.headers().names()) {
for (String value: response.getHeaders(name)) { for (String value: response.headers().getAll(name)) {
System.out.println("HEADER: " + name + " = " + value); System.out.println("HEADER: " + name + " = " + value);
} }
} }
@ -52,7 +52,7 @@ public class HttpSnoopClientHandler extends ChannelInboundMessageHandlerAdapter<
if (msg instanceof HttpContent) { if (msg instanceof HttpContent) {
HttpContent content = (HttpContent) msg; HttpContent content = (HttpContent) msg;
System.out.print(content.getContent().toString(CharsetUtil.UTF_8)); System.out.print(content.data().toString(CharsetUtil.UTF_8));
System.out.flush(); System.out.flush();
if (content instanceof LastHttpContent) { if (content instanceof LastHttpContent) {

View File

@ -24,15 +24,15 @@ import io.netty.channel.ChannelInboundMessageHandlerAdapter;
import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.DecoderResult;
import io.netty.handler.codec.http.Cookie; import io.netty.handler.codec.http.Cookie;
import io.netty.handler.codec.http.CookieDecoder; import io.netty.handler.codec.http.CookieDecoder;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.DefaultHttpResponseHeader; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequestHeader;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseHeader;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.ServerCookieEncoder; import io.netty.handler.codec.http.ServerCookieEncoder;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
@ -49,15 +49,14 @@ import static io.netty.handler.codec.http.HttpVersion.*;
public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<Object> { public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<Object> {
private HttpRequestHeader request; private HttpRequest request;
private boolean readingChunks;
/** Buffer that stores the response content */ /** Buffer that stores the response content */
private final StringBuilder buf = new StringBuilder(); private final StringBuilder buf = new StringBuilder();
@Override @Override
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpRequestHeader) { if (msg instanceof HttpRequest) {
HttpRequestHeader request = this.request = (HttpRequestHeader) msg; HttpRequest request = this.request = (HttpRequest) msg;
if (is100ContinueExpected(request)) { if (is100ContinueExpected(request)) {
send100Continue(ctx); send100Continue(ctx);
@ -67,13 +66,13 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
buf.append("WELCOME TO THE WILD WILD WEB SERVER\r\n"); buf.append("WELCOME TO THE WILD WILD WEB SERVER\r\n");
buf.append("===================================\r\n"); buf.append("===================================\r\n");
buf.append("VERSION: ").append(request.getProtocolVersion()).append("\r\n"); buf.append("VERSION: ").append(request.protocolVersion()).append("\r\n");
buf.append("HOSTNAME: ").append(getHost(request, "unknown")).append("\r\n"); buf.append("HOSTNAME: ").append(getHost(request, "unknown")).append("\r\n");
buf.append("REQUEST_URI: ").append(request.getUri()).append("\r\n\r\n"); buf.append("REQUEST_URI: ").append(request.uri()).append("\r\n\r\n");
List<Map.Entry<String, String>> headers = request.getHeaders(); List<Map.Entry<String, String>> headers = request.headers().entries();
if (!headers.isEmpty()) { if (!headers.isEmpty()) {
for (Map.Entry<String, String> h: request.getHeaders()) { for (Map.Entry<String, String> h: request.headers().entries()) {
String key = h.getKey(); String key = h.getKey();
String value = h.getValue(); String value = h.getValue();
buf.append("HEADER: ").append(key).append(" = ").append(value).append("\r\n"); buf.append("HEADER: ").append(key).append(" = ").append(value).append("\r\n");
@ -81,7 +80,7 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
buf.append("\r\n"); buf.append("\r\n");
} }
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri()); QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri());
Map<String, List<String>> params = queryStringDecoder.getParameters(); Map<String, List<String>> params = queryStringDecoder.getParameters();
if (!params.isEmpty()) { if (!params.isEmpty()) {
for (Entry<String, List<String>> p: params.entrySet()) { for (Entry<String, List<String>> p: params.entrySet()) {
@ -94,40 +93,28 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
buf.append("\r\n"); buf.append("\r\n");
} }
if (isTransferEncodingChunked(request)) { appendDecoderResult(buf, request);
readingChunks = true;
} else {
appendDecoderResult(buf, request);
}
} }
if (msg instanceof HttpContent) { if (msg instanceof HttpContent) {
HttpContent httpContent = (HttpContent) msg; HttpContent httpContent = (HttpContent) msg;
ByteBuf content = httpContent.getContent(); ByteBuf content = httpContent.data();
if (content.readable()) { if (content.readable()) {
buf.append("CONTENT: "); buf.append("CONTENT: ");
buf.append(content.toString(CharsetUtil.UTF_8)); buf.append(content.toString(CharsetUtil.UTF_8));
buf.append("\r\n"); buf.append("\r\n");
appendDecoderResult(buf, request);
} }
appendDecoderResult(buf, request);
writeResponse(ctx, request);
if (msg instanceof LastHttpContent) { if (msg instanceof LastHttpContent) {
if (readingChunks) {
buf.append("CHUNK: ");
} else {
buf.append("CONTENT: ");
}
buf.append(content.toString(CharsetUtil.UTF_8)).append("\r\n");
readingChunks = false;
buf.append("END OF CONTENT\r\n"); buf.append("END OF CONTENT\r\n");
LastHttpContent trailer = (LastHttpContent) msg; LastHttpContent trailer = (LastHttpContent) msg;
if (!trailer.getHeaderNames().isEmpty()) { if (!trailer.trailingHeaders().isEmpty()) {
buf.append("\r\n"); buf.append("\r\n");
for (String name: trailer.getHeaderNames()) { for (String name: trailer.trailingHeaders().names()) {
for (String value: trailer.getHeaders(name)) { for (String value: trailer.trailingHeaders().getAll(name)) {
buf.append("TRAILING HEADER: "); buf.append("TRAILING HEADER: ");
buf.append(name).append(" = ").append(value).append("\r\n"); buf.append(name).append(" = ").append(value).append("\r\n");
} }
@ -135,20 +122,13 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
buf.append("\r\n"); buf.append("\r\n");
} }
appendDecoderResult(buf, trailer);
writeResponse(ctx, trailer); writeResponse(ctx, trailer);
} else {
if (readingChunks) {
buf.append("CHUNK: ");
}
buf.append(content.toString(CharsetUtil.UTF_8)).append("\r\n");
appendDecoderResult(buf, httpContent);
} }
} }
} }
private static void appendDecoderResult(StringBuilder buf, HttpObject o) { private static void appendDecoderResult(StringBuilder buf, HttpObject o) {
DecoderResult result = o.getDecoderResult(); DecoderResult result = o.decoderResult();
if (result.isSuccess()) { if (result.isSuccess()) {
return; return;
} }
@ -167,34 +147,34 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
boolean keepAlive = isKeepAlive(request); boolean keepAlive = isKeepAlive(request);
// Build the response object. // Build the response object.
HttpResponse response = new DefaultHttpResponse( FullHttpResponse response = new DefaultFullHttpResponse(
HTTP_1_1, currentObj.getDecoderResult().isSuccess()? OK : BAD_REQUEST); HTTP_1_1, currentObj.decoderResult().isSuccess()? OK : BAD_REQUEST,
Unpooled.copiedBuffer(buf.toString(), CharsetUtil.UTF_8));
response.setContent(Unpooled.copiedBuffer(buf.toString(), CharsetUtil.UTF_8)); response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
if (keepAlive) { if (keepAlive) {
// Add 'Content-Length' header only for a keep-alive connection. // Add 'Content-Length' header only for a keep-alive connection.
response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes()); response.headers().set(CONTENT_LENGTH, response.data().readableBytes());
// Add keep alive header as per: // Add keep alive header as per:
// - http://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01.html#Connection // - http://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01.html#Connection
response.setHeader(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
} }
// Encode the cookie. // Encode the cookie.
String cookieString = request.getHeader(COOKIE); String cookieString = request.headers().get(COOKIE);
if (cookieString != null) { if (cookieString != null) {
Set<Cookie> cookies = CookieDecoder.decode(cookieString); Set<Cookie> cookies = CookieDecoder.decode(cookieString);
if (!cookies.isEmpty()) { if (!cookies.isEmpty()) {
// Reset the cookies if necessary. // Reset the cookies if necessary.
for (Cookie cookie: cookies) { for (Cookie cookie: cookies) {
response.addHeader(SET_COOKIE, ServerCookieEncoder.encode(cookie)); response.headers().add(SET_COOKIE, ServerCookieEncoder.encode(cookie));
} }
} }
} else { } else {
// Browser sent no cookie. Add some. // Browser sent no cookie. Add some.
response.addHeader(SET_COOKIE, ServerCookieEncoder.encode("key1", "value1")); response.headers().add(SET_COOKIE, ServerCookieEncoder.encode("key1", "value1"));
response.addHeader(SET_COOKIE, ServerCookieEncoder.encode("key2", "value2")); response.headers().add(SET_COOKIE, ServerCookieEncoder.encode("key2", "value2"));
} }
// Write the response. // Write the response.
@ -207,7 +187,7 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
} }
private static void send100Continue(ChannelHandlerContext ctx) { private static void send100Continue(ChannelHandlerContext ctx) {
HttpResponseHeader response = new DefaultHttpResponseHeader(HTTP_1_1, CONTINUE); HttpResponse response = new DefaultHttpResponse(HTTP_1_1, CONTINUE);
ctx.write(response); ctx.write(response);
} }

View File

@ -18,7 +18,6 @@ package io.netty.example.http.snoop;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpContentCompressor;
import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpResponseEncoder;
@ -38,7 +37,7 @@ public class HttpSnoopServerInitializer extends ChannelInitializer<SocketChannel
//pipeline.addLast("aggregator", new HttpChunkAggregator(1048576)); //pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
p.addLast("encoder", new HttpResponseEncoder()); p.addLast("encoder", new HttpResponseEncoder());
// Remove the following line if you don't want automatic content compression. // Remove the following line if you don't want automatic content compression.
p.addLast("deflater", new HttpContentCompressor()); //p.addLast("deflater", new HttpContentCompressor());
p.addLast("handler", new HttpSnoopServerHandler()); p.addLast("handler", new HttpSnoopServerHandler());
} }
} }

View File

@ -21,10 +21,10 @@ import io.netty.channel.socket.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.ClientCookieEncoder; import io.netty.handler.codec.http.ClientCookieEncoder;
import io.netty.handler.codec.http.DefaultCookie; import io.netty.handler.codec.http.DefaultCookie;
import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringEncoder; import io.netty.handler.codec.http.QueryStringEncoder;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory; import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
@ -178,33 +178,31 @@ public class HttpUploadClient {
return null; return null;
} }
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uriGet.toASCIIString()); FullHttpRequest request =
request.setHeader(HttpHeaders.Names.HOST, host); new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uriGet.toASCIIString());
request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE); HttpHeaders headers = request.headers();
request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP + ',' headers.set(HttpHeaders.Names.HOST, host);
headers.set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
headers.set(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP + ','
+ HttpHeaders.Values.DEFLATE); + HttpHeaders.Values.DEFLATE);
request.setHeader(HttpHeaders.Names.ACCEPT_CHARSET, "ISO-8859-1,utf-8;q=0.7,*;q=0.7"); headers.set(HttpHeaders.Names.ACCEPT_CHARSET, "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
request.setHeader(HttpHeaders.Names.ACCEPT_LANGUAGE, "fr"); headers.set(HttpHeaders.Names.ACCEPT_LANGUAGE, "fr");
request.setHeader(HttpHeaders.Names.REFERER, uriSimple.toString()); headers.set(HttpHeaders.Names.REFERER, uriSimple.toString());
request.setHeader(HttpHeaders.Names.USER_AGENT, "Netty Simple Http Client side"); headers.set(HttpHeaders.Names.USER_AGENT, "Netty Simple Http Client side");
request.setHeader(HttpHeaders.Names.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); headers.set(HttpHeaders.Names.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
// connection will not close but needed headers.set(HttpHeaders.Names.COOKIE, ClientCookieEncoder.encode(new DefaultCookie("my-cookie", "foo"),
// request.setHeader("Connection","keep-alive");
// request.setHeader("Keep-Alive","300");
request.setHeader(HttpHeaders.Names.COOKIE, ClientCookieEncoder.encode(new DefaultCookie("my-cookie", "foo"),
new DefaultCookie("another-cookie", "bar"))); new DefaultCookie("another-cookie", "bar")));
// send request // send request
List<Entry<String, String>> headers = request.getHeaders(); List<Entry<String, String>> entries = headers.entries();
channel.write(request); channel.write(request).sync();
// Wait for the server to close the connection. // Wait for the server to close the connection.
channel.closeFuture().sync(); channel.closeFuture().sync();
return headers; return entries;
} }
/** /**
@ -220,7 +218,8 @@ public class HttpUploadClient {
Channel channel = bootstrap.connect().sync().channel(); Channel channel = bootstrap.connect().sync().channel();
// Prepare the HTTP request. // Prepare the HTTP request.
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriSimple.toASCIIString()); FullHttpRequest request =
new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriSimple.toASCIIString());
// Use the PostBody encoder // Use the PostBody encoder
HttpPostRequestEncoder bodyRequestEncoder = null; HttpPostRequestEncoder bodyRequestEncoder = null;
@ -236,7 +235,7 @@ public class HttpUploadClient {
// it is legal to add directly header or cookie into the request until finalize // it is legal to add directly header or cookie into the request until finalize
for (Entry<String, String> entry : headers) { for (Entry<String, String> entry : headers) {
request.setHeader(entry.getKey(), entry.getValue()); request.headers().set(entry.getKey(), entry.getValue());
} }
// add Form attribute // add Form attribute
@ -304,7 +303,8 @@ public class HttpUploadClient {
Channel channel = bootstrap.connect().sync().channel(); Channel channel = bootstrap.connect().sync().channel();
// Prepare the HTTP request. // Prepare the HTTP request.
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriFile.toASCIIString()); FullHttpRequest request =
new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriFile.toASCIIString());
// Use the PostBody encoder // Use the PostBody encoder
HttpPostRequestEncoder bodyRequestEncoder = null; HttpPostRequestEncoder bodyRequestEncoder = null;
@ -320,7 +320,7 @@ public class HttpUploadClient {
// it is legal to add directly header or cookie into the request until finalize // it is legal to add directly header or cookie into the request until finalize
for (Entry<String, String> entry : headers) { for (Entry<String, String> entry : headers) {
request.setHeader(entry.getKey(), entry.getValue()); request.headers().set(entry.getKey(), entry.getValue());
} }
// add Form attribute from previous request in formpost() // add Form attribute from previous request in formpost()

View File

@ -19,7 +19,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter; import io.netty.channel.ChannelInboundMessageHandlerAdapter;
import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseHeader; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.codec.http.LastHttpContent;
import io.netty.logging.InternalLogger; import io.netty.logging.InternalLogger;
import io.netty.logging.InternalLoggerFactory; import io.netty.logging.InternalLoggerFactory;
@ -36,21 +36,21 @@ public class HttpUploadClientHandler extends ChannelInboundMessageHandlerAdapter
@Override @Override
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpResponseHeader) { if (msg instanceof HttpResponse) {
HttpResponseHeader response = (HttpResponseHeader) msg; HttpResponse response = (HttpResponse) msg;
logger.info("STATUS: " + response.getStatus()); logger.info("STATUS: " + response.status());
logger.info("VERSION: " + response.getProtocolVersion()); logger.info("VERSION: " + response.protocolVersion());
if (!response.getHeaderNames().isEmpty()) { if (!response.headers().isEmpty()) {
for (String name : response.getHeaderNames()) { for (String name : response.headers().names()) {
for (String value : response.getHeaders(name)) { for (String value : response.headers().getAll(name)) {
logger.info("HEADER: " + name + " = " + value); logger.info("HEADER: " + name + " = " + value);
} }
} }
} }
if (response.getStatus().getCode() == 200 && HttpHeaders.isTransferEncodingChunked(response)) { if (response.status().code() == 200 && HttpHeaders.isTransferEncodingChunked(response)) {
readingChunks = true; readingChunks = true;
logger.info("CHUNKED CONTENT {"); logger.info("CHUNKED CONTENT {");
} else { } else {
@ -59,7 +59,7 @@ public class HttpUploadClientHandler extends ChannelInboundMessageHandlerAdapter
} }
if (msg instanceof HttpContent) { if (msg instanceof HttpContent) {
HttpContent chunk = (HttpContent) msg; HttpContent chunk = (HttpContent) msg;
logger.info(chunk.getContent().toString(CharsetUtil.UTF_8)); logger.info(chunk.data().toString(CharsetUtil.UTF_8));
if (chunk instanceof LastHttpContent) { if (chunk instanceof LastHttpContent) {
if (readingChunks) { if (readingChunks) {
@ -69,7 +69,7 @@ public class HttpUploadClientHandler extends ChannelInboundMessageHandlerAdapter
} }
readingChunks = false; readingChunks = false;
} else { } else {
logger.info(chunk.getContent().toString(CharsetUtil.UTF_8)); logger.info(chunk.data().toString(CharsetUtil.UTF_8));
} }
} }
} }

View File

@ -23,11 +23,11 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter; import io.netty.channel.ChannelInboundMessageHandlerAdapter;
import io.netty.handler.codec.http.Cookie; import io.netty.handler.codec.http.Cookie;
import io.netty.handler.codec.http.CookieDecoder; import io.netty.handler.codec.http.CookieDecoder;
import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequestHeader; import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.handler.codec.http.QueryStringDecoder;
@ -64,7 +64,7 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
private static final InternalLogger logger = InternalLoggerFactory.getInstance(HttpUploadServerHandler.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(HttpUploadServerHandler.class);
private HttpRequestHeader request; private HttpRequest request;
private boolean readingChunks; private boolean readingChunks;
@ -95,15 +95,15 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
@Override @Override
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpRequestHeader) { if (msg instanceof HttpRequest) {
// clean previous FileUpload if Any // clean previous FileUpload if Any
if (decoder != null) { if (decoder != null) {
decoder.cleanFiles(); decoder.cleanFiles();
decoder = null; decoder = null;
} }
HttpRequestHeader request = this.request = (HttpRequestHeader) msg; HttpRequest request = this.request = (HttpRequest) msg;
URI uri = new URI(request.getUri()); URI uri = new URI(request.uri());
if (!uri.getPath().startsWith("/form")) { if (!uri.getPath().startsWith("/form")) {
// Write Menu // Write Menu
writeMenu(ctx); writeMenu(ctx);
@ -113,13 +113,13 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
responseContent.append("WELCOME TO THE WILD WILD WEB SERVER\r\n"); responseContent.append("WELCOME TO THE WILD WILD WEB SERVER\r\n");
responseContent.append("===================================\r\n"); responseContent.append("===================================\r\n");
responseContent.append("VERSION: " + request.getProtocolVersion().getText() + "\r\n"); responseContent.append("VERSION: " + request.protocolVersion().getText() + "\r\n");
responseContent.append("REQUEST_URI: " + request.getUri() + "\r\n\r\n"); responseContent.append("REQUEST_URI: " + request.uri() + "\r\n\r\n");
responseContent.append("\r\n\r\n"); responseContent.append("\r\n\r\n");
// new method // new method
List<Entry<String, String>> headers = request.getHeaders(); List<Entry<String, String>> headers = request.headers().entries();
for (Entry<String, String> entry : headers) { for (Entry<String, String> entry : headers) {
responseContent.append("HEADER: " + entry.getKey() + '=' + entry.getValue() + "\r\n"); responseContent.append("HEADER: " + entry.getKey() + '=' + entry.getValue() + "\r\n");
} }
@ -127,7 +127,7 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
// new method // new method
Set<Cookie> cookies; Set<Cookie> cookies;
String value = request.getHeader(COOKIE); String value = request.headers().get(COOKIE);
if (value == null) { if (value == null) {
cookies = Collections.emptySet(); cookies = Collections.emptySet();
} else { } else {
@ -138,7 +138,7 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
} }
responseContent.append("\r\n\r\n"); responseContent.append("\r\n\r\n");
QueryStringDecoder decoderQuery = new QueryStringDecoder(request.getUri()); QueryStringDecoder decoderQuery = new QueryStringDecoder(request.uri());
Map<String, List<String>> uriAttributes = decoderQuery.getParameters(); Map<String, List<String>> uriAttributes = decoderQuery.getParameters();
for (Entry<String, List<String>> attr: uriAttributes.entrySet()) { for (Entry<String, List<String>> attr: uriAttributes.entrySet()) {
for (String attrVal: attr.getValue()) { for (String attrVal: attr.getValue()) {
@ -300,23 +300,23 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
responseContent.setLength(0); responseContent.setLength(0);
// Decide whether to close the connection or not. // Decide whether to close the connection or not.
boolean close = HttpHeaders.Values.CLOSE.equalsIgnoreCase(request.getHeader(CONNECTION)) boolean close = HttpHeaders.Values.CLOSE.equalsIgnoreCase(request.headers().get(CONNECTION))
|| request.getProtocolVersion().equals(HttpVersion.HTTP_1_0) || request.protocolVersion().equals(HttpVersion.HTTP_1_0)
&& !HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(request.getHeader(CONNECTION)); && !HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(request.headers().get(CONNECTION));
// Build the response object. // Build the response object.
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); FullHttpResponse response = new DefaultFullHttpResponse(
response.setContent(buf); HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf);
response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8"); response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
if (!close) { if (!close) {
// There's no need to add 'Content-Length' header // There's no need to add 'Content-Length' header
// if this is the last response. // if this is the last response.
response.setHeader(CONTENT_LENGTH, String.valueOf(buf.readableBytes())); response.headers().set(CONTENT_LENGTH, String.valueOf(buf.readableBytes()));
} }
Set<Cookie> cookies; Set<Cookie> cookies;
String value = request.getHeader(COOKIE); String value = request.headers().get(COOKIE);
if (value == null) { if (value == null) {
cookies = Collections.emptySet(); cookies = Collections.emptySet();
} else { } else {
@ -325,7 +325,7 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
if (!cookies.isEmpty()) { if (!cookies.isEmpty()) {
// Reset the cookies if necessary. // Reset the cookies if necessary.
for (Cookie cookie : cookies) { for (Cookie cookie : cookies) {
response.addHeader(SET_COOKIE, ServerCookieEncoder.encode(cookie)); response.headers().add(SET_COOKIE, ServerCookieEncoder.encode(cookie));
} }
} }
// Write the response. // Write the response.
@ -410,10 +410,12 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
ByteBuf buf = copiedBuffer(responseContent.toString(), CharsetUtil.UTF_8); ByteBuf buf = copiedBuffer(responseContent.toString(), CharsetUtil.UTF_8);
// Build the response object. // Build the response object.
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); FullHttpResponse response = new DefaultFullHttpResponse(
response.setContent(buf); HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf);
response.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8");
response.setHeader(CONTENT_LENGTH, String.valueOf(buf.readableBytes())); response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
response.headers().set(CONTENT_LENGTH, String.valueOf(buf.readableBytes()));
// Write the response. // Write the response.
ctx.channel().write(response); ctx.channel().write(response);
} }

View File

@ -20,10 +20,10 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter; import io.netty.channel.ChannelInboundMessageHandlerAdapter;
import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame; import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame;
@ -52,23 +52,23 @@ public class AutobahnServerHandler extends ChannelInboundMessageHandlerAdapter<O
@Override @Override
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpRequest) { if (msg instanceof FullHttpRequest) {
handleHttpRequest(ctx, (HttpRequest) msg); handleHttpRequest(ctx, (FullHttpRequest) msg);
} else if (msg instanceof WebSocketFrame) { } else if (msg instanceof WebSocketFrame) {
handleWebSocketFrame(ctx, (WebSocketFrame) msg); handleWebSocketFrame(ctx, (WebSocketFrame) msg);
} }
} }
private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) throws Exception { private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
// Handle a bad request. // Handle a bad request.
if (!req.getDecoderResult().isSuccess()) { if (!req.decoderResult().isSuccess()) {
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, BAD_REQUEST)); sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
return; return;
} }
// Allow only GET methods. // Allow only GET methods.
if (req.getMethod() != GET) { if (req.method() != GET) {
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN)); sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
return; return;
} }
@ -112,16 +112,17 @@ public class AutobahnServerHandler extends ChannelInboundMessageHandlerAdapter<O
} }
} }
private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) { private static void sendHttpResponse(
ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {
// Generate an error page if response status code is not OK (200). // Generate an error page if response status code is not OK (200).
if (res.getStatus().getCode() != 200) { if (res.status().code() != 200) {
res.setContent(Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8)); res.data().writeBytes(Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8));
setContentLength(res, res.getContent().readableBytes()); setContentLength(res, res.data().readableBytes());
} }
// Send the response and close the connection if necessary. // Send the response and close the connection if necessary.
ChannelFuture f = ctx.channel().write(res); ChannelFuture f = ctx.channel().write(res);
if (!isKeepAlive(req) || res.getStatus().getCode() != 200) { if (!isKeepAlive(req) || res.status().code() != 200) {
f.addListener(ChannelFutureListener.CLOSE); f.addListener(ChannelFutureListener.CLOSE);
} }
} }
@ -132,7 +133,7 @@ public class AutobahnServerHandler extends ChannelInboundMessageHandlerAdapter<O
ctx.close(); ctx.close();
} }
private static String getWebSocketLocation(HttpRequest req) { private static String getWebSocketLocation(FullHttpRequest req) {
return "ws://" + req.getHeader(HttpHeaders.Names.HOST); return "ws://" + req.headers().get(HttpHeaders.Names.HOST);
} }
} }

View File

@ -44,6 +44,7 @@ import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestEncoder; import io.netty.handler.codec.http.HttpRequestEncoder;
import io.netty.handler.codec.http.HttpResponseDecoder; import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
@ -91,6 +92,7 @@ public class WebSocketClient {
ChannelPipeline pipeline = ch.pipeline(); ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("decoder", new HttpResponseDecoder()); pipeline.addLast("decoder", new HttpResponseDecoder());
pipeline.addLast("encoder", new HttpRequestEncoder()); pipeline.addLast("encoder", new HttpRequestEncoder());
pipeline.addLast("aggregator", new HttpObjectAggregator(8192));
pipeline.addLast("ws-handler", handler); pipeline.addLast("ws-handler", handler);
} }
}); });
@ -101,7 +103,7 @@ public class WebSocketClient {
// Send 10 messages and wait for responses // Send 10 messages and wait for responses
System.out.println("WebSocket Client sending message"); System.out.println("WebSocket Client sending message");
for (int i = 0; i < 1; i++) { for (int i = 0; i < 10; i++) {
ch.write(new TextWebSocketFrame("Message #" + i)); ch.write(new TextWebSocketFrame("Message #" + i));
} }

View File

@ -42,7 +42,7 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter; import io.netty.channel.ChannelInboundMessageHandlerAdapter;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
@ -82,16 +82,16 @@ public class WebSocketClientHandler extends ChannelInboundMessageHandlerAdapter<
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
Channel ch = ctx.channel(); Channel ch = ctx.channel();
if (!handshaker.isHandshakeComplete()) { if (!handshaker.isHandshakeComplete()) {
handshaker.finishHandshake(ch, (HttpResponse) msg); handshaker.finishHandshake(ch, (FullHttpResponse) msg);
System.out.println("WebSocket Client connected!"); System.out.println("WebSocket Client connected!");
handshakeFuture.setSuccess(); handshakeFuture.setSuccess();
return; return;
} }
if (msg instanceof HttpResponse) { if (msg instanceof FullHttpResponse) {
HttpResponse response = (HttpResponse) msg; FullHttpResponse response = (FullHttpResponse) msg;
throw new Exception("Unexpected HttpResponse (status=" + response.getStatus() + ", content=" throw new Exception("Unexpected FullHttpResponse (status=" + response.status() + ", content="
+ response.getContent().toString(CharsetUtil.UTF_8) + ')'); + response.data().toString(CharsetUtil.UTF_8) + ')');
} }
WebSocketFrame frame = (WebSocketFrame) msg; WebSocketFrame frame = (WebSocketFrame) msg;

View File

@ -21,9 +21,9 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter; import io.netty.channel.ChannelInboundMessageHandlerAdapter;
import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
@ -53,41 +53,39 @@ public class WebSocketServerHandler extends ChannelInboundMessageHandlerAdapter<
@Override @Override
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpRequest) { if (msg instanceof FullHttpRequest) {
handleHttpRequest(ctx, (HttpRequest) msg); handleHttpRequest(ctx, (FullHttpRequest) msg);
} else if (msg instanceof WebSocketFrame) { } else if (msg instanceof WebSocketFrame) {
handleWebSocketFrame(ctx, (WebSocketFrame) msg); handleWebSocketFrame(ctx, (WebSocketFrame) msg);
} }
} }
private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) throws Exception { private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
// Handle a bad request. // Handle a bad request.
if (!req.getDecoderResult().isSuccess()) { if (!req.decoderResult().isSuccess()) {
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, BAD_REQUEST)); sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
return; return;
} }
// Allow only GET methods. // Allow only GET methods.
if (req.getMethod() != GET) { if (req.method() != GET) {
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN)); sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
return; return;
} }
// Send the demo page and favicon.ico // Send the demo page and favicon.ico
if ("/".equals(req.getUri())) { if ("/".equals(req.uri())) {
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, OK);
ByteBuf content = WebSocketServerIndexPage.getContent(getWebSocketLocation(req)); ByteBuf content = WebSocketServerIndexPage.getContent(getWebSocketLocation(req));
FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, OK, content);
res.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8"); res.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
setContentLength(res, content.readableBytes()); setContentLength(res, content.readableBytes());
res.setContent(content);
sendHttpResponse(ctx, req, res); sendHttpResponse(ctx, req, res);
return; return;
} }
if ("/favicon.ico".equals(req.getUri())) { if ("/favicon.ico".equals(req.uri())) {
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, NOT_FOUND); FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND);
sendHttpResponse(ctx, req, res); sendHttpResponse(ctx, req, res);
return; return;
} }
@ -127,16 +125,17 @@ public class WebSocketServerHandler extends ChannelInboundMessageHandlerAdapter<
ctx.channel().write(new TextWebSocketFrame(request.toUpperCase())); ctx.channel().write(new TextWebSocketFrame(request.toUpperCase()));
} }
private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) { private static void sendHttpResponse(
ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {
// Generate an error page if response status code is not OK (200). // Generate an error page if response status code is not OK (200).
if (res.getStatus().getCode() != 200) { if (res.status().code() != 200) {
res.setContent(Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8)); res.data().writeBytes(Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8));
setContentLength(res, res.getContent().readableBytes()); setContentLength(res, res.data().readableBytes());
} }
// Send the response and close the connection if necessary. // Send the response and close the connection if necessary.
ChannelFuture f = ctx.channel().write(res); ChannelFuture f = ctx.channel().write(res);
if (!isKeepAlive(req) || res.getStatus().getCode() != 200) { if (!isKeepAlive(req) || res.status().code() != 200) {
f.addListener(ChannelFutureListener.CLOSE); f.addListener(ChannelFutureListener.CLOSE);
} }
} }
@ -147,7 +146,7 @@ public class WebSocketServerHandler extends ChannelInboundMessageHandlerAdapter<
ctx.close(); ctx.close();
} }
private static String getWebSocketLocation(HttpRequest req) { private static String getWebSocketLocation(FullHttpRequest req) {
return "ws://" + req.getHeader(HOST) + WEBSOCKET_PATH; return "ws://" + req.headers().get(HOST) + WEBSOCKET_PATH;
} }
} }

View File

@ -22,9 +22,9 @@ import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter; import io.netty.channel.ChannelInboundMessageHandlerAdapter;
import io.netty.example.http.websocketx.server.WebSocketServerIndexPage; import io.netty.example.http.websocketx.server.WebSocketServerIndexPage;
import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
@ -54,42 +54,40 @@ public class WebSocketSslServerHandler extends ChannelInboundMessageHandlerAdapt
@Override @Override
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpRequest) { if (msg instanceof FullHttpRequest) {
handleHttpRequest(ctx, (HttpRequest) msg); handleHttpRequest(ctx, (FullHttpRequest) msg);
} else if (msg instanceof WebSocketFrame) { } else if (msg instanceof WebSocketFrame) {
handleWebSocketFrame(ctx, (WebSocketFrame) msg); handleWebSocketFrame(ctx, (WebSocketFrame) msg);
} }
} }
private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) throws Exception { private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
// Handle a bad request. // Handle a bad request.
if (!req.getDecoderResult().isSuccess()) { if (!req.decoderResult().isSuccess()) {
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, BAD_REQUEST)); sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
return; return;
} }
// Allow only GET methods. // Allow only GET methods.
if (req.getMethod() != GET) { if (req.method() != GET) {
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN)); sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
return; return;
} }
// Send the demo page and favicon.ico // Send the demo page and favicon.ico
if ("/".equals(req.getUri())) { if ("/".equals(req.uri())) {
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, OK);
ByteBuf content = WebSocketServerIndexPage.getContent(getWebSocketLocation(req)); ByteBuf content = WebSocketServerIndexPage.getContent(getWebSocketLocation(req));
FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, OK, content);
res.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8"); res.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
setContentLength(res, content.readableBytes()); setContentLength(res, content.readableBytes());
res.setContent(content);
sendHttpResponse(ctx, req, res); sendHttpResponse(ctx, req, res);
return; return;
} }
if ("/favicon.ico".equals(req.getUri())) { if ("/favicon.ico".equals(req.uri())) {
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, NOT_FOUND); FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND);
sendHttpResponse(ctx, req, res); sendHttpResponse(ctx, req, res);
return; return;
} }
@ -129,16 +127,17 @@ public class WebSocketSslServerHandler extends ChannelInboundMessageHandlerAdapt
ctx.channel().write(new TextWebSocketFrame(request.toUpperCase())); ctx.channel().write(new TextWebSocketFrame(request.toUpperCase()));
} }
private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) { private static void sendHttpResponse(
ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {
// Generate an error page if response status code is not OK (200). // Generate an error page if response status code is not OK (200).
if (res.getStatus().getCode() != 200) { if (res.status().code() != 200) {
res.setContent(Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8)); res.data().writeBytes(Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8));
setContentLength(res, res.getContent().readableBytes()); setContentLength(res, res.data().readableBytes());
} }
// Send the response and close the connection if necessary. // Send the response and close the connection if necessary.
ChannelFuture f = ctx.channel().write(res); ChannelFuture f = ctx.channel().write(res);
if (!isKeepAlive(req) || res.getStatus().getCode() != 200) { if (!isKeepAlive(req) || res.status().code() != 200) {
f.addListener(ChannelFutureListener.CLOSE); f.addListener(ChannelFutureListener.CLOSE);
} }
} }
@ -149,7 +148,7 @@ public class WebSocketSslServerHandler extends ChannelInboundMessageHandlerAdapt
ctx.close(); ctx.close();
} }
private static String getWebSocketLocation(HttpRequest req) { private static String getWebSocketLocation(FullHttpRequest req) {
return "wss://" + req.getHeader(HOST) + WEBSOCKET_PATH; return "wss://" + req.headers().get(HOST) + WEBSOCKET_PATH;
} }
} }