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:
parent
f136eafd5e
commit
34820511ff
@ -936,6 +936,10 @@ public abstract class AbstractByteBuf implements ByteBuf {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (isFreed()) {
|
||||
return getClass().getSimpleName() + "(freed)";
|
||||
}
|
||||
|
||||
return getClass().getSimpleName() + '(' +
|
||||
"ridx=" + readerIndex + ", " +
|
||||
"widx=" + writerIndex + ", " +
|
||||
|
@ -57,9 +57,6 @@ public class DefaultByteBufHolder implements ByteBufHolder {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (isFreed()) {
|
||||
return "Message{data=(FREED)}";
|
||||
}
|
||||
return "Message{data=" + ByteBufUtil.hexDump(data()) + '}';
|
||||
return getClass().getSimpleName() + '(' + data().toString() + ')';
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ import static io.netty.handler.codec.http.CookieEncoderUtil.*;
|
||||
* the HTTP cookie version 0, 1, and 2.
|
||||
* <pre>
|
||||
* // Example
|
||||
* {@link HttpRequestHeader} req = ...;
|
||||
* {@link HttpRequest} req = ...;
|
||||
* res.setHeader("Cookie", {@link ClientCookieEncoder}.encode("JSESSIONID", "1234"));
|
||||
* </pre>
|
||||
*
|
||||
|
@ -29,7 +29,7 @@ import java.util.TreeSet;
|
||||
* the HTTP cookie version 0, 1, and 2.
|
||||
*
|
||||
* <pre>
|
||||
* {@link HttpRequestHeader} req = ...;
|
||||
* {@link HttpRequest} req = ...;
|
||||
* String value = req.getHeader("Cookie");
|
||||
* Set<{@link Cookie}> cookies = new {@link CookieDecoder}().decode(value);
|
||||
* </pre>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -22,40 +22,40 @@ import io.netty.buffer.ByteBuf;
|
||||
*/
|
||||
public class DefaultHttpContent extends DefaultHttpObject implements HttpContent {
|
||||
|
||||
private ByteBuf content;
|
||||
private final ByteBuf content;
|
||||
|
||||
/**
|
||||
* Creates a new instance with the specified chunk content.
|
||||
*/
|
||||
public DefaultHttpContent(ByteBuf content) {
|
||||
setContent(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContent(ByteBuf content) {
|
||||
if (content == null) {
|
||||
throw new NullPointerException("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
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append(getClass().getSimpleName());
|
||||
|
||||
buf.append(" size: ");
|
||||
buf.append(getContent().readableBytes());
|
||||
|
||||
buf.append(", decodeResult: ");
|
||||
buf.append(getDecoderResult());
|
||||
buf.append(')');
|
||||
|
||||
return buf.toString();
|
||||
return getClass().getSimpleName() + "(data: " + data() + ", decoderResult: " + decoderResult() + ')';
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -15,31 +15,61 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Combination of {@link HttpHeader} and {@link LastHttpContent} which can be used to <i>combine</i> the headers and
|
||||
* the actual content. {@link HttpObjectAggregator} makes use of this.
|
||||
*
|
||||
* The default {@link HttpMessage} implementation.
|
||||
*/
|
||||
public abstract class DefaultHttpMessage extends DefaultHttpHeader implements LastHttpContent {
|
||||
private ByteBuf content = Unpooled.EMPTY_BUFFER;
|
||||
public abstract class DefaultHttpMessage extends DefaultHttpObject implements HttpMessage {
|
||||
|
||||
public DefaultHttpMessage(HttpVersion version) {
|
||||
super(version);
|
||||
}
|
||||
private final HttpVersion version;
|
||||
private final HttpHeaders headers = new DefaultHttpHeaders();
|
||||
|
||||
@Override
|
||||
public ByteBuf getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContent(ByteBuf content) {
|
||||
if (content == null) {
|
||||
throw new NullPointerException("content");
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
protected DefaultHttpMessage(final HttpVersion version) {
|
||||
if (version == null) {
|
||||
throw new NullPointerException("version");
|
||||
}
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
@ -19,22 +19,22 @@ import io.netty.handler.codec.DecoderResult;
|
||||
|
||||
public class DefaultHttpObject implements HttpObject {
|
||||
|
||||
private DecoderResult decodeResult = DecoderResult.SUCCESS;
|
||||
private DecoderResult decoderResult = DecoderResult.SUCCESS;
|
||||
|
||||
protected DefaultHttpObject() {
|
||||
// Disallow direct instantiation
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecoderResult getDecoderResult() {
|
||||
return decodeResult;
|
||||
public DecoderResult decoderResult() {
|
||||
return decoderResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDecoderResult(DecoderResult result) {
|
||||
if (result == null) {
|
||||
throw new NullPointerException("result");
|
||||
public void updateDecoderResult(DecoderResult decoderResult) {
|
||||
if (decoderResult == null) {
|
||||
throw new NullPointerException("decoderResult");
|
||||
}
|
||||
decodeResult = result;
|
||||
this.decoderResult = decoderResult;
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
@ -15,34 +15,63 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link HttpRequest}.
|
||||
* The default {@link HttpRequest} implementation.
|
||||
*/
|
||||
public class DefaultHttpRequest extends DefaultHttpRequestHeader implements HttpRequest {
|
||||
private ByteBuf content = Unpooled.EMPTY_BUFFER;
|
||||
public class DefaultHttpRequest extends DefaultHttpMessage implements HttpRequest {
|
||||
|
||||
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) {
|
||||
this(httpVersion, method, uri, Unpooled.EMPTY_BUFFER);
|
||||
}
|
||||
|
||||
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");
|
||||
super(httpVersion);
|
||||
if (method == null) {
|
||||
throw new NullPointerException("method");
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
@ -15,35 +15,50 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
/**
|
||||
* Default implementation of a {@link HttpResponse}.
|
||||
* The default {@link HttpResponse} implementation.
|
||||
*/
|
||||
public class DefaultHttpResponse extends DefaultHttpResponseHeader implements HttpResponse {
|
||||
private ByteBuf content = Unpooled.EMPTY_BUFFER;
|
||||
public class DefaultHttpResponse extends DefaultHttpMessage implements HttpResponse {
|
||||
|
||||
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) {
|
||||
this(version, status, Unpooled.EMPTY_BUFFER);
|
||||
}
|
||||
|
||||
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");
|
||||
super(version);
|
||||
if (status == null) {
|
||||
throw new NullPointerException("status");
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -19,16 +19,14 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The default {@link LastHttpContent} implementation.
|
||||
*/
|
||||
public class DefaultLastHttpContent extends DefaultHttpContent implements LastHttpContent {
|
||||
|
||||
private final HttpHeaders headers = new HttpHeaders() {
|
||||
private final HttpHeaders trailingHeaders = new DefaultHttpHeaders() {
|
||||
@Override
|
||||
void validateHeaderName0(String name) {
|
||||
super.validateHeaderName0(name);
|
||||
@ -42,7 +40,7 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
|
||||
};
|
||||
|
||||
public DefaultLastHttpContent() {
|
||||
this(Unpooled.EMPTY_BUFFER);
|
||||
this(Unpooled.buffer(0));
|
||||
}
|
||||
|
||||
public DefaultLastHttpContent(ByteBuf content) {
|
||||
@ -50,64 +48,20 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHeader(final String name, final Object value) {
|
||||
headers.addHeader(name, value);
|
||||
public LastHttpContent copy() {
|
||||
DefaultLastHttpContent copy = new DefaultLastHttpContent(data().copy());
|
||||
copy.trailingHeaders().set(trailingHeaders());
|
||||
return copy;
|
||||
}
|
||||
|
||||
@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();
|
||||
public HttpHeaders trailingHeaders() {
|
||||
return trailingHeaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append(getClass().getSimpleName());
|
||||
buf.append(", size: ");
|
||||
buf.append(getContent().readableBytes());
|
||||
buf.append(", decodeResult: ");
|
||||
buf.append(getDecoderResult());
|
||||
buf.append(')');
|
||||
StringBuilder buf = new StringBuilder(super.toString());
|
||||
buf.append(StringUtil.NEWLINE);
|
||||
appendHeaders(buf);
|
||||
|
||||
@ -117,7 +71,7 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
|
||||
}
|
||||
|
||||
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(": ");
|
||||
buf.append(e.getValue());
|
||||
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
@ -84,8 +84,8 @@ public class HttpClientCodec extends CombinedChannelHandler {
|
||||
@Override
|
||||
protected void encode(
|
||||
ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
|
||||
if (msg instanceof HttpRequestHeader && !done) {
|
||||
queue.offer(((HttpRequestHeader) msg).getMethod());
|
||||
if (msg instanceof HttpRequest && !done) {
|
||||
queue.offer(((HttpRequest) msg).method());
|
||||
}
|
||||
|
||||
super.encode(ctx, msg, out);
|
||||
@ -137,8 +137,8 @@ public class HttpClientCodec extends CombinedChannelHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isContentAlwaysEmpty(HttpHeader msg) {
|
||||
final int statusCode = ((HttpResponseHeader) msg).getStatus().getCode();
|
||||
protected boolean isContentAlwaysEmpty(HttpMessage msg) {
|
||||
final int statusCode = ((HttpResponse) msg).status().code();
|
||||
if (statusCode == 100) {
|
||||
// 100-continue response should be excluded from paired comparison.
|
||||
return true;
|
||||
@ -148,7 +148,7 @@ public class HttpClientCodec extends CombinedChannelHandler {
|
||||
// current response.
|
||||
HttpMethod method = queue.poll();
|
||||
|
||||
char firstChar = method.getName().charAt(0);
|
||||
char firstChar = method.name().charAt(0);
|
||||
switch (firstChar) {
|
||||
case 'H':
|
||||
// According to 4.3, RFC2616:
|
||||
|
@ -15,134 +15,19 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.buffer.ByteBufHolder;
|
||||
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.
|
||||
* {@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,
|
||||
* please {@link HttpObjectAggregator} after {@link HttpObjectDecoder} in the
|
||||
* place {@link HttpObjectAggregator} after {@link HttpObjectDecoder} in the
|
||||
* {@link ChannelPipeline}.
|
||||
* @apiviz.landmark
|
||||
*/
|
||||
public interface HttpContent extends HttpObject {
|
||||
|
||||
HttpContent EMPTY = new HttpContent() {
|
||||
|
||||
@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);
|
||||
public interface HttpContent extends HttpObject, ByteBufHolder {
|
||||
@Override
|
||||
HttpContent copy();
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ import io.netty.handler.codec.compression.ZlibWrapper;
|
||||
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.
|
||||
* If there is no matching encoding, no compression is done. For more
|
||||
* information on how this handler modifies the message, please refer to
|
||||
@ -93,8 +93,8 @@ public class HttpContentCompressor extends HttpContentEncoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result beginEncode(HttpHeader header, HttpContent msg, String acceptEncoding) throws Exception {
|
||||
String contentEncoding = header.getHeader(HttpHeaders.Names.CONTENT_ENCODING);
|
||||
protected Result beginEncode(HttpMessage header, HttpContent msg, String acceptEncoding) throws Exception {
|
||||
String contentEncoding = header.headers().get(HttpHeaders.Names.CONTENT_ENCODING);
|
||||
if (contentEncoding != null &&
|
||||
!HttpHeaders.Values.IDENTITY.equalsIgnoreCase(contentEncoding)) {
|
||||
return null;
|
||||
|
@ -16,13 +16,14 @@
|
||||
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.ChannelHandlerContext;
|
||||
import io.netty.channel.embedded.EmbeddedByteChannel;
|
||||
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
|
||||
* {@link EmbeddedByteChannel}, which is created by {@link #newContentDecoder(String)}.
|
||||
* 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> {
|
||||
|
||||
private EmbeddedByteChannel decoder;
|
||||
private HttpHeader header;
|
||||
private HttpMessage header;
|
||||
private boolean decodeStarted;
|
||||
|
||||
/**
|
||||
@ -55,13 +56,13 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<Object>
|
||||
|
||||
@Override
|
||||
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.
|
||||
return msg;
|
||||
}
|
||||
if (msg instanceof HttpHeader) {
|
||||
if (msg instanceof HttpMessage) {
|
||||
assert header == null;
|
||||
header = (HttpHeader) msg;
|
||||
header = (HttpMessage) msg;
|
||||
|
||||
cleanup();
|
||||
}
|
||||
@ -71,11 +72,11 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<Object>
|
||||
|
||||
if (!decodeStarted) {
|
||||
decodeStarted = true;
|
||||
HttpHeader header = this.header;
|
||||
HttpMessage header = this.header;
|
||||
this.header = null;
|
||||
|
||||
// Determine the content encoding.
|
||||
String contentEncoding = header.getHeader(HttpHeaders.Names.CONTENT_ENCODING);
|
||||
String contentEncoding = header.headers().get(HttpHeaders.Names.CONTENT_ENCODING);
|
||||
if (contentEncoding != null) {
|
||||
contentEncoding = contentEncoding.trim();
|
||||
} else {
|
||||
@ -89,17 +90,17 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<Object>
|
||||
if (HttpHeaders.Values.IDENTITY.equals(targetContentEncoding)) {
|
||||
// Do NOT set the 'Content-Encoding' header if the target encoding is 'identity'
|
||||
// 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 {
|
||||
header.setHeader(HttpHeaders.Names.CONTENT_ENCODING, targetContentEncoding);
|
||||
header.headers().set(HttpHeaders.Names.CONTENT_ENCODING, targetContentEncoding);
|
||||
}
|
||||
Object[] decoded = decodeContent(header, c);
|
||||
|
||||
// Replace the content.
|
||||
if (header.containsHeader(HttpHeaders.Names.CONTENT_LENGTH)) {
|
||||
header.setHeader(
|
||||
if (header.headers().contains(HttpHeaders.Names.CONTENT_LENGTH)) {
|
||||
header.headers().set(
|
||||
HttpHeaders.Names.CONTENT_LENGTH,
|
||||
Integer.toString(((HttpContent) decoded[1]).getContent().readableBytes()));
|
||||
Integer.toString(((ByteBufHolder) decoded[1]).data().readableBytes()));
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
@ -108,13 +109,13 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<Object>
|
||||
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;
|
||||
}
|
||||
|
||||
private Object[] decodeContent(HttpHeader header, HttpContent c) {
|
||||
private Object[] decodeContent(HttpMessage header, HttpContent c) {
|
||||
ByteBuf newContent = Unpooled.buffer();
|
||||
ByteBuf content = c.getContent();
|
||||
ByteBuf content = c.data();
|
||||
decode(content, newContent);
|
||||
|
||||
if (c instanceof LastHttpContent) {
|
||||
|
@ -20,7 +20,7 @@ import io.netty.handler.codec.compression.ZlibCodecFactory;
|
||||
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
|
||||
* handler modifies the message, please refer to {@link HttpContentDecoder}.
|
||||
*/
|
||||
|
@ -16,6 +16,7 @@
|
||||
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.ChannelHandlerContext;
|
||||
import io.netty.channel.embedded.EmbeddedByteChannel;
|
||||
@ -25,20 +26,20 @@ import java.util.ArrayDeque;
|
||||
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
|
||||
* {@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
|
||||
* 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
|
||||
* encoded content. If there is no supported or allowed encoding in the
|
||||
* corresponding {@link HttpRequestHeader}'s {@code "Accept-Encoding"} header,
|
||||
* {@link #beginEncode(HttpHeader, HttpContent, String)} should return {@code null} so that
|
||||
* corresponding {@link HttpRequest}'s {@code "Accept-Encoding"} header,
|
||||
* {@link #beginEncode(HttpMessage, HttpContent, String)} should return {@code null} so that
|
||||
* no encoding occurs (i.e. pass-through).
|
||||
* <p>
|
||||
* 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
|
||||
* {@link HttpContentCompressor}.
|
||||
* <p>
|
||||
@ -46,11 +47,11 @@ import java.util.Queue;
|
||||
* so that this handler can intercept HTTP responses before {@link HttpObjectEncoder}
|
||||
* 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 EmbeddedByteChannel encoder;
|
||||
private HttpHeader header;
|
||||
private HttpMessage header;
|
||||
private boolean encodeStarted;
|
||||
|
||||
/**
|
||||
@ -58,14 +59,14 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeade
|
||||
*/
|
||||
protected HttpContentEncoder() {
|
||||
super(
|
||||
new Class<?>[] { HttpHeader.class },
|
||||
new Class<?>[] { HttpMessage.class },
|
||||
new Class<?>[] { HttpObject.class });
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object decode(ChannelHandlerContext ctx, HttpHeader msg)
|
||||
protected Object decode(ChannelHandlerContext ctx, HttpMessage msg)
|
||||
throws Exception {
|
||||
String acceptedEncoding = msg.getHeader(HttpHeaders.Names.ACCEPT_ENCODING);
|
||||
String acceptedEncoding = msg.headers().get(HttpHeaders.Names.ACCEPT_ENCODING);
|
||||
if (acceptedEncoding == null) {
|
||||
acceptedEncoding = HttpHeaders.Values.IDENTITY;
|
||||
}
|
||||
@ -78,31 +79,31 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeade
|
||||
public Object encode(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.
|
||||
return msg;
|
||||
}
|
||||
if (msg instanceof HttpHeader) {
|
||||
if (msg instanceof HttpMessage) {
|
||||
assert header == null;
|
||||
|
||||
// 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
|
||||
if (msg instanceof HttpContent) {
|
||||
if (msg instanceof HttpRequestHeader) {
|
||||
HttpRequestHeader reqHeader = (HttpRequestHeader) msg;
|
||||
header = new DefaultHttpRequestHeader(reqHeader.getProtocolVersion(), reqHeader.getMethod(),
|
||||
reqHeader.getUri());
|
||||
if (msg instanceof HttpRequest) {
|
||||
HttpRequest reqHeader = (HttpRequest) msg;
|
||||
header = new DefaultHttpRequest(reqHeader.protocolVersion(), reqHeader.method(),
|
||||
reqHeader.uri());
|
||||
HttpHeaders.setHeaders(reqHeader, header);
|
||||
} else if (msg instanceof HttpResponseHeader) {
|
||||
HttpResponseHeader responseHeader = (HttpResponseHeader) msg;
|
||||
header = new DefaultHttpResponseHeader(responseHeader.getProtocolVersion(),
|
||||
responseHeader.getStatus());
|
||||
} else if (msg instanceof HttpResponse) {
|
||||
HttpResponse responseHeader = (HttpResponse) msg;
|
||||
header = new DefaultHttpResponse(responseHeader.protocolVersion(),
|
||||
responseHeader.status());
|
||||
HttpHeaders.setHeaders(responseHeader, header);
|
||||
} else {
|
||||
return msg;
|
||||
}
|
||||
} else {
|
||||
header = (HttpHeader) msg;
|
||||
header = (HttpMessage) msg;
|
||||
}
|
||||
|
||||
cleanup();
|
||||
@ -113,7 +114,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeade
|
||||
|
||||
if (!encodeStarted) {
|
||||
encodeStarted = true;
|
||||
HttpHeader header = this.header;
|
||||
HttpMessage header = this.header;
|
||||
this.header = null;
|
||||
|
||||
// Determine the content encoding.
|
||||
@ -124,25 +125,29 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeade
|
||||
Result result = beginEncode(header, c, acceptEncoding);
|
||||
|
||||
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();
|
||||
|
||||
// Encode the content and remove or replace the existing headers
|
||||
// so that the message looks like a decoded message.
|
||||
header.setHeader(
|
||||
header.headers().set(
|
||||
HttpHeaders.Names.CONTENT_ENCODING,
|
||||
result.getTargetContentEncoding());
|
||||
|
||||
Object[] encoded = encodeContent(header, c);
|
||||
|
||||
if (!HttpHeaders.isTransferEncodingChunked(header) && encoded.length == 3) {
|
||||
if (header.containsHeader(HttpHeaders.Names.CONTENT_LENGTH)) {
|
||||
long length = ((HttpContent) encoded[1]).getContent().readableBytes() +
|
||||
((HttpContent) encoded[2]).getContent().readableBytes();
|
||||
if (header.headers().contains(HttpHeaders.Names.CONTENT_LENGTH)) {
|
||||
long length = ((ByteBufHolder) encoded[1]).data().readableBytes() +
|
||||
((ByteBufHolder) encoded[2]).data().readableBytes();
|
||||
|
||||
header.setHeader(
|
||||
header.headers().set(
|
||||
HttpHeaders.Names.CONTENT_LENGTH,
|
||||
Long.toString(length));
|
||||
}
|
||||
@ -157,9 +162,9 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeade
|
||||
return null;
|
||||
}
|
||||
|
||||
private Object[] encodeContent(HttpHeader header, HttpContent c) {
|
||||
private Object[] encodeContent(HttpMessage header, HttpContent c) {
|
||||
ByteBuf newContent = Unpooled.buffer();
|
||||
ByteBuf content = c.getContent();
|
||||
ByteBuf content = c.data();
|
||||
encode(content, newContent);
|
||||
|
||||
if (c instanceof LastHttpContent) {
|
||||
@ -200,7 +205,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeade
|
||||
* {@code null} if {@code acceptEncoding} is unsupported or rejected
|
||||
* 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
|
||||
public void afterRemove(ChannelHandlerContext ctx) throws Exception {
|
||||
|
@ -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();
|
||||
}
|
@ -17,21 +17,89 @@ package io.netty.handler.codec.http;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
|
||||
/**
|
||||
* 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.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.
|
||||
@ -483,13 +551,13 @@ public class HttpHeaders {
|
||||
* {@code "Connection"} header first and then the return value of
|
||||
* {@link HttpVersion#isKeepAliveDefault()}.
|
||||
*/
|
||||
public static boolean isKeepAlive(HttpHeader message) {
|
||||
String connection = message.getHeader(Names.CONNECTION);
|
||||
public static boolean isKeepAlive(HttpMessage message) {
|
||||
String connection = message.headers().get(Names.CONNECTION);
|
||||
if (Values.CLOSE.equalsIgnoreCase(connection)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (message.getProtocolVersion().isKeepAliveDefault()) {
|
||||
if (message.protocolVersion().isKeepAliveDefault()) {
|
||||
return !Values.CLOSE.equalsIgnoreCase(connection);
|
||||
} else {
|
||||
return Values.KEEP_ALIVE.equalsIgnoreCase(connection);
|
||||
@ -515,18 +583,18 @@ public class HttpHeaders {
|
||||
* </ul></li>
|
||||
* </ul>
|
||||
*/
|
||||
public static void setKeepAlive(HttpHeader message, boolean keepAlive) {
|
||||
if (message.getProtocolVersion().isKeepAliveDefault()) {
|
||||
public static void setKeepAlive(HttpMessage message, boolean keepAlive) {
|
||||
if (message.protocolVersion().isKeepAliveDefault()) {
|
||||
if (keepAlive) {
|
||||
message.removeHeader(Names.CONNECTION);
|
||||
message.headers().remove(Names.CONNECTION);
|
||||
} else {
|
||||
message.setHeader(Names.CONNECTION, Values.CLOSE);
|
||||
message.headers().set(Names.CONNECTION, Values.CLOSE);
|
||||
}
|
||||
} else {
|
||||
if (keepAlive) {
|
||||
message.setHeader(Names.CONNECTION, Values.KEEP_ALIVE);
|
||||
message.headers().set(Names.CONNECTION, Values.KEEP_ALIVE);
|
||||
} 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
|
||||
*/
|
||||
public static String getHeader(HttpHeader message, String name) {
|
||||
return message.getHeader(name);
|
||||
public static String getHeader(HttpMessage message, String 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
|
||||
* header
|
||||
*/
|
||||
public static String getHeader(HttpHeader message, String name, String defaultValue) {
|
||||
String value = message.getHeader(name);
|
||||
public static String getHeader(HttpMessage message, String name, String defaultValue) {
|
||||
String value = message.headers().get(name);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
@ -566,8 +634,8 @@ public class HttpHeaders {
|
||||
* 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>.
|
||||
*/
|
||||
public static void setHeader(HttpHeader message, String name, Object value) {
|
||||
message.setHeader(name, value);
|
||||
public static void setHeader(HttpMessage message, String name, Object value) {
|
||||
message.headers().set(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -584,8 +652,8 @@ public class HttpHeaders {
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public static void setHeader(HttpHeader message, String name, Iterable<?> values) {
|
||||
message.setHeader(name, values);
|
||||
public static void setHeader(HttpMessage message, String name, Iterable<?> 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
|
||||
* <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) {
|
||||
message.addHeader(name, value);
|
||||
public static void addHeader(HttpMessage message, String name, Object value) {
|
||||
message.headers().add(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the header with the specified name.
|
||||
*/
|
||||
public static void removeHeader(HttpHeader message, String name) {
|
||||
message.removeHeader(name);
|
||||
public static void removeHeader(HttpMessage message, String name) {
|
||||
message.headers().remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all headers from the specified message.
|
||||
*/
|
||||
public static void clearHeaders(HttpHeader message) {
|
||||
message.clearHeaders();
|
||||
public static void clearHeaders(HttpMessage message) {
|
||||
message.headers().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -622,7 +690,7 @@ public class HttpHeaders {
|
||||
* @throws NumberFormatException
|
||||
* 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);
|
||||
if (value == null) {
|
||||
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
|
||||
* 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);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
@ -655,23 +723,23 @@ public class HttpHeaders {
|
||||
* 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.
|
||||
*/
|
||||
public static void setIntHeader(HttpHeader message, String name, int value) {
|
||||
message.setHeader(name, value);
|
||||
public static void setIntHeader(HttpMessage message, String name, int value) {
|
||||
message.headers().set(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static void setIntHeader(HttpHeader message, String name, Iterable<Integer> values) {
|
||||
message.setHeader(name, values);
|
||||
public static void setIntHeader(HttpMessage message, String name, Iterable<Integer> values) {
|
||||
message.headers().set(name, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new integer header with the specified name and value.
|
||||
*/
|
||||
public static void addIntHeader(HttpHeader message, String name, int value) {
|
||||
message.addHeader(name, value);
|
||||
public static void addIntHeader(HttpMessage message, String name, int value) {
|
||||
message.headers().add(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -683,7 +751,7 @@ public class HttpHeaders {
|
||||
* @throws ParseException
|
||||
* 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);
|
||||
if (value == null) {
|
||||
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
|
||||
* 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);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
@ -718,11 +786,11 @@ public class HttpHeaders {
|
||||
* The specified value is formatted as defined in
|
||||
* <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) {
|
||||
message.setHeader(name, new HttpHeaderDateFormat().format(value));
|
||||
message.headers().set(name, new HttpHeaderDateFormat().format(value));
|
||||
} 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
|
||||
* <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) {
|
||||
message.setHeader(name, values);
|
||||
public static void setDateHeader(HttpMessage message, String name, Iterable<Date> values) {
|
||||
message.headers().set(name, values);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -741,13 +809,13 @@ public class HttpHeaders {
|
||||
* value is formatted as defined in
|
||||
* <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) {
|
||||
message.addHeader(name, value);
|
||||
public static void addDateHeader(HttpMessage message, String name, Date value) {
|
||||
message.headers().add(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* other.
|
||||
*
|
||||
@ -757,7 +825,7 @@ public class HttpHeaders {
|
||||
* if the message does not have the {@code "Content-Length"} header
|
||||
* 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);
|
||||
if (value != null) {
|
||||
return Long.parseLong(value);
|
||||
@ -776,7 +844,7 @@ public class HttpHeaders {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* other.
|
||||
*
|
||||
@ -784,8 +852,8 @@ public class HttpHeaders {
|
||||
* not have the {@code "Content-Length"} header or its value is not
|
||||
* a number
|
||||
*/
|
||||
public static long getContentLength(HttpHeader message, long defaultValue) {
|
||||
String contentLength = message.getHeader(Names.CONTENT_LENGTH);
|
||||
public static long getContentLength(HttpMessage message, long defaultValue) {
|
||||
String contentLength = message.headers().get(Names.CONTENT_LENGTH);
|
||||
if (contentLength != null) {
|
||||
try {
|
||||
return Long.parseLong(contentLength);
|
||||
@ -809,20 +877,20 @@ public class HttpHeaders {
|
||||
* Returns the content length of the specified web socket message. If the
|
||||
* 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.
|
||||
if (message instanceof HttpRequestHeader) {
|
||||
HttpRequestHeader req = (HttpRequestHeader) message;
|
||||
if (HttpMethod.GET.equals(req.getMethod()) &&
|
||||
req.containsHeader(Names.SEC_WEBSOCKET_KEY1) &&
|
||||
req.containsHeader(Names.SEC_WEBSOCKET_KEY2)) {
|
||||
if (message instanceof HttpRequest) {
|
||||
HttpRequest req = (HttpRequest) message;
|
||||
if (HttpMethod.GET.equals(req.method()) &&
|
||||
req.headers().contains(Names.SEC_WEBSOCKET_KEY1) &&
|
||||
req.headers().contains(Names.SEC_WEBSOCKET_KEY2)) {
|
||||
return 8;
|
||||
}
|
||||
} else if (message instanceof HttpResponseHeader) {
|
||||
HttpResponseHeader res = (HttpResponseHeader) message;
|
||||
if (res.getStatus().getCode() == 101 &&
|
||||
res.containsHeader(Names.SEC_WEBSOCKET_ORIGIN) &&
|
||||
res.containsHeader(Names.SEC_WEBSOCKET_LOCATION)) {
|
||||
} else if (message instanceof HttpResponse) {
|
||||
HttpResponse res = (HttpResponse) message;
|
||||
if (res.status().code() == 101 &&
|
||||
res.headers().contains(Names.SEC_WEBSOCKET_ORIGIN) &&
|
||||
res.headers().contains(Names.SEC_WEBSOCKET_LOCATION)) {
|
||||
return 16;
|
||||
}
|
||||
}
|
||||
@ -834,30 +902,30 @@ public class HttpHeaders {
|
||||
/**
|
||||
* Sets the {@code "Content-Length"} header.
|
||||
*/
|
||||
public static void setContentLength(HttpHeader message, long length) {
|
||||
message.setHeader(Names.CONTENT_LENGTH, length);
|
||||
public static void setContentLength(HttpMessage message, long length) {
|
||||
message.headers().set(Names.CONTENT_LENGTH, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the {@code "Host"} header.
|
||||
*/
|
||||
public static String getHost(HttpHeader message) {
|
||||
return message.getHeader(Names.HOST);
|
||||
public static String getHost(HttpMessage message) {
|
||||
return message.headers().get(Names.HOST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the {@code "Host"} header. If there is no such
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code "Host"} header.
|
||||
*/
|
||||
public static void setHost(HttpHeader message, String value) {
|
||||
message.setHeader(Names.HOST, value);
|
||||
public static void setHost(HttpMessage message, String value) {
|
||||
message.headers().set(Names.HOST, value);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -866,7 +934,7 @@ public class HttpHeaders {
|
||||
* @throws ParseException
|
||||
* 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);
|
||||
}
|
||||
|
||||
@ -875,18 +943,18 @@ public class HttpHeaders {
|
||||
* header or the header is not a formatted date, the {@code defaultValue}
|
||||
* is returned.
|
||||
*/
|
||||
public static Date getDate(HttpHeader message, Date defaultValue) {
|
||||
public static Date getDate(HttpMessage message, Date defaultValue) {
|
||||
return getDateHeader(message, Names.DATE, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code "Date"} header.
|
||||
*/
|
||||
public static void setDate(HttpHeader message, Date value) {
|
||||
public static void setDate(HttpMessage message, Date value) {
|
||||
if (value != null) {
|
||||
message.setHeader(Names.DATE, new HttpHeaderDateFormat().format(value));
|
||||
message.headers().set(Names.DATE, new HttpHeaderDateFormat().format(value));
|
||||
} 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
|
||||
* {@code "Expect: 100-continue"} header.
|
||||
*/
|
||||
public static boolean is100ContinueExpected(HttpHeader message) {
|
||||
public static boolean is100ContinueExpected(HttpMessage message) {
|
||||
// Expect: 100-continue is for requests only.
|
||||
if (!(message instanceof HttpRequestHeader)) {
|
||||
if (!(message instanceof HttpRequest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return false;
|
||||
}
|
||||
@ -915,7 +983,7 @@ public class HttpHeaders {
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
return true;
|
||||
}
|
||||
@ -928,7 +996,7 @@ public class HttpHeaders {
|
||||
* If there is any existing {@code "Expect"} header, they are replaced with
|
||||
* the new one.
|
||||
*/
|
||||
public static void set100ContinueExpected(HttpHeader message) {
|
||||
public static void set100ContinueExpected(HttpMessage message) {
|
||||
set100ContinueExpected(message, true);
|
||||
}
|
||||
|
||||
@ -939,20 +1007,20 @@ public class HttpHeaders {
|
||||
* {@code "Expect"} headers are removed. Otherwise, all {@code "Expect"}
|
||||
* headers are removed completely.
|
||||
*/
|
||||
public static void set100ContinueExpected(HttpHeader message, boolean set) {
|
||||
public static void set100ContinueExpected(HttpMessage message, boolean set) {
|
||||
if (set) {
|
||||
message.setHeader(Names.EXPECT, Values.CONTINUE);
|
||||
message.headers().set(Names.EXPECT, Values.CONTINUE);
|
||||
} else {
|
||||
message.removeHeader(Names.EXPECT);
|
||||
message.headers().remove(Names.EXPECT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the headers on the dst like they are set on the src
|
||||
*/
|
||||
public static void setHeaders(HttpHeader src, HttpHeader dst) {
|
||||
for (String name: src.getHeaderNames()) {
|
||||
dst.setHeader(name, src.getHeaders(name));
|
||||
public static void setHeaders(HttpMessage src, HttpMessage dst) {
|
||||
for (String name: src.headers().names()) {
|
||||
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
|
||||
* @return True if transfer encoding is chunked, otherwise false
|
||||
*/
|
||||
public static boolean isTransferEncodingChunked(HttpHeader message) {
|
||||
List<String> transferEncodingHeaders = message.getHeaders(Names.TRANSFER_ENCODING);
|
||||
public static boolean isTransferEncodingChunked(HttpMessage message) {
|
||||
List<String> transferEncodingHeaders = message.headers().getAll(Names.TRANSFER_ENCODING);
|
||||
if (transferEncodingHeaders.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
@ -1084,322 +1152,167 @@ public class HttpHeaders {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void removeTransferEncodingChunked(HttpHeader m) {
|
||||
List<String> values = m.getHeaders(Names.TRANSFER_ENCODING);
|
||||
public static void removeTransferEncodingChunked(HttpMessage m) {
|
||||
List<String> values = m.headers().getAll(Names.TRANSFER_ENCODING);
|
||||
values.remove(Values.CHUNKED);
|
||||
if (values.isEmpty()) {
|
||||
m.removeHeader(Names.TRANSFER_ENCODING);
|
||||
m.headers().remove(Names.TRANSFER_ENCODING);
|
||||
} 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);
|
||||
removeHeader(m, Names.CONTENT_LENGTH);
|
||||
}
|
||||
|
||||
public static boolean isContentLengthSet(HttpHeader m) {
|
||||
List<String> contentLength = m.getHeaders(Names.CONTENT_LENGTH);
|
||||
public static boolean isContentLengthSet(HttpMessage m) {
|
||||
List<String> contentLength = m.headers().getAll(Names.CONTENT_LENGTH);
|
||||
return !contentLength.isEmpty();
|
||||
}
|
||||
|
||||
private static final int BUCKET_SIZE = 17;
|
||||
protected HttpHeaders() { }
|
||||
|
||||
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;
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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");
|
||||
}
|
||||
|
||||
if (h > 0) {
|
||||
return h;
|
||||
} else if (h == Integer.MIN_VALUE) {
|
||||
return Integer.MAX_VALUE;
|
||||
} else {
|
||||
return -h;
|
||||
for (Map.Entry<String, String> e: headers) {
|
||||
add(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean eq(String name1, String name2) {
|
||||
int nameLen = name1.length();
|
||||
if (nameLen != name2.length()) {
|
||||
return false;
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
clear();
|
||||
for (Map.Entry<String, String> e: headers) {
|
||||
add(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
void setHeader(final String name, final Object value) {
|
||||
validateHeaderName0(name);
|
||||
String strVal = toString(value);
|
||||
validateHeaderValue(strVal);
|
||||
int h = hash(name);
|
||||
int i = index(h);
|
||||
removeHeader0(h, i, name);
|
||||
addHeader0(h, i, name, strVal);
|
||||
}
|
||||
/**
|
||||
* Removes the header with the specified name.
|
||||
*
|
||||
* @param name The name of the header to remove
|
||||
*/
|
||||
public abstract void remove(String name);
|
||||
|
||||
void setHeader(final String name, final Iterable<?> values) {
|
||||
if (values == null) {
|
||||
throw new NullPointerException("values");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Removes all headers from this {@link HttpMessage}.
|
||||
*/
|
||||
public abstract void clear();
|
||||
}
|
||||
|
@ -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,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
@ -15,9 +15,28 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
|
||||
/**
|
||||
* Combines {@link HttpMessage} and {@link LastHttpContent} into one
|
||||
* message. So it represent a <i>complete</i> http message.
|
||||
* An interface that defines a HTTP message, providing common properties for
|
||||
* {@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();
|
||||
}
|
||||
|
@ -154,13 +154,13 @@ public class HttpMethod implements Comparable<HttpMethod> {
|
||||
/**
|
||||
* Returns the name of this method.
|
||||
*/
|
||||
public String getName() {
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getName().hashCode();
|
||||
return name().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -170,16 +170,16 @@ public class HttpMethod implements Comparable<HttpMethod> {
|
||||
}
|
||||
|
||||
HttpMethod that = (HttpMethod) o;
|
||||
return getName().equals(that.getName());
|
||||
return name().equals(that.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
return name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(HttpMethod o) {
|
||||
return getName().compareTo(o.getName());
|
||||
return name().compareTo(o.name());
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,14 @@ package io.netty.handler.codec.http;
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaders.*;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.CompositeByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
@ -29,9 +28,11 @@ import io.netty.util.CharsetUtil;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaders.*;
|
||||
|
||||
/**
|
||||
* A {@link ChannelHandler} that aggregates an {@link HttpHeader}
|
||||
* and its following {@link HttpContent}s into a single {@link HttpHeader} with
|
||||
* A {@link ChannelHandler} that aggregates an {@link HttpMessage}
|
||||
* 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
|
||||
* care of HTTP messages whose transfer encoding is 'chunked'. Insert this
|
||||
* 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);
|
||||
|
||||
private final int maxContentLength;
|
||||
private HttpMessage currentMessage;
|
||||
private FullHttpMessage currentMessage;
|
||||
|
||||
private int maxCumulationBufferComponents = DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS;
|
||||
private ChannelHandlerContext ctx;
|
||||
@ -111,10 +112,12 @@ public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
|
||||
|
||||
@Override
|
||||
protected Object decode(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
|
||||
HttpMessage currentMessage = this.currentMessage;
|
||||
FullHttpMessage currentMessage = this.currentMessage;
|
||||
|
||||
if (msg instanceof HttpHeader) {
|
||||
HttpHeader m = (HttpHeader) msg;
|
||||
if (msg instanceof HttpMessage) {
|
||||
assert currentMessage == null;
|
||||
|
||||
HttpMessage m = (HttpMessage) msg;
|
||||
|
||||
// Handle the 'Expect: 100-continue' header if necessary.
|
||||
// TODO: Respond with 413 Request Entity Too Large
|
||||
@ -125,41 +128,40 @@ public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
|
||||
ctx.write(CONTINUE.duplicate());
|
||||
}
|
||||
|
||||
if (!m.getDecoderResult().isSuccess()) {
|
||||
if (!m.decoderResult().isSuccess()) {
|
||||
removeTransferEncodingChunked(m);
|
||||
this.currentMessage = null;
|
||||
return m;
|
||||
}
|
||||
if (msg instanceof HttpRequestHeader) {
|
||||
HttpRequestHeader header = (HttpRequestHeader) msg;
|
||||
this.currentMessage = new DefaultHttpRequest(header.getProtocolVersion(),
|
||||
header.getMethod(), header.getUri());
|
||||
} else {
|
||||
HttpResponseHeader header = (HttpResponseHeader) msg;
|
||||
this.currentMessage = new DefaultHttpResponse(header.getProtocolVersion(), header.getStatus());
|
||||
if (msg instanceof HttpRequest) {
|
||||
HttpRequest header = (HttpRequest) msg;
|
||||
this.currentMessage = currentMessage = new DefaultFullHttpRequest(header.protocolVersion(),
|
||||
header.method(), header.uri(), Unpooled.compositeBuffer(maxCumulationBufferComponents));
|
||||
} else if (msg instanceof HttpResponse) {
|
||||
HttpResponse header = (HttpResponse) msg;
|
||||
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.
|
||||
removeTransferEncodingChunked(m);
|
||||
this.currentMessage.setContent(Unpooled.compositeBuffer(maxCumulationBufferComponents));
|
||||
removeTransferEncodingChunked(currentMessage);
|
||||
return null;
|
||||
|
||||
} else if (msg instanceof HttpContent) {
|
||||
// Sanity check
|
||||
if (currentMessage == null) {
|
||||
throw new IllegalStateException(
|
||||
"received " + HttpContent.class.getSimpleName() +
|
||||
" without " + HttpHeader.class.getSimpleName() +
|
||||
" or last message's transfer encoding was 'SINGLE'");
|
||||
}
|
||||
assert currentMessage != null;
|
||||
|
||||
// Merge the received chunk into the content of the current message.
|
||||
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
|
||||
// and discard the traffic or close the connection.
|
||||
// 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
|
||||
appendToCumulation(chunk.getContent());
|
||||
if (chunk.data().readable()) {
|
||||
content.addComponent(chunk.data());
|
||||
content.writerIndex(content.writerIndex() + chunk.data().readableBytes());
|
||||
} else {
|
||||
chunk.free();
|
||||
}
|
||||
|
||||
final boolean last;
|
||||
if (!chunk.getDecoderResult().isSuccess()) {
|
||||
currentMessage.setDecoderResult(
|
||||
DecoderResult.partialFailure(chunk.getDecoderResult().cause()));
|
||||
if (!chunk.decoderResult().isSuccess()) {
|
||||
currentMessage.updateDecoderResult(
|
||||
DecoderResult.partialFailure(chunk.decoderResult().cause()));
|
||||
last = true;
|
||||
} else {
|
||||
last = msg instanceof LastHttpContent;
|
||||
@ -187,13 +194,13 @@ public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
|
||||
// Merge trailing headers into the message.
|
||||
if (chunk instanceof LastHttpContent) {
|
||||
LastHttpContent trailer = (LastHttpContent) chunk;
|
||||
for (Entry<String, String> header: trailer.getHeaders()) {
|
||||
currentMessage.setHeader(header.getKey(), header.getValue());
|
||||
for (Entry<String, String> header: trailer.trailingHeaders()) {
|
||||
currentMessage.headers().add(header.getKey(), header.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
// Set the 'Content-Length' header.
|
||||
currentMessage.setHeader(
|
||||
currentMessage.headers().set(
|
||||
HttpHeaders.Names.CONTENT_LENGTH,
|
||||
String.valueOf(content.readableBytes()));
|
||||
|
||||
@ -203,21 +210,12 @@ public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Only " + HttpHeader.class.getSimpleName() + " and " +
|
||||
HttpContent.class.getSimpleName() + " are accepted: " + msg.getClass().getName());
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
private void appendToCumulation(ByteBuf input) {
|
||||
CompositeByteBuf cumulation = (CompositeByteBuf) currentMessage.getContent();
|
||||
cumulation.addComponent(input);
|
||||
cumulation.writerIndex(cumulation.capacity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeAdd(ChannelHandlerContext ctx) throws Exception {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ import io.netty.handler.codec.TooLongFrameException;
|
||||
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.
|
||||
*
|
||||
* <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
|
||||
* 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
|
||||
* consumption. For example, the following HTTP message:
|
||||
* <pre>
|
||||
@ -76,7 +76,7 @@ import java.util.List;
|
||||
* </pre>
|
||||
* triggers {@link HttpRequestDecoder} to generate 3 objects:
|
||||
* <ol>
|
||||
* <li>An {@link HttpRequestHeader},</li>
|
||||
* <li>An {@link HttpRequest},</li>
|
||||
* <li>The first {@link HttpContent} whose content is {@code 'abcdefghijklmnopqrstuvwxyz'},</li>
|
||||
* <li>The second {@link LastHttpContent} whose content is {@code '1234567890abcdef'}, which marks
|
||||
* the end of the content.</li>
|
||||
@ -103,7 +103,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||
private final int maxHeaderSize;
|
||||
private final int maxChunkSize;
|
||||
private ByteBuf content;
|
||||
private HttpHeader message;
|
||||
private HttpMessage message;
|
||||
private long chunkSize;
|
||||
private int headerSize;
|
||||
private int contentRead;
|
||||
@ -211,7 +211,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||
switch (nextState) {
|
||||
case READ_FIXED_LENGTH_CONTENT:
|
||||
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);
|
||||
// chunkSize will be decreased as the READ_FIXED_LENGTH_CONTENT_AS_CHUNKS
|
||||
// state reads data chunk by chunk.
|
||||
@ -221,7 +221,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||
break;
|
||||
case READ_VARIABLE_LENGTH_CONTENT:
|
||||
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);
|
||||
return message;
|
||||
}
|
||||
@ -396,10 +396,10 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isContentAlwaysEmpty(HttpHeader msg) {
|
||||
if (msg instanceof HttpResponseHeader) {
|
||||
HttpResponseHeader res = (HttpResponseHeader) msg;
|
||||
int code = res.getStatus().getCode();
|
||||
protected boolean isContentAlwaysEmpty(HttpMessage msg) {
|
||||
if (msg instanceof HttpResponse) {
|
||||
HttpResponse res = (HttpResponse) msg;
|
||||
int code = res.status().code();
|
||||
|
||||
// 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
|
||||
// - https://github.com/netty/netty/issues/222
|
||||
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
|
||||
return false;
|
||||
}
|
||||
@ -423,12 +423,12 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||
}
|
||||
|
||||
private Object reset() {
|
||||
HttpHeader message = this.message;
|
||||
HttpMessage message = this.message;
|
||||
ByteBuf content = this.content;
|
||||
LastHttpContent httpContent;
|
||||
|
||||
if (content == null || !content.readable()) {
|
||||
httpContent = HttpContent.LAST_CONTENT;
|
||||
httpContent = LastHttpContent.EMPTY_LAST_CONTENT;
|
||||
} else {
|
||||
httpContent = new DefaultLastHttpContent(content);
|
||||
}
|
||||
@ -441,13 +441,13 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||
return messages;
|
||||
}
|
||||
|
||||
private HttpHeader invalidMessage(Exception cause) {
|
||||
private HttpMessage invalidMessage(Exception cause) {
|
||||
checkpoint(State.BAD_MESSAGE);
|
||||
if (message != null) {
|
||||
message.setDecoderResult(DecoderResult.partialFailure(cause));
|
||||
message.updateDecoderResult(DecoderResult.partialFailure(cause));
|
||||
} else {
|
||||
message = createInvalidMessage();
|
||||
message.setDecoderResult(DecoderResult.failure(cause));
|
||||
message.updateDecoderResult(DecoderResult.failure(cause));
|
||||
}
|
||||
return message;
|
||||
}
|
||||
@ -455,7 +455,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||
private HttpContent invalidChunk(Exception cause) {
|
||||
checkpoint(State.BAD_MESSAGE);
|
||||
HttpContent chunk = new DefaultHttpContent(Unpooled.EMPTY_BUFFER);
|
||||
chunk.setDecoderResult(DecoderResult.failure(cause));
|
||||
chunk.updateDecoderResult(DecoderResult.failure(cause));
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@ -492,19 +492,19 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||
|
||||
private State readHeaders(ByteBuf buffer) {
|
||||
headerSize = 0;
|
||||
final HttpHeader message = this.message;
|
||||
final HttpMessage message = this.message;
|
||||
String line = readHeader(buffer);
|
||||
String name = null;
|
||||
String value = null;
|
||||
if (!line.isEmpty()) {
|
||||
message.clearHeaders();
|
||||
message.headers().clear();
|
||||
do {
|
||||
char firstChar = line.charAt(0);
|
||||
if (name != null && (firstChar == ' ' || firstChar == '\t')) {
|
||||
value = value + ' ' + line.trim();
|
||||
} else {
|
||||
if (name != null) {
|
||||
message.addHeader(name, value);
|
||||
message.headers().add(name, value);
|
||||
}
|
||||
String[] header = splitHeader(line);
|
||||
name = header[0];
|
||||
@ -516,7 +516,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||
|
||||
// Add the last header.
|
||||
if (name != null) {
|
||||
message.addHeader(name, value);
|
||||
message.headers().add(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -544,7 +544,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||
do {
|
||||
char firstChar = line.charAt(0);
|
||||
if (lastHeader != null && (firstChar == ' ' || firstChar == '\t')) {
|
||||
List<String> current = trailer.getHeaders(lastHeader);
|
||||
List<String> current = trailer.trailingHeaders().getAll(lastHeader);
|
||||
if (!current.isEmpty()) {
|
||||
int lastPos = current.size() - 1;
|
||||
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) &&
|
||||
!name.equalsIgnoreCase(HttpHeaders.Names.TRANSFER_ENCODING) &&
|
||||
!name.equalsIgnoreCase(HttpHeaders.Names.TRAILER)) {
|
||||
trailer.addHeader(name, header[1]);
|
||||
trailer.trailingHeaders().add(name, header[1]);
|
||||
}
|
||||
lastHeader = name;
|
||||
}
|
||||
@ -569,7 +569,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||
return trailer;
|
||||
}
|
||||
|
||||
return HttpContent.LAST_CONTENT;
|
||||
return LastHttpContent.EMPTY_LAST_CONTENT;
|
||||
}
|
||||
|
||||
private String readHeader(ByteBuf buffer) {
|
||||
@ -612,8 +612,8 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||
}
|
||||
|
||||
protected abstract boolean isDecodingRequest();
|
||||
protected abstract HttpHeader createMessage(String[] initialLine) throws Exception;
|
||||
protected abstract HttpHeader createInvalidMessage();
|
||||
protected abstract HttpMessage createMessage(String[] initialLine) throws Exception;
|
||||
protected abstract HttpMessage createInvalidMessage();
|
||||
|
||||
private static int getChunkSize(String hex) {
|
||||
hex = hex.trim();
|
||||
|
@ -15,8 +15,6 @@
|
||||
*/
|
||||
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.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
@ -24,8 +22,11 @@ import io.netty.util.CharsetUtil;
|
||||
|
||||
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}.
|
||||
*
|
||||
* <h3>Extensibility</h3>
|
||||
@ -38,9 +39,14 @@ import java.util.Map;
|
||||
* implement all abstract methods properly.
|
||||
* @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.
|
||||
@ -49,38 +55,44 @@ public abstract class HttpObjectEncoder<H extends HttpHeader> extends MessageToB
|
||||
super(HttpObject.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
|
||||
if (msg instanceof HttpHeader) {
|
||||
HttpHeader m = (HttpHeader) msg;
|
||||
chunked = HttpHeaders.isTransferEncodingChunked(m);
|
||||
// Encode the message.
|
||||
out.markWriterIndex();
|
||||
if (msg instanceof HttpMessage) {
|
||||
if (state != ST_INIT) {
|
||||
throw new IllegalStateException("unexpected message type: " + msg.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
HttpMessage m = (HttpMessage) msg;
|
||||
|
||||
// Encode the message.
|
||||
encodeInitialLine(out, (H) m);
|
||||
encodeHeaders(out, m);
|
||||
out.writeByte(CR);
|
||||
out.writeByte(LF);
|
||||
|
||||
state = HttpHeaders.isTransferEncodingChunked(m) ? ST_CONTENT_CHUNK : ST_CONTENT_NON_CHUNK;
|
||||
}
|
||||
|
||||
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) {
|
||||
out.writeByte((byte) '0');
|
||||
out.writeByte(CR);
|
||||
out.writeByte(LF);
|
||||
encodeTrailingHeaders(out, (LastHttpContent) chunk);
|
||||
out.writeByte(CR);
|
||||
out.writeByte(LF);
|
||||
} else {
|
||||
ByteBuf content = chunk.getContent();
|
||||
int contentLength = content.readableBytes();
|
||||
state = ST_INIT;
|
||||
}
|
||||
} else if (state == ST_CONTENT_CHUNK) {
|
||||
if (contentLength > 0) {
|
||||
out.writeBytes(copiedBuffer(Integer.toHexString(contentLength), CharsetUtil.US_ASCII));
|
||||
out.writeByte(CR);
|
||||
out.writeByte(LF);
|
||||
@ -88,18 +100,30 @@ public abstract class HttpObjectEncoder<H extends HttpHeader> extends MessageToB
|
||||
out.writeByte(CR);
|
||||
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) {
|
||||
for (Map.Entry<String, String> h: message.getHeaders()) {
|
||||
private static void encodeHeaders(ByteBuf buf, HttpMessage message) {
|
||||
for (Map.Entry<String, String> h: message.headers()) {
|
||||
encodeHeader(buf, h.getKey(), h.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
@ -15,9 +15,35 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
|
||||
/**
|
||||
* Combinate the {@link HttpRequestHeader} and {@link HttpMessage}, so the request is a <i>complete</i> HTTP
|
||||
* request.
|
||||
* 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 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();
|
||||
}
|
||||
|
@ -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>
|
||||
* <table border="1">
|
||||
@ -71,14 +71,14 @@ public class HttpRequestDecoder extends HttpObjectDecoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpHeader createMessage(String[] initialLine) throws Exception {
|
||||
return new DefaultHttpRequestHeader(
|
||||
protected HttpMessage createMessage(String[] initialLine) throws Exception {
|
||||
return new DefaultHttpRequest(
|
||||
HttpVersion.valueOf(initialLine[2]), HttpMethod.valueOf(initialLine[0]), initialLine[1]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpHeader createInvalidMessage() {
|
||||
return new DefaultHttpRequestHeader(HttpVersion.HTTP_1_0, HttpMethod.GET, "/bad-request");
|
||||
protected HttpMessage createInvalidMessage() {
|
||||
return new DefaultHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.GET, "/bad-request");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -15,25 +15,31 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpConstants.*;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
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}.
|
||||
*/
|
||||
public class HttpRequestEncoder extends HttpObjectEncoder<HttpRequestHeader> {
|
||||
public class HttpRequestEncoder extends HttpObjectEncoder<HttpRequest> {
|
||||
private static final char SLASH = '/';
|
||||
|
||||
@Override
|
||||
protected void encodeInitialLine(ByteBuf buf, HttpRequestHeader request) throws Exception {
|
||||
buf.writeBytes(request.getMethod().toString().getBytes(CharsetUtil.US_ASCII));
|
||||
public boolean isEncodable(Object msg) throws Exception {
|
||||
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);
|
||||
|
||||
// Add / as absolute path if no is present.
|
||||
// See http://tools.ietf.org/html/rfc2616#section-5.1.2
|
||||
String uri = request.getUri();
|
||||
String uri = request.uri();
|
||||
int start = uri.indexOf("://");
|
||||
if (start != -1) {
|
||||
int startIndex = start + 3;
|
||||
@ -44,7 +50,7 @@ public class HttpRequestEncoder extends HttpObjectEncoder<HttpRequestHeader> {
|
||||
buf.writeBytes(uri.getBytes("UTF-8"));
|
||||
|
||||
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(LF);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -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,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
@ -15,9 +15,26 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
|
||||
/**
|
||||
* Combination of a {@link HttpResponseHeader} and {@link HttpMessage}.
|
||||
* So it represent a <i>complete</i> http response.
|
||||
* 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 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();
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
* <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>
|
||||
* header. Because {@link HttpResponseDecoder} is not able to determine if the
|
||||
* 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.
|
||||
* </p><p>
|
||||
* If you are writing an HTTP client that issues a <tt>HEAD</tt> request,
|
||||
@ -102,15 +102,15 @@ public class HttpResponseDecoder extends HttpObjectDecoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpHeader createMessage(String[] initialLine) {
|
||||
return new DefaultHttpResponseHeader(
|
||||
protected HttpMessage createMessage(String[] initialLine) {
|
||||
return new DefaultHttpResponse(
|
||||
HttpVersion.valueOf(initialLine[0]),
|
||||
new HttpResponseStatus(Integer.valueOf(initialLine[1]), initialLine[2]));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpHeader createInvalidMessage() {
|
||||
return new DefaultHttpResponseHeader(HttpVersion.HTTP_1_0, UNKNOWN_STATUS);
|
||||
protected HttpMessage createInvalidMessage() {
|
||||
return new DefaultHttpResponse(HttpVersion.HTTP_1_0, UNKNOWN_STATUS);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -15,23 +15,29 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpConstants.*;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
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}.
|
||||
*/
|
||||
public class HttpResponseEncoder extends HttpObjectEncoder<HttpResponseHeader> {
|
||||
public class HttpResponseEncoder extends HttpObjectEncoder<HttpResponse> {
|
||||
|
||||
@Override
|
||||
protected void encodeInitialLine(ByteBuf buf, HttpResponseHeader response) throws Exception {
|
||||
buf.writeBytes(response.getProtocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
|
||||
public boolean isEncodable(Object msg) throws Exception {
|
||||
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.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.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(LF);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -447,20 +447,20 @@ public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
|
||||
/**
|
||||
* Returns the code of this status.
|
||||
*/
|
||||
public int getCode() {
|
||||
public int code() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the reason phrase of this status.
|
||||
*/
|
||||
public String getReasonPhrase() {
|
||||
public String reasonPhrase() {
|
||||
return reasonPhrase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getCode();
|
||||
return code();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -469,12 +469,12 @@ public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getCode() == ((HttpResponseStatus) o).getCode();
|
||||
return code() == ((HttpResponseStatus) o).code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(HttpResponseStatus o) {
|
||||
return getCode() - o.getCode();
|
||||
return code() - o.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -15,9 +15,9 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
|
||||
/**
|
||||
* The last {@link HttpContent} which has trailing headers.
|
||||
@ -25,68 +25,48 @@ import java.util.Set;
|
||||
public interface LastHttpContent extends HttpContent {
|
||||
|
||||
/**
|
||||
* Returns the trailing header value with the specified header name.
|
||||
* 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
|
||||
* The 'end of content' marker in chunked encoding.
|
||||
*/
|
||||
String getHeader(String name);
|
||||
LastHttpContent EMPTY_LAST_CONTENT = new LastHttpContent() {
|
||||
|
||||
/**
|
||||
* Returns the trailing header values with the specified header name.
|
||||
*
|
||||
* @return the {@link List} of header values. An empty list if there is no
|
||||
* such header.
|
||||
*/
|
||||
List<String> getHeaders(String name);
|
||||
@Override
|
||||
public ByteBuf data() {
|
||||
return Unpooled.EMPTY_BUFFER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the all header names and values that this trailer contains.
|
||||
*
|
||||
* @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
|
||||
public LastHttpContent copy() {
|
||||
return EMPTY_LAST_CONTENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if and only if there is a trailing header with
|
||||
* the specified header name.
|
||||
*/
|
||||
boolean containsHeader(String name);
|
||||
@Override
|
||||
public HttpHeaders trailingHeaders() {
|
||||
return HttpHeaders.EMPTY_HEADERS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Set} of all trailing header names that this trailer
|
||||
* contains.
|
||||
*/
|
||||
Set<String> getHeaderNames();
|
||||
@Override
|
||||
public DecoderResult decoderResult() {
|
||||
return DecoderResult.SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new trailing header with the specified name and value.
|
||||
*/
|
||||
void addHeader(String name, Object value);
|
||||
@Override
|
||||
public void updateDecoderResult(DecoderResult result) {
|
||||
throw new UnsupportedOperationException("read only");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new trailing header with the specified name and value.
|
||||
* If there is an existing trailing header with the same name, the existing
|
||||
* one is removed.
|
||||
*/
|
||||
void setHeader(String name, Object value);
|
||||
@Override
|
||||
public boolean isFreed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new trailing header with the specified name and values.
|
||||
* If there is an existing trailing header with the same name, the existing
|
||||
* one is removed.
|
||||
*/
|
||||
void setHeader(String name, Iterable<?> values);
|
||||
@Override
|
||||
public void free() {
|
||||
// NOOP
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the trailing header with the specified name.
|
||||
*/
|
||||
void removeHeader(String name);
|
||||
HttpHeaders trailingHeaders();
|
||||
|
||||
/**
|
||||
* Removes all trailing headers from this trailer.
|
||||
*/
|
||||
void clearHeaders();
|
||||
@Override
|
||||
LastHttpContent copy();
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ import java.util.Map;
|
||||
* @see QueryStringEncoder
|
||||
*
|
||||
* @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 {
|
||||
|
||||
|
@ -36,7 +36,7 @@ import java.util.List;
|
||||
* @see QueryStringDecoder
|
||||
*
|
||||
* @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 {
|
||||
|
||||
|
@ -15,19 +15,19 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import static io.netty.handler.codec.http.CookieEncoderUtil.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
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
|
||||
* the HTTP cookie version 0, 1, and 2.
|
||||
* <pre>
|
||||
* // Example
|
||||
* {@link HttpRequestHeader} req = ...;
|
||||
* {@link HttpRequest} req = ...;
|
||||
* res.setHeader("Set-Cookie", {@link ServerCookieEncoder}.encode("JSESSIONID", "1234"));
|
||||
* </pre>
|
||||
*
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
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.nio.charset.Charset;
|
||||
@ -46,8 +46,8 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
|
||||
/**
|
||||
* Keep all HttpDatas until cleanAllHttpDatas() is called.
|
||||
*/
|
||||
private final ConcurrentHashMap<HttpRequestHeader, List<HttpData>> requestFileDeleteMap =
|
||||
new ConcurrentHashMap<HttpRequestHeader, List<HttpData>>();
|
||||
private final ConcurrentHashMap<HttpRequest, List<HttpData>> requestFileDeleteMap =
|
||||
new ConcurrentHashMap<HttpRequest, List<HttpData>>();
|
||||
/**
|
||||
* HttpData will be in memory if less than default size (16KB).
|
||||
* The type will be Mixed.
|
||||
@ -79,7 +79,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
|
||||
/**
|
||||
* @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);
|
||||
if (list == null) {
|
||||
list = new ArrayList<HttpData>();
|
||||
@ -89,7 +89,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attribute createAttribute(HttpRequestHeader request, String name) {
|
||||
public Attribute createAttribute(HttpRequest request, String name) {
|
||||
if (useDisk) {
|
||||
Attribute attribute = new DiskAttribute(name);
|
||||
List<HttpData> fileToDelete = getList(request);
|
||||
@ -106,7 +106,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attribute createAttribute(HttpRequestHeader request, String name, String value) {
|
||||
public Attribute createAttribute(HttpRequest request, String name, String value) {
|
||||
if (useDisk) {
|
||||
Attribute attribute;
|
||||
try {
|
||||
@ -133,7 +133,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
|
||||
}
|
||||
|
||||
@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,
|
||||
long size) {
|
||||
if (useDisk) {
|
||||
@ -155,7 +155,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHttpDataFromClean(HttpRequestHeader request, InterfaceHttpData data) {
|
||||
public void removeHttpDataFromClean(HttpRequest request, InterfaceHttpData data) {
|
||||
if (data instanceof HttpData) {
|
||||
List<HttpData> fileToDelete = getList(request);
|
||||
fileToDelete.remove(data);
|
||||
@ -163,7 +163,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanRequestHttpDatas(HttpRequestHeader request) {
|
||||
public void cleanRequestHttpDatas(HttpRequest request) {
|
||||
List<HttpData> fileToDelete = requestFileDeleteMap.remove(request);
|
||||
if (fileToDelete != null) {
|
||||
for (HttpData data: fileToDelete) {
|
||||
@ -175,7 +175,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
|
||||
|
||||
@Override
|
||||
public void cleanAllHttpDatas() {
|
||||
for (HttpRequestHeader request : requestFileDeleteMap.keySet()) {
|
||||
for (HttpRequest request : requestFileDeleteMap.keySet()) {
|
||||
List<HttpData> fileToDelete = requestFileDeleteMap.get(request);
|
||||
if (fileToDelete != null) {
|
||||
for (HttpData data: fileToDelete) {
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
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;
|
||||
|
||||
@ -28,20 +28,20 @@ public interface HttpDataFactory {
|
||||
* @param request associated request
|
||||
* @return a new Attribute with no value
|
||||
*/
|
||||
Attribute createAttribute(HttpRequestHeader request, String name);
|
||||
Attribute createAttribute(HttpRequest request, String name);
|
||||
|
||||
/**
|
||||
* @param request associated request
|
||||
* @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 size the size of the Uploaded file
|
||||
* @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,
|
||||
long size);
|
||||
|
||||
@ -50,14 +50,14 @@ public interface HttpDataFactory {
|
||||
* is still a temporary one as setup at construction)
|
||||
* @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
|
||||
*
|
||||
* @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
|
||||
|
@ -16,11 +16,11 @@
|
||||
package io.netty.handler.codec.http.multipart;
|
||||
|
||||
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.HttpContent;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
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.multipart.HttpPostBodyUtil.SeekAheadNoBackArrayException;
|
||||
import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadOptimize;
|
||||
@ -50,7 +50,7 @@ public class HttpPostRequestDecoder {
|
||||
/**
|
||||
* Request to decode
|
||||
*/
|
||||
private final HttpRequestHeader request;
|
||||
private final HttpRequest request;
|
||||
|
||||
/**
|
||||
* Default charset to use
|
||||
@ -136,7 +136,7 @@ public class HttpPostRequestDecoder {
|
||||
* if the default charset was wrong when decoding or other
|
||||
* errors
|
||||
*/
|
||||
public HttpPostRequestDecoder(HttpRequestHeader request) throws ErrorDataDecoderException,
|
||||
public HttpPostRequestDecoder(HttpRequest request) throws ErrorDataDecoderException,
|
||||
IncompatibleDataDecoderException {
|
||||
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
|
||||
* errors
|
||||
*/
|
||||
public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequestHeader request) throws ErrorDataDecoderException,
|
||||
public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request) throws ErrorDataDecoderException,
|
||||
IncompatibleDataDecoderException {
|
||||
this(factory, request, HttpConstants.DEFAULT_CHARSET);
|
||||
}
|
||||
@ -176,7 +176,7 @@ public class HttpPostRequestDecoder {
|
||||
* if the default charset was wrong when decoding or other
|
||||
* errors
|
||||
*/
|
||||
public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequestHeader request, Charset charset)
|
||||
public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request, Charset charset)
|
||||
throws ErrorDataDecoderException, IncompatibleDataDecoderException {
|
||||
if (factory == null) {
|
||||
throw new NullPointerException("factory");
|
||||
@ -188,15 +188,15 @@ public class HttpPostRequestDecoder {
|
||||
throw new NullPointerException("charset");
|
||||
}
|
||||
this.request = request;
|
||||
HttpMethod method = request.getMethod();
|
||||
HttpMethod method = request.method();
|
||||
if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT) || method.equals(HttpMethod.PATCH)) {
|
||||
bodyToDecode = true;
|
||||
}
|
||||
this.charset = charset;
|
||||
this.factory = factory;
|
||||
// Fill default values
|
||||
if (this.request.containsHeader(HttpHeaders.Names.CONTENT_TYPE)) {
|
||||
checkMultipart(this.request.getHeader(HttpHeaders.Names.CONTENT_TYPE));
|
||||
if (this.request.headers().contains(HttpHeaders.Names.CONTENT_TYPE)) {
|
||||
checkMultipart(this.request.headers().get(HttpHeaders.Names.CONTENT_TYPE));
|
||||
} else {
|
||||
isMultipart = false;
|
||||
}
|
||||
@ -341,7 +341,7 @@ public class HttpPostRequestDecoder {
|
||||
* errors
|
||||
*/
|
||||
public void offer(HttpContent content) throws ErrorDataDecoderException {
|
||||
ByteBuf chunked = content.getContent();
|
||||
ByteBuf chunked = content.data();
|
||||
if (undecodedChunk == null) {
|
||||
undecodedChunk = chunked;
|
||||
} else {
|
||||
|
@ -18,11 +18,11 @@ package io.netty.handler.codec.http.multipart;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.MessageBuf;
|
||||
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.HttpContent;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.stream.ChunkedMessageInput;
|
||||
|
||||
import java.io.File;
|
||||
@ -49,7 +49,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
||||
/**
|
||||
* Request to encode
|
||||
*/
|
||||
private final HttpRequest request;
|
||||
private final FullHttpRequest request;
|
||||
|
||||
/**
|
||||
* Default charset to use
|
||||
@ -100,7 +100,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
||||
* @throws ErrorDataEncoderException
|
||||
* 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,
|
||||
HttpConstants.DEFAULT_CHARSET);
|
||||
}
|
||||
@ -118,7 +118,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
||||
* @throws ErrorDataEncoderException
|
||||
* 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 {
|
||||
this(factory, request, multipart, HttpConstants.DEFAULT_CHARSET);
|
||||
}
|
||||
@ -138,7 +138,8 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
||||
* @throws ErrorDataEncoderException
|
||||
* 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 {
|
||||
if (factory == null) {
|
||||
throw new NullPointerException("factory");
|
||||
@ -149,7 +150,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
||||
if (charset == null) {
|
||||
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");
|
||||
}
|
||||
this.request = request;
|
||||
@ -598,7 +599,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
||||
* @throws ErrorDataEncoderException
|
||||
* 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
|
||||
if (!headerFinalized) {
|
||||
if (isMultipart) {
|
||||
@ -617,10 +618,10 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
||||
} else {
|
||||
throw new ErrorDataEncoderException("Header already encoded");
|
||||
}
|
||||
List<String> contentTypes = request.getHeaders(HttpHeaders.Names.CONTENT_TYPE);
|
||||
List<String> transferEncoding = request.getHeaders(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||
List<String> contentTypes = request.headers().getAll(HttpHeaders.Names.CONTENT_TYPE);
|
||||
List<String> transferEncoding = request.headers().getAll(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||
if (contentTypes != null) {
|
||||
request.removeHeader(HttpHeaders.Names.CONTENT_TYPE);
|
||||
request.headers().remove(HttpHeaders.Names.CONTENT_TYPE);
|
||||
for (String contentType : contentTypes) {
|
||||
// "multipart/form-data; boundary=--89421926422648"
|
||||
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)) {
|
||||
// ignore
|
||||
} else {
|
||||
request.addHeader(HttpHeaders.Names.CONTENT_TYPE, contentType);
|
||||
request.headers().add(HttpHeaders.Names.CONTENT_TYPE, contentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isMultipart) {
|
||||
String value = HttpHeaders.Values.MULTIPART_FORM_DATA + "; " + HttpHeaders.Values.BOUNDARY + '='
|
||||
+ multipartDataBoundary;
|
||||
request.addHeader(HttpHeaders.Names.CONTENT_TYPE, value);
|
||||
request.headers().add(HttpHeaders.Names.CONTENT_TYPE, value);
|
||||
} else {
|
||||
// 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
|
||||
long realSize = globalBodySize;
|
||||
@ -648,25 +649,25 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
||||
realSize -= 1; // last '&' removed
|
||||
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) {
|
||||
isChunked = true;
|
||||
if (transferEncoding != null) {
|
||||
request.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||
request.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||
for (String v : transferEncoding) {
|
||||
if (v.equalsIgnoreCase(HttpHeaders.Values.CHUNKED)) {
|
||||
// ignore
|
||||
} else {
|
||||
request.addHeader(HttpHeaders.Names.TRANSFER_ENCODING, v);
|
||||
request.headers().add(HttpHeaders.Names.TRANSFER_ENCODING, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
HttpHeaders.setTransferEncodingChunked(request);
|
||||
request.setContent(EMPTY_BUFFER);
|
||||
request.data().clear();
|
||||
} else {
|
||||
// get the only one body and set it to the request
|
||||
HttpContent chunk = nextChunk();
|
||||
request.setContent(chunk.getContent());
|
||||
request.data().clear().writeBytes(chunk.data());
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ package io.netty.handler.codec.http.websocketx;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
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.util.Map;
|
||||
@ -148,5 +148,5 @@ public abstract class WebSocketClientHandshaker {
|
||||
* @param response
|
||||
* HTTP response containing the closing handshake details
|
||||
*/
|
||||
public abstract void finishHandshake(Channel channel, HttpResponse response);
|
||||
public abstract void finishHandshake(Channel channel, FullHttpResponse response);
|
||||
}
|
||||
|
@ -15,19 +15,18 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http.websocketx;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
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.Values;
|
||||
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.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseDecoder;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
@ -142,10 +141,10 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
||||
}
|
||||
|
||||
// Format request
|
||||
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
|
||||
request.addHeader(Names.UPGRADE, Values.WEBSOCKET);
|
||||
request.addHeader(Names.CONNECTION, Values.UPGRADE);
|
||||
request.addHeader(Names.HOST, wsURL.getHost());
|
||||
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
|
||||
request.headers().add(Names.UPGRADE, Values.WEBSOCKET);
|
||||
request.headers().add(Names.CONNECTION, Values.UPGRADE);
|
||||
request.headers().add(Names.HOST, wsURL.getHost());
|
||||
|
||||
int wsPort = wsURL.getPort();
|
||||
String originValue = "http://" + wsURL.getHost();
|
||||
@ -155,24 +154,24 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
||||
originValue = originValue + ':' + wsPort;
|
||||
}
|
||||
|
||||
request.addHeader(Names.ORIGIN, originValue);
|
||||
request.addHeader(Names.SEC_WEBSOCKET_KEY1, key1);
|
||||
request.addHeader(Names.SEC_WEBSOCKET_KEY2, key2);
|
||||
request.headers().add(Names.ORIGIN, originValue);
|
||||
request.headers().add(Names.SEC_WEBSOCKET_KEY1, key1);
|
||||
request.headers().add(Names.SEC_WEBSOCKET_KEY2, key2);
|
||||
String expectedSubprotocol = getExpectedSubprotocol();
|
||||
if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) {
|
||||
request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
|
||||
request.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
|
||||
}
|
||||
|
||||
if (customHeaders != null) {
|
||||
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.
|
||||
// See also: http://www.ietf.org/mail-archive/web/hybi/current/msg02149.html
|
||||
request.setHeader(Names.CONTENT_LENGTH, key3.length);
|
||||
request.setContent(Unpooled.copiedBuffer(key3));
|
||||
request.headers().set(Names.CONTENT_LENGTH, key3.length);
|
||||
request.data().writeBytes(key3);
|
||||
|
||||
ChannelFuture future = channel.write(request);
|
||||
future.addListener(new ChannelFutureListener() {
|
||||
@ -217,31 +216,31 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
||||
* @throws WebSocketHandshakeException
|
||||
*/
|
||||
@Override
|
||||
public void finishHandshake(Channel channel, HttpResponse response) {
|
||||
public void finishHandshake(Channel channel, FullHttpResponse response) {
|
||||
final HttpResponseStatus status = new HttpResponseStatus(101, "WebSocket Protocol Handshake");
|
||||
|
||||
if (!response.getStatus().equals(status)) {
|
||||
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus());
|
||||
if (!response.status().equals(status)) {
|
||||
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)) {
|
||||
throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
|
||||
+ upgrade);
|
||||
}
|
||||
|
||||
String connection = response.getHeader(Names.CONNECTION);
|
||||
String connection = response.headers().get(Names.CONNECTION);
|
||||
if (!Values.UPGRADE.equalsIgnoreCase(connection)) {
|
||||
throw new WebSocketHandshakeException("Invalid handshake response connection: "
|
||||
+ connection);
|
||||
}
|
||||
|
||||
byte[] challenge = response.getContent().array();
|
||||
byte[] challenge = response.data().array();
|
||||
if (!Arrays.equals(challenge, expectedChallengeResponseBytes)) {
|
||||
throw new WebSocketHandshakeException("Invalid challenge");
|
||||
}
|
||||
|
||||
String subprotocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||
String subprotocol = response.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||
setActualSubprotocol(subprotocol);
|
||||
|
||||
setHandshakeComplete();
|
||||
|
@ -20,13 +20,13 @@ import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
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.Values;
|
||||
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.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseDecoder;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
@ -124,11 +124,11 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
|
||||
}
|
||||
|
||||
// Format request
|
||||
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
|
||||
request.addHeader(Names.UPGRADE, Values.WEBSOCKET.toLowerCase());
|
||||
request.addHeader(Names.CONNECTION, Values.UPGRADE);
|
||||
request.addHeader(Names.SEC_WEBSOCKET_KEY, key);
|
||||
request.addHeader(Names.HOST, wsURL.getHost());
|
||||
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
|
||||
request.headers().add(Names.UPGRADE, Values.WEBSOCKET.toLowerCase());
|
||||
request.headers().add(Names.CONNECTION, Values.UPGRADE);
|
||||
request.headers().add(Names.SEC_WEBSOCKET_KEY, key);
|
||||
request.headers().add(Names.HOST, wsURL.getHost());
|
||||
|
||||
int wsPort = wsURL.getPort();
|
||||
String originValue = "http://" + wsURL.getHost();
|
||||
@ -137,18 +137,18 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
|
||||
// See http://tools.ietf.org/html/rfc6454#section-6.2
|
||||
originValue = originValue + ':' + wsPort;
|
||||
}
|
||||
request.addHeader(Names.SEC_WEBSOCKET_ORIGIN, originValue);
|
||||
request.headers().add(Names.SEC_WEBSOCKET_ORIGIN, originValue);
|
||||
|
||||
String expectedSubprotocol = getExpectedSubprotocol();
|
||||
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) {
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
public void finishHandshake(Channel channel, HttpResponse response) {
|
||||
public void finishHandshake(Channel channel, FullHttpResponse response) {
|
||||
final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
|
||||
|
||||
if (!response.getStatus().equals(status)) {
|
||||
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus());
|
||||
if (!response.status().equals(status)) {
|
||||
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)) {
|
||||
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)) {
|
||||
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)) {
|
||||
throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept,
|
||||
expectedChallengeResponseString));
|
||||
}
|
||||
|
||||
String subprotocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||
String subprotocol = response.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||
setActualSubprotocol(subprotocol);
|
||||
|
||||
setHandshakeComplete();
|
||||
|
@ -20,13 +20,13 @@ import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
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.Values;
|
||||
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.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseDecoder;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
@ -124,11 +124,11 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
|
||||
}
|
||||
|
||||
// Format request
|
||||
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
|
||||
request.addHeader(Names.UPGRADE, Values.WEBSOCKET.toLowerCase());
|
||||
request.addHeader(Names.CONNECTION, Values.UPGRADE);
|
||||
request.addHeader(Names.SEC_WEBSOCKET_KEY, key);
|
||||
request.addHeader(Names.HOST, wsURL.getHost());
|
||||
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
|
||||
request.headers().add(Names.UPGRADE, Values.WEBSOCKET.toLowerCase());
|
||||
request.headers().add(Names.CONNECTION, Values.UPGRADE);
|
||||
request.headers().add(Names.SEC_WEBSOCKET_KEY, key);
|
||||
request.headers().add(Names.HOST, wsURL.getHost());
|
||||
|
||||
int wsPort = wsURL.getPort();
|
||||
String originValue = "http://" + wsURL.getHost();
|
||||
@ -137,18 +137,18 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
|
||||
// See http://tools.ietf.org/html/rfc6454#section-6.2
|
||||
originValue = originValue + ':' + wsPort;
|
||||
}
|
||||
request.addHeader(Names.SEC_WEBSOCKET_ORIGIN, originValue);
|
||||
request.headers().add(Names.SEC_WEBSOCKET_ORIGIN, originValue);
|
||||
|
||||
String expectedSubprotocol = getExpectedSubprotocol();
|
||||
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) {
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
public void finishHandshake(Channel channel, HttpResponse response) {
|
||||
public void finishHandshake(Channel channel, FullHttpResponse response) {
|
||||
final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
|
||||
|
||||
if (!response.getStatus().equals(status)) {
|
||||
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus());
|
||||
if (!response.status().equals(status)) {
|
||||
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)) {
|
||||
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)) {
|
||||
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)) {
|
||||
throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept,
|
||||
expectedChallengeResponseString));
|
||||
}
|
||||
|
||||
String subprotocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||
String subprotocol = response.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||
setActualSubprotocol(subprotocol);
|
||||
|
||||
setHandshakeComplete();
|
||||
|
@ -20,13 +20,13 @@ import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
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.Values;
|
||||
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.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseDecoder;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
@ -124,11 +124,11 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
|
||||
}
|
||||
|
||||
// Format request
|
||||
HttpRequestHeader request = new DefaultHttpRequestHeader(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
|
||||
request.addHeader(Names.UPGRADE, Values.WEBSOCKET.toLowerCase());
|
||||
request.addHeader(Names.CONNECTION, Values.UPGRADE);
|
||||
request.addHeader(Names.SEC_WEBSOCKET_KEY, key);
|
||||
request.addHeader(Names.HOST, wsURL.getHost());
|
||||
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
|
||||
request.headers().add(Names.UPGRADE, Values.WEBSOCKET.toLowerCase());
|
||||
request.headers().add(Names.CONNECTION, Values.UPGRADE);
|
||||
request.headers().add(Names.SEC_WEBSOCKET_KEY, key);
|
||||
request.headers().add(Names.HOST, wsURL.getHost());
|
||||
|
||||
int wsPort = wsURL.getPort();
|
||||
String originValue = "http://" + wsURL.getHost();
|
||||
@ -137,18 +137,18 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
|
||||
// See http://tools.ietf.org/html/rfc6454#section-6.2
|
||||
originValue = originValue + ':' + wsPort;
|
||||
}
|
||||
request.addHeader(Names.SEC_WEBSOCKET_ORIGIN, originValue);
|
||||
request.headers().add(Names.SEC_WEBSOCKET_ORIGIN, originValue);
|
||||
|
||||
String expectedSubprotocol = getExpectedSubprotocol();
|
||||
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) {
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
public void finishHandshake(Channel channel, HttpResponse response) {
|
||||
public void finishHandshake(Channel channel, FullHttpResponse response) {
|
||||
final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
|
||||
|
||||
if (!response.getStatus().equals(status)) {
|
||||
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus());
|
||||
if (!response.status().equals(status)) {
|
||||
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)) {
|
||||
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)) {
|
||||
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)) {
|
||||
throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept,
|
||||
expectedChallengeResponseString));
|
||||
}
|
||||
|
||||
String subprotocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||
String subprotocol = response.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||
setActualSubprotocol(subprotocol);
|
||||
|
||||
setHandshakeComplete();
|
||||
|
@ -18,7 +18,7 @@ package io.netty.handler.codec.http.websocketx;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
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 java.util.Collections;
|
||||
@ -112,7 +112,7 @@ public abstract class WebSocketServerHandshaker {
|
||||
* @param req
|
||||
* HTTP Request
|
||||
*/
|
||||
public ChannelFuture handshake(Channel channel, HttpRequest req) {
|
||||
public ChannelFuture handshake(Channel channel, FullHttpRequest req) {
|
||||
if (channel == null) {
|
||||
throw new NullPointerException("channel");
|
||||
}
|
||||
@ -129,7 +129,7 @@ public abstract class WebSocketServerHandshaker {
|
||||
* @param promise
|
||||
* 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
|
||||
|
@ -22,13 +22,13 @@ import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
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.Names;
|
||||
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.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.logging.InternalLogger;
|
||||
@ -119,64 +119,63 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
|
||||
* HTTP request
|
||||
*/
|
||||
@Override
|
||||
public ChannelFuture handshake(Channel channel, HttpRequest req, ChannelPromise promise) {
|
||||
public ChannelFuture handshake(Channel channel, FullHttpRequest req, ChannelPromise promise) {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Channel %s WS Version 00 server handshake", channel.id()));
|
||||
}
|
||||
|
||||
// Serve the WebSocket handshake request.
|
||||
if (!Values.UPGRADE.equalsIgnoreCase(req.getHeader(CONNECTION))
|
||||
|| !WEBSOCKET.equalsIgnoreCase(req.getHeader(Names.UPGRADE))) {
|
||||
if (!Values.UPGRADE.equalsIgnoreCase(req.headers().get(CONNECTION))
|
||||
|| !WEBSOCKET.equalsIgnoreCase(req.headers().get(Names.UPGRADE))) {
|
||||
throw new WebSocketHandshakeException("not a WebSocket handshake request: missing upgrade");
|
||||
}
|
||||
|
||||
// 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.
|
||||
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"));
|
||||
res.addHeader(Names.UPGRADE, WEBSOCKET);
|
||||
res.addHeader(CONNECTION, Values.UPGRADE);
|
||||
res.headers().add(Names.UPGRADE, WEBSOCKET);
|
||||
res.headers().add(CONNECTION, Values.UPGRADE);
|
||||
|
||||
// Fill in the headers and contents depending on handshake method.
|
||||
if (isHixie76) {
|
||||
// New handshake method with a challenge:
|
||||
res.addHeader(SEC_WEBSOCKET_ORIGIN, req.getHeader(ORIGIN));
|
||||
res.addHeader(SEC_WEBSOCKET_LOCATION, getWebSocketUrl());
|
||||
String subprotocols = req.getHeader(SEC_WEBSOCKET_PROTOCOL);
|
||||
res.headers().add(SEC_WEBSOCKET_ORIGIN, req.headers().get(ORIGIN));
|
||||
res.headers().add(SEC_WEBSOCKET_LOCATION, getWebSocketUrl());
|
||||
String subprotocols = req.headers().get(SEC_WEBSOCKET_PROTOCOL);
|
||||
if (subprotocols != null) {
|
||||
String selectedSubprotocol = selectSubprotocol(subprotocols);
|
||||
if (selectedSubprotocol == null) {
|
||||
throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols);
|
||||
} else {
|
||||
res.addHeader(SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
|
||||
res.headers().add(SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
|
||||
setSelectedSubprotocol(selectedSubprotocol);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the answer of the challenge.
|
||||
String key1 = req.getHeader(SEC_WEBSOCKET_KEY1);
|
||||
String key2 = req.getHeader(SEC_WEBSOCKET_KEY2);
|
||||
String key1 = req.headers().get(SEC_WEBSOCKET_KEY1);
|
||||
String key2 = req.headers().get(SEC_WEBSOCKET_KEY2);
|
||||
int a = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key1).replaceAll("")) /
|
||||
BEGINNING_SPACE.matcher(key1).replaceAll("").length());
|
||||
int b = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key2).replaceAll("")) /
|
||||
BEGINNING_SPACE.matcher(key2).replaceAll("").length());
|
||||
long c = req.getContent().readLong();
|
||||
long c = req.data().readLong();
|
||||
ByteBuf input = Unpooled.buffer(16);
|
||||
input.writeInt(a);
|
||||
input.writeInt(b);
|
||||
input.writeLong(c);
|
||||
ByteBuf output = Unpooled.wrappedBuffer(WebSocketUtil.md5(input.array()));
|
||||
res.setContent(output);
|
||||
res.data().writeBytes(WebSocketUtil.md5(input.array()));
|
||||
} else {
|
||||
// Old Hixie 75 handshake method with no challenge:
|
||||
res.addHeader(WEBSOCKET_ORIGIN, req.getHeader(ORIGIN));
|
||||
res.addHeader(WEBSOCKET_LOCATION, getWebSocketUrl());
|
||||
String protocol = req.getHeader(WEBSOCKET_PROTOCOL);
|
||||
res.headers().add(WEBSOCKET_ORIGIN, req.headers().get(ORIGIN));
|
||||
res.headers().add(WEBSOCKET_LOCATION, getWebSocketUrl());
|
||||
String protocol = req.headers().get(WEBSOCKET_PROTOCOL);
|
||||
if (protocol != null) {
|
||||
res.addHeader(WEBSOCKET_PROTOCOL, selectSubprotocol(protocol));
|
||||
res.headers().add(WEBSOCKET_PROTOCOL, selectSubprotocol(protocol));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,12 +20,12 @@ import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
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.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.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.logging.InternalLogger;
|
||||
@ -109,15 +109,16 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker {
|
||||
* HTTP request
|
||||
*/
|
||||
@Override
|
||||
public ChannelFuture handshake(Channel channel, HttpRequest req, ChannelPromise promise) {
|
||||
public ChannelFuture handshake(Channel channel, FullHttpRequest req, ChannelPromise promise) {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
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) {
|
||||
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));
|
||||
}
|
||||
|
||||
res.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS);
|
||||
res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
||||
res.addHeader(Names.CONNECTION, Names.UPGRADE);
|
||||
res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept);
|
||||
String subprotocols = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||
res.headers().add(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
||||
res.headers().add(Names.CONNECTION, Names.UPGRADE);
|
||||
res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept);
|
||||
String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||
if (subprotocols != null) {
|
||||
String selectedSubprotocol = selectSubprotocol(subprotocols);
|
||||
if (selectedSubprotocol == null) {
|
||||
throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols);
|
||||
} else {
|
||||
res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
|
||||
res.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
|
||||
setSelectedSubprotocol(selectedSubprotocol);
|
||||
}
|
||||
}
|
||||
|
@ -20,12 +20,12 @@ import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.handler.codec.http.DefaultHttpResponseHeader;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
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.HttpResponseHeader;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.logging.InternalLogger;
|
||||
@ -110,15 +110,15 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
|
||||
* HTTP request
|
||||
*/
|
||||
@Override
|
||||
public ChannelFuture handshake(Channel channel, HttpRequest req, ChannelPromise promise) {
|
||||
public ChannelFuture handshake(Channel channel, FullHttpRequest req, ChannelPromise promise) {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
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) {
|
||||
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));
|
||||
}
|
||||
|
||||
res.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS);
|
||||
res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
||||
res.addHeader(Names.CONNECTION, Names.UPGRADE);
|
||||
res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept);
|
||||
String subprotocols = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||
res.headers().add(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
||||
res.headers().add(Names.CONNECTION, Names.UPGRADE);
|
||||
res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept);
|
||||
String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||
if (subprotocols != null) {
|
||||
String selectedSubprotocol = selectSubprotocol(subprotocols);
|
||||
if (selectedSubprotocol == null) {
|
||||
throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols);
|
||||
} else {
|
||||
res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
|
||||
res.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
|
||||
setSelectedSubprotocol(selectedSubprotocol);
|
||||
}
|
||||
}
|
||||
|
@ -20,12 +20,12 @@ import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.handler.codec.http.DefaultHttpResponseHeader;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
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.HttpResponseHeader;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.logging.InternalLogger;
|
||||
@ -109,15 +109,15 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
|
||||
* HTTP request
|
||||
*/
|
||||
@Override
|
||||
public ChannelFuture handshake(Channel channel, HttpRequest req, ChannelPromise promise) {
|
||||
public ChannelFuture handshake(Channel channel, FullHttpRequest req, ChannelPromise promise) {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
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) {
|
||||
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));
|
||||
}
|
||||
|
||||
res.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS);
|
||||
res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
||||
res.addHeader(Names.CONNECTION, Names.UPGRADE);
|
||||
res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept);
|
||||
String subprotocols = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||
res.headers().add(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
||||
res.headers().add(Names.CONNECTION, Names.UPGRADE);
|
||||
res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept);
|
||||
String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||
if (subprotocols != null) {
|
||||
String selectedSubprotocol = selectSubprotocol(subprotocols);
|
||||
if (selectedSubprotocol == null) {
|
||||
throw new WebSocketHandshakeException(
|
||||
"Requested subprotocol(s) not supported: " + subprotocols);
|
||||
} else {
|
||||
res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
|
||||
res.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
|
||||
setSelectedSubprotocol(selectedSubprotocol);
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,10 @@
|
||||
package io.netty.handler.codec.http.websocketx;
|
||||
|
||||
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.HttpRequestHeader;
|
||||
import io.netty.handler.codec.http.HttpResponseHeader;
|
||||
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.HttpVersion;
|
||||
|
||||
@ -81,9 +81,9 @@ public class WebSocketServerHandshakerFactory {
|
||||
* @return A new WebSocketServerHandshaker for the requested web socket version. Null if web
|
||||
* 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.equals(WebSocketVersion.V13.toHttpHeaderValue())) {
|
||||
// Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification).
|
||||
@ -113,11 +113,10 @@ public class WebSocketServerHandshakerFactory {
|
||||
* Channel
|
||||
*/
|
||||
public static void sendUnsupportedWebSocketVersionResponse(Channel channel) {
|
||||
HttpResponseHeader res = new DefaultHttpResponseHeader(
|
||||
HttpResponse res = new DefaultHttpResponse(
|
||||
HttpVersion.HTTP_1_1,
|
||||
HttpResponseStatus.SWITCHING_PROTOCOLS);
|
||||
res.setStatus(HttpResponseStatus.UPGRADE_REQUIRED);
|
||||
res.setHeader(Names.SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue());
|
||||
HttpResponseStatus.UPGRADE_REQUIRED);
|
||||
res.headers().set(Names.SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue());
|
||||
channel.write(res);
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.util.AttributeKey;
|
||||
|
||||
@ -92,8 +92,8 @@ public class WebSocketServerProtocolHandler extends ChannelInboundMessageHandler
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
if (cause instanceof WebSocketHandshakeException) {
|
||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.BAD_REQUEST);
|
||||
response.setContent(Unpooled.wrappedBuffer(cause.getMessage().getBytes()));
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||
HTTP_1_1, HttpResponseStatus.BAD_REQUEST, Unpooled.wrappedBuffer(cause.getMessage().getBytes()));
|
||||
ctx.channel().write(response).addListener(ChannelFutureListener.CLOSE);
|
||||
} else {
|
||||
ctx.close();
|
||||
@ -113,7 +113,8 @@ public class WebSocketServerProtocolHandler extends ChannelInboundMessageHandler
|
||||
@Override
|
||||
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
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);
|
||||
} else {
|
||||
ctx.nextInboundMessageBuffer().add(msg);
|
||||
|
@ -15,26 +15,28 @@
|
||||
*/
|
||||
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.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||
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.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpRequestHeader;
|
||||
import io.netty.handler.codec.http.HttpResponseHeader;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
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}.
|
||||
*/
|
||||
public class WebSocketServerProtocolHandshakeHandler extends ChannelInboundMessageHandlerAdapter<HttpRequest> {
|
||||
public class WebSocketServerProtocolHandshakeHandler
|
||||
extends ChannelInboundMessageHandlerAdapter<FullHttpRequest> {
|
||||
|
||||
private final String websocketPath;
|
||||
private final String subprotocols;
|
||||
@ -48,9 +50,9 @@ public class WebSocketServerProtocolHandshakeHandler extends ChannelInboundMessa
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageReceived(final ChannelHandlerContext ctx, HttpRequest req) throws Exception {
|
||||
if (req.getMethod() != GET) {
|
||||
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN));
|
||||
public void messageReceived(final ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
|
||||
if (req.method() != GET) {
|
||||
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
|
||||
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);
|
||||
if (!isKeepAlive(req) || res.getStatus().getCode() != 200) {
|
||||
if (!isKeepAlive(req) || res.status().code() != 200) {
|
||||
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";
|
||||
if (cp.get(SslHandler.class) != null) {
|
||||
// SSL in use so use Secure WebSockets
|
||||
protocol = "wss";
|
||||
}
|
||||
return protocol + "://" + req.getHeader(HttpHeaders.Names.HOST) + path;
|
||||
return protocol + "://" + req.headers().get(HttpHeaders.Names.HOST) + path;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,13 +19,13 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.embedded.EmbeddedMessageChannel;
|
||||
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.HttpObjectDecoder;
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* <h3>Parameters that prevents excessive memory consumption</h3>
|
||||
* <table border="1">
|
||||
@ -84,14 +84,14 @@ public abstract class RtspObjectDecoder extends HttpObjectDecoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isContentAlwaysEmpty(HttpHeader msg) {
|
||||
protected boolean isContentAlwaysEmpty(HttpMessage msg) {
|
||||
// Unlike HTTP, RTSP always assumes zero-length body if Content-Length
|
||||
// header is absent.
|
||||
boolean empty = super.isContentAlwaysEmpty(msg);
|
||||
if (empty) {
|
||||
return true;
|
||||
}
|
||||
if (!msg.containsHeader(RtspHeaders.Names.CONTENT_LENGTH)) {
|
||||
if (!msg.headers().contains(RtspHeaders.Names.CONTENT_LENGTH)) {
|
||||
return true;
|
||||
}
|
||||
return empty;
|
||||
|
@ -17,20 +17,19 @@ package io.netty.handler.codec.rtsp;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.UnsupportedMessageTypeException;
|
||||
import io.netty.handler.codec.http.HttpHeader;
|
||||
import io.netty.handler.codec.http.FullHttpMessage;
|
||||
import io.netty.handler.codec.http.HttpMessage;
|
||||
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}.
|
||||
|
||||
*
|
||||
* @apiviz.landmark
|
||||
*/
|
||||
@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.
|
||||
@ -39,13 +38,7 @@ public abstract class RtspObjectEncoder<H extends HttpHeader> extends HttpObject
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, Object msg,
|
||||
ByteBuf out) throws Exception {
|
||||
// Ignore unrelated message types such as HttpChunk.
|
||||
if (!(msg instanceof HttpHeader)) {
|
||||
throw new UnsupportedMessageTypeException(msg, HttpHeader.class);
|
||||
}
|
||||
|
||||
super.encode(ctx, msg, out);
|
||||
public boolean isEncodable(Object msg) throws Exception {
|
||||
return msg instanceof FullHttpMessage;
|
||||
}
|
||||
}
|
||||
|
@ -17,12 +17,12 @@ package io.netty.handler.codec.rtsp;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.TooLongFrameException;
|
||||
import io.netty.handler.codec.http.DefaultHttpRequestHeader;
|
||||
import io.netty.handler.codec.http.HttpHeader;
|
||||
import io.netty.handler.codec.http.DefaultHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpMessage;
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* <h3>Parameters that prevents excessive memory consumption</h3>
|
||||
* <table border="1">
|
||||
@ -65,14 +65,14 @@ public class RtspRequestDecoder extends RtspObjectDecoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpHeader createMessage(String[] initialLine) throws Exception {
|
||||
return new DefaultHttpRequestHeader(RtspVersions.valueOf(initialLine[2]),
|
||||
protected HttpMessage createMessage(String[] initialLine) throws Exception {
|
||||
return new DefaultHttpRequest(RtspVersions.valueOf(initialLine[2]),
|
||||
RtspMethods.valueOf(initialLine[0]), initialLine[1]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpHeader createInvalidMessage() {
|
||||
return new DefaultHttpRequestHeader(RtspVersions.RTSP_1_0, RtspMethods.OPTIONS, "/bad-request");
|
||||
protected HttpMessage createInvalidMessage() {
|
||||
return new DefaultHttpRequest(RtspVersions.RTSP_1_0, RtspMethods.OPTIONS, "/bad-request");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -16,24 +16,30 @@
|
||||
package io.netty.handler.codec.rtsp;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Encodes an RTSP request represented in {@link HttpRequestHeader} into
|
||||
* Encodes an RTSP request represented in {@link FullHttpRequest} into
|
||||
* a {@link ByteBuf}.
|
||||
|
||||
*/
|
||||
public class RtspRequestEncoder extends RtspObjectEncoder<HttpRequestHeader> {
|
||||
public class RtspRequestEncoder extends RtspObjectEncoder<HttpRequest> {
|
||||
|
||||
@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 {
|
||||
buf.writeBytes(request.getMethod().toString().getBytes(CharsetUtil.US_ASCII));
|
||||
buf.writeBytes(request.method().toString().getBytes(CharsetUtil.US_ASCII));
|
||||
buf.writeByte((byte) ' ');
|
||||
buf.writeBytes(request.getUri().getBytes(CharsetUtil.UTF_8));
|
||||
buf.writeBytes(request.uri().getBytes(CharsetUtil.UTF_8));
|
||||
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) '\n');
|
||||
}
|
||||
|
@ -17,13 +17,13 @@ package io.netty.handler.codec.rtsp;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.TooLongFrameException;
|
||||
import io.netty.handler.codec.http.DefaultHttpResponseHeader;
|
||||
import io.netty.handler.codec.http.HttpHeader;
|
||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpMessage;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* <h3>Parameters that prevents excessive memory consumption</h3>
|
||||
* <table border="1">
|
||||
@ -69,15 +69,15 @@ public class RtspResponseDecoder extends RtspObjectDecoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpHeader createMessage(String[] initialLine) throws Exception {
|
||||
return new DefaultHttpResponseHeader(
|
||||
protected HttpMessage createMessage(String[] initialLine) throws Exception {
|
||||
return new DefaultHttpResponse(
|
||||
RtspVersions.valueOf(initialLine[0]),
|
||||
new HttpResponseStatus(Integer.valueOf(initialLine[1]), initialLine[2]));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpHeader createInvalidMessage() {
|
||||
return new DefaultHttpResponseHeader(RtspVersions.RTSP_1_0, UNKNOWN_STATUS);
|
||||
protected HttpMessage createInvalidMessage() {
|
||||
return new DefaultHttpResponse(RtspVersions.RTSP_1_0, UNKNOWN_STATUS);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -16,25 +16,30 @@
|
||||
package io.netty.handler.codec.rtsp;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.http.HttpHeader;
|
||||
import io.netty.handler.codec.http.HttpResponseHeader;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
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}.
|
||||
|
||||
*/
|
||||
public class RtspResponseEncoder extends RtspObjectEncoder<HttpResponseHeader> {
|
||||
public class RtspResponseEncoder extends RtspObjectEncoder<HttpResponse> {
|
||||
|
||||
@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 {
|
||||
buf.writeBytes(response.getProtocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
|
||||
buf.writeBytes(response.protocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
|
||||
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.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) '\n');
|
||||
}
|
||||
|
@ -196,9 +196,9 @@ public class SpdyHeaders {
|
||||
*/
|
||||
public static void setMethod(int spdyVersion, SpdyHeaderBlock block, HttpMethod method) {
|
||||
if (spdyVersion < 3) {
|
||||
block.setHeader(Spdy2HttpNames.METHOD, method.getName());
|
||||
block.setHeader(Spdy2HttpNames.METHOD, method.name());
|
||||
} 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));
|
||||
String reasonPhrase = status.substring(space + 1);
|
||||
HttpResponseStatus responseStatus = HttpResponseStatus.valueOf(code);
|
||||
if (responseStatus.getReasonPhrase().equals(reasonPhrase)) {
|
||||
if (responseStatus.reasonPhrase().equals(reasonPhrase)) {
|
||||
return responseStatus;
|
||||
} else {
|
||||
return new HttpResponseStatus(code, reasonPhrase);
|
||||
|
@ -16,18 +16,17 @@
|
||||
package io.netty.handler.codec.spdy;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||
import io.netty.handler.codec.TooLongFrameException;
|
||||
import io.netty.handler.codec.http.DefaultHttpRequest;
|
||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpHeader;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
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.HttpMessage;
|
||||
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.HttpVersion;
|
||||
|
||||
@ -36,13 +35,13 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* 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> {
|
||||
|
||||
private final int spdyVersion;
|
||||
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.
|
||||
@ -98,20 +97,21 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||
}
|
||||
|
||||
try {
|
||||
HttpResponse httpResponse = createHttpResponse(spdyVersion, spdySynStreamFrame);
|
||||
FullHttpResponse httpResponseWithEntity =
|
||||
createHttpResponse(spdyVersion, spdySynStreamFrame);
|
||||
|
||||
// Set the Stream-ID, Associated-To-Stream-ID, Priority, and URL as headers
|
||||
SpdyHttpHeaders.setStreamId(httpResponse, streamID);
|
||||
SpdyHttpHeaders.setAssociatedToStreamId(httpResponse, associatedToStreamId);
|
||||
SpdyHttpHeaders.setPriority(httpResponse, spdySynStreamFrame.getPriority());
|
||||
SpdyHttpHeaders.setUrl(httpResponse, URL);
|
||||
SpdyHttpHeaders.setStreamId(httpResponseWithEntity, streamID);
|
||||
SpdyHttpHeaders.setAssociatedToStreamId(httpResponseWithEntity, associatedToStreamId);
|
||||
SpdyHttpHeaders.setPriority(httpResponseWithEntity, spdySynStreamFrame.getPriority());
|
||||
SpdyHttpHeaders.setUrl(httpResponseWithEntity, URL);
|
||||
|
||||
if (spdySynStreamFrame.isLast()) {
|
||||
HttpHeaders.setContentLength(httpResponse, 0);
|
||||
return httpResponse;
|
||||
HttpHeaders.setContentLength(httpResponseWithEntity, 0);
|
||||
return httpResponseWithEntity;
|
||||
} else {
|
||||
// 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) {
|
||||
SpdyRstStreamFrame spdyRstStreamFrame =
|
||||
@ -121,16 +121,16 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||
} else {
|
||||
// SYN_STREAM frames initiated by the client are HTTP requests
|
||||
try {
|
||||
HttpRequest httpRequest = createHttpRequest(spdyVersion, spdySynStreamFrame);
|
||||
FullHttpRequest httpRequestWithEntity = createHttpRequest(spdyVersion, spdySynStreamFrame);
|
||||
|
||||
// Set the Stream-ID as a header
|
||||
SpdyHttpHeaders.setStreamId(httpRequest, streamID);
|
||||
SpdyHttpHeaders.setStreamId(httpRequestWithEntity, streamID);
|
||||
|
||||
if (spdySynStreamFrame.isLast()) {
|
||||
return httpRequest;
|
||||
return httpRequestWithEntity;
|
||||
} else {
|
||||
// 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) {
|
||||
// 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();
|
||||
|
||||
try {
|
||||
HttpResponse httpResponse = createHttpResponse(spdyVersion, spdySynReplyFrame);
|
||||
FullHttpResponse httpResponseWithEntity = createHttpResponse(spdyVersion, spdySynReplyFrame);
|
||||
|
||||
// Set the Stream-ID as a header
|
||||
SpdyHttpHeaders.setStreamId(httpResponse, streamID);
|
||||
SpdyHttpHeaders.setStreamId(httpResponseWithEntity, streamID);
|
||||
|
||||
if (spdySynReplyFrame.isLast()) {
|
||||
HttpHeaders.setContentLength(httpResponse, 0);
|
||||
return httpResponse;
|
||||
HttpHeaders.setContentLength(httpResponseWithEntity, 0);
|
||||
return httpResponseWithEntity;
|
||||
} else {
|
||||
// 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) {
|
||||
// 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;
|
||||
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.
|
||||
// SpdySessionHandler should prevent this from happening.
|
||||
@ -183,22 +183,22 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
|
||||
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.
|
||||
// SpdySessionHandler should prevent this from happening.
|
||||
if (httpMessage == null) {
|
||||
if (fullHttpMessage == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ByteBuf content = httpMessage.getContent();
|
||||
ByteBuf content = fullHttpMessage.data();
|
||||
if (content.readableBytes() > maxContentLength - spdyDataFrame.getData().readableBytes()) {
|
||||
messageMap.remove(streamID);
|
||||
throw new TooLongFrameException(
|
||||
@ -207,18 +207,12 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||
|
||||
ByteBuf spdyDataFrameData = spdyDataFrame.getData();
|
||||
int spdyDataFrameDataLen = spdyDataFrameData.readableBytes();
|
||||
if (content == Unpooled.EMPTY_BUFFER) {
|
||||
content = Unpooled.buffer(spdyDataFrameDataLen);
|
||||
content.writeBytes(spdyDataFrameData, spdyDataFrameData.readerIndex(), spdyDataFrameDataLen);
|
||||
httpMessage.setContent(content);
|
||||
} else {
|
||||
content.writeBytes(spdyDataFrameData, spdyDataFrameData.readerIndex(), spdyDataFrameDataLen);
|
||||
}
|
||||
content.writeBytes(spdyDataFrameData, spdyDataFrameData.readerIndex(), spdyDataFrameDataLen);
|
||||
|
||||
if (spdyDataFrame.isLast()) {
|
||||
HttpHeaders.setContentLength(httpMessage, content.readableBytes());
|
||||
HttpHeaders.setContentLength(fullHttpMessage, content.readableBytes());
|
||||
messageMap.remove(streamID);
|
||||
return httpMessage;
|
||||
return fullHttpMessage;
|
||||
}
|
||||
|
||||
} else if (msg instanceof SpdyRstStreamFrame) {
|
||||
@ -231,7 +225,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static HttpRequest createHttpRequest(int spdyVersion, SpdyHeaderBlock requestFrame)
|
||||
private static FullHttpRequest createHttpRequest(int spdyVersion, SpdyHeaderBlock requestFrame)
|
||||
throws Exception {
|
||||
// Create the first line of the request from the name/value pairs
|
||||
HttpMethod method = SpdyHeaders.getMethod(spdyVersion, requestFrame);
|
||||
@ -241,7 +235,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||
SpdyHeaders.removeUrl(spdyVersion, requestFrame);
|
||||
SpdyHeaders.removeVersion(spdyVersion, requestFrame);
|
||||
|
||||
HttpRequest httpRequest = new DefaultHttpRequest(httpVersion, method, url);
|
||||
FullHttpRequest httpRequestWithEntity = new DefaultFullHttpRequest(httpVersion, method, url);
|
||||
|
||||
// Remove the scheme header
|
||||
SpdyHeaders.removeScheme(spdyVersion, requestFrame);
|
||||
@ -250,23 +244,23 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||
// Replace the SPDY host header with the HTTP host header
|
||||
String host = SpdyHeaders.getHost(requestFrame);
|
||||
SpdyHeaders.removeHost(requestFrame);
|
||||
HttpHeaders.setHost(httpRequest, host);
|
||||
HttpHeaders.setHost(httpRequestWithEntity, host);
|
||||
}
|
||||
|
||||
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
|
||||
HttpHeaders.setKeepAlive(httpRequest, true);
|
||||
HttpHeaders.setKeepAlive(httpRequestWithEntity, true);
|
||||
|
||||
// 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 {
|
||||
// Create the first line of the response from the name/value pairs
|
||||
HttpResponseStatus status = SpdyHeaders.getStatus(spdyVersion, responseFrame);
|
||||
@ -274,18 +268,18 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||
SpdyHeaders.removeStatus(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()) {
|
||||
httpResponse.addHeader(e.getKey(), e.getValue());
|
||||
httpResponseWithEntity.headers().add(e.getKey(), e.getValue());
|
||||
}
|
||||
|
||||
// The Connection and Keep-Alive headers are no longer valid
|
||||
HttpHeaders.setKeepAlive(httpResponse, true);
|
||||
HttpHeaders.setKeepAlive(httpResponseWithEntity, true);
|
||||
|
||||
// Transfer-Encoding header is not valid
|
||||
httpResponse.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||
httpResponse.removeHeader(HttpHeaders.Names.TRAILER);
|
||||
httpResponseWithEntity.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||
httpResponseWithEntity.headers().remove(HttpHeaders.Names.TRAILER);
|
||||
|
||||
return httpResponse;
|
||||
return httpResponseWithEntity;
|
||||
}
|
||||
}
|
||||
|
@ -18,26 +18,26 @@ package io.netty.handler.codec.spdy;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||
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.HttpHeader;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
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.HttpMessage;
|
||||
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.List;
|
||||
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.
|
||||
*
|
||||
* <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>
|
||||
* <tr>
|
||||
* <th>Header Name</th><th>Header Value</th>
|
||||
@ -58,7 +58,7 @@ import java.util.Map;
|
||||
*
|
||||
* <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>
|
||||
* <tr>
|
||||
* <th>Header Name</th><th>Header Value</th>
|
||||
@ -71,7 +71,7 @@ import java.util.Map;
|
||||
*
|
||||
* <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>
|
||||
* <tr>
|
||||
* <th>Header Name</th><th>Header Value</th>
|
||||
@ -112,8 +112,8 @@ import java.util.Map;
|
||||
* <h3>Chunked Content</h3>
|
||||
*
|
||||
* This encoder associates all {@link HttpContent}s that it receives
|
||||
* with the most recently received 'chunked' {@link HttpRequestHeader}
|
||||
* or {@link HttpResponseHeader}.
|
||||
* with the most recently received 'chunked' {@link HttpRequest}
|
||||
* or {@link HttpResponse}.
|
||||
*
|
||||
* <h3>Pushed Resources</h3>
|
||||
*
|
||||
@ -144,16 +144,16 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
|
||||
public Object encode(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
|
||||
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);
|
||||
out.add(spdySynStreamFrame);
|
||||
}
|
||||
if (msg instanceof HttpResponseHeader) {
|
||||
if (msg instanceof HttpResponse) {
|
||||
|
||||
HttpResponseHeader httpResponse = (HttpResponseHeader) msg;
|
||||
if (httpResponse.containsHeader(SpdyHttpHeaders.Names.ASSOCIATED_TO_STREAM_ID)) {
|
||||
HttpResponse httpResponse = (HttpResponse) msg;
|
||||
if (httpResponse.headers().contains(SpdyHttpHeaders.Names.ASSOCIATED_TO_STREAM_ID)) {
|
||||
SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpResponse);
|
||||
out.add(spdySynStreamFrame);
|
||||
} else {
|
||||
@ -165,12 +165,12 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
|
||||
|
||||
HttpContent chunk = (HttpContent) msg;
|
||||
SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(currentStreamId);
|
||||
spdyDataFrame.setData(chunk.getContent());
|
||||
spdyDataFrame.setData(chunk.data());
|
||||
spdyDataFrame.setLast(chunk instanceof LastHttpContent);
|
||||
|
||||
if (chunk instanceof LastHttpContent) {
|
||||
LastHttpContent trailer = (LastHttpContent) chunk;
|
||||
List<Map.Entry<String, String>> trailers = trailer.getHeaders();
|
||||
List<Map.Entry<String, String>> trailers = trailer.trailingHeaders().entries();
|
||||
if (trailers.isEmpty()) {
|
||||
out.add(spdyDataFrame);
|
||||
} else {
|
||||
@ -194,7 +194,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
|
||||
return out.toArray();
|
||||
}
|
||||
|
||||
private SpdySynStreamFrame createSynStreamFrame(HttpHeader httpMessage)
|
||||
private SpdySynStreamFrame createSynStreamFrame(HttpMessage httpMessage)
|
||||
throws Exception {
|
||||
// Get the Stream-ID, Associated-To-Stream-ID, Priority, URL, and scheme from the headers
|
||||
int streamID = SpdyHttpHeaders.getStreamId(httpMessage);
|
||||
@ -210,33 +210,33 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
|
||||
|
||||
// The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding
|
||||
// headers are not valid and MUST not be sent.
|
||||
httpMessage.removeHeader(HttpHeaders.Names.CONNECTION);
|
||||
httpMessage.removeHeader("Keep-Alive");
|
||||
httpMessage.removeHeader("Proxy-Connection");
|
||||
httpMessage.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||
httpMessage.headers().remove(HttpHeaders.Names.CONNECTION);
|
||||
httpMessage.headers().remove("Keep-Alive");
|
||||
httpMessage.headers().remove("Proxy-Connection");
|
||||
httpMessage.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||
|
||||
SpdySynStreamFrame spdySynStreamFrame =
|
||||
new DefaultSpdySynStreamFrame(streamID, associatedToStreamId, priority);
|
||||
|
||||
// Unfold the first line of the message into name/value pairs
|
||||
if (httpMessage instanceof HttpRequest) {
|
||||
HttpRequestHeader httpRequest = (HttpRequestHeader) httpMessage;
|
||||
SpdyHeaders.setMethod(spdyVersion, spdySynStreamFrame, httpRequest.getMethod());
|
||||
SpdyHeaders.setUrl(spdyVersion, spdySynStreamFrame, httpRequest.getUri());
|
||||
SpdyHeaders.setVersion(spdyVersion, spdySynStreamFrame, httpMessage.getProtocolVersion());
|
||||
if (httpMessage instanceof FullHttpRequest) {
|
||||
HttpRequest httpRequest = (HttpRequest) httpMessage;
|
||||
SpdyHeaders.setMethod(spdyVersion, spdySynStreamFrame, httpRequest.method());
|
||||
SpdyHeaders.setUrl(spdyVersion, spdySynStreamFrame, httpRequest.uri());
|
||||
SpdyHeaders.setVersion(spdyVersion, spdySynStreamFrame, httpMessage.protocolVersion());
|
||||
}
|
||||
if (httpMessage instanceof HttpResponseHeader) {
|
||||
HttpResponseHeader httpResponse = (HttpResponseHeader) httpMessage;
|
||||
SpdyHeaders.setStatus(spdyVersion, spdySynStreamFrame, httpResponse.getStatus());
|
||||
if (httpMessage instanceof HttpResponse) {
|
||||
HttpResponse httpResponse = (HttpResponse) httpMessage;
|
||||
SpdyHeaders.setStatus(spdyVersion, spdySynStreamFrame, httpResponse.status());
|
||||
SpdyHeaders.setUrl(spdyVersion, spdySynStreamFrame, URL);
|
||||
SpdyHeaders.setVersion(spdyVersion, spdySynStreamFrame, httpMessage.getProtocolVersion());
|
||||
SpdyHeaders.setVersion(spdyVersion, spdySynStreamFrame, httpMessage.protocolVersion());
|
||||
spdySynStreamFrame.setUnidirectional(true);
|
||||
}
|
||||
|
||||
// Replace the HTTP host header with the SPDY host header
|
||||
if (spdyVersion >= 3) {
|
||||
String host = HttpHeaders.getHost(httpMessage);
|
||||
httpMessage.removeHeader(HttpHeaders.Names.HOST);
|
||||
httpMessage.headers().remove(HttpHeaders.Names.HOST);
|
||||
SpdyHeaders.setHost(spdySynStreamFrame, host);
|
||||
}
|
||||
|
||||
@ -247,7 +247,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
|
||||
SpdyHeaders.setScheme(spdyVersion, spdySynStreamFrame, scheme);
|
||||
|
||||
// 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());
|
||||
}
|
||||
currentStreamId = spdySynStreamFrame.getStreamId();
|
||||
@ -255,7 +255,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
|
||||
return spdySynStreamFrame;
|
||||
}
|
||||
|
||||
private SpdySynReplyFrame createSynReplyFrame(HttpResponseHeader httpResponse)
|
||||
private SpdySynReplyFrame createSynReplyFrame(HttpResponse httpResponse)
|
||||
throws Exception {
|
||||
boolean chunked = HttpHeaders.isTransferEncodingChunked(httpResponse);
|
||||
|
||||
@ -265,19 +265,19 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
|
||||
|
||||
// The Connection, Keep-Alive, Proxy-Connection, and Transfer-ENcoding
|
||||
// headers are not valid and MUST not be sent.
|
||||
httpResponse.removeHeader(HttpHeaders.Names.CONNECTION);
|
||||
httpResponse.removeHeader("Keep-Alive");
|
||||
httpResponse.removeHeader("Proxy-Connection");
|
||||
httpResponse.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||
httpResponse.headers().remove(HttpHeaders.Names.CONNECTION);
|
||||
httpResponse.headers().remove("Keep-Alive");
|
||||
httpResponse.headers().remove("Proxy-Connection");
|
||||
httpResponse.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||
|
||||
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);
|
||||
|
||||
// Unfold the first line of the response into name/value pairs
|
||||
SpdyHeaders.setStatus(spdyVersion, spdySynReplyFrame, httpResponse.getStatus());
|
||||
SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, httpResponse.getProtocolVersion());
|
||||
SpdyHeaders.setStatus(spdyVersion, spdySynReplyFrame, httpResponse.status());
|
||||
SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, httpResponse.protocolVersion());
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,8 @@
|
||||
*/
|
||||
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.HttpMessage;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static void removeStreamId(HttpHeader message) {
|
||||
message.removeHeader(Names.STREAM_ID);
|
||||
public static void removeStreamId(HttpMessage message) {
|
||||
message.headers().remove(Names.STREAM_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the {@code "X-SPDY-Associated-To-Stream-ID"} header.
|
||||
*/
|
||||
public static void removeAssociatedToStreamId(HttpHeader message) {
|
||||
message.removeHeader(Names.ASSOCIATED_TO_STREAM_ID);
|
||||
public static void removeAssociatedToStreamId(HttpMessage message) {
|
||||
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
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the {@code "X-SPDY-Priority"} header.
|
||||
*/
|
||||
public static void removePriority(HttpHeader message) {
|
||||
message.removeHeader(Names.PRIORITY);
|
||||
public static void removePriority(HttpMessage message) {
|
||||
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
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the {@code "X-SPDY-URL"} header.
|
||||
*/
|
||||
public static void removeUrl(HttpHeader message) {
|
||||
message.removeHeader(Names.URL);
|
||||
public static void removeUrl(HttpMessage message) {
|
||||
message.headers().remove(Names.URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the {@code "X-SPDY-URL"} header.
|
||||
*/
|
||||
public static String getUrl(HttpHeader message) {
|
||||
return message.getHeader(Names.URL);
|
||||
public static String getUrl(HttpMessage message) {
|
||||
return message.headers().get(Names.URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code "X-SPDY-URL"} header.
|
||||
*/
|
||||
public static void setUrl(HttpHeader message, String url) {
|
||||
message.setHeader(Names.URL, url);
|
||||
public static void setUrl(HttpMessage message, String url) {
|
||||
message.headers().set(Names.URL, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the {@code "X-SPDY-Scheme"} header.
|
||||
*/
|
||||
public static void removeScheme(HttpHeader message) {
|
||||
message.removeHeader(Names.SCHEME);
|
||||
public static void removeScheme(HttpMessage message) {
|
||||
message.headers().remove(Names.SCHEME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the {@code "X-SPDY-Scheme"} header.
|
||||
*/
|
||||
public static String getScheme(HttpHeader message) {
|
||||
return message.getHeader(Names.SCHEME);
|
||||
public static String getScheme(HttpMessage message) {
|
||||
return message.headers().get(Names.SCHEME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code "X-SPDY-Scheme"} header.
|
||||
*/
|
||||
public static void setScheme(HttpHeader message, String scheme) {
|
||||
message.setHeader(Names.SCHEME, scheme);
|
||||
public static void setScheme(HttpMessage message, String scheme) {
|
||||
message.headers().set(Names.SCHEME, scheme);
|
||||
}
|
||||
}
|
||||
|
@ -15,31 +15,31 @@
|
||||
*/
|
||||
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.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 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.
|
||||
*/
|
||||
public class SpdyHttpResponseStreamIdHandler extends
|
||||
MessageToMessageCodec<Object, HttpHeader> {
|
||||
MessageToMessageCodec<Object, HttpMessage> {
|
||||
private static final Integer NO_ID = -1;
|
||||
private final Queue<Integer> ids = new LinkedList<Integer>();
|
||||
|
||||
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
|
||||
protected Object encode(ChannelHandlerContext ctx, HttpHeader msg) throws Exception {
|
||||
protected Object encode(ChannelHandlerContext ctx, HttpMessage msg) throws Exception {
|
||||
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);
|
||||
}
|
||||
return msg;
|
||||
@ -47,12 +47,12 @@ public class SpdyHttpResponseStreamIdHandler extends
|
||||
|
||||
@Override
|
||||
protected Object decode(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
if (msg instanceof HttpHeader) {
|
||||
boolean contains = ((HttpHeader) msg).containsHeader(SpdyHttpHeaders.Names.STREAM_ID);
|
||||
if (msg instanceof HttpMessage) {
|
||||
boolean contains = ((HttpMessage) msg).headers().contains(SpdyHttpHeaders.Names.STREAM_ID);
|
||||
if (!contains) {
|
||||
ids.add(NO_ID);
|
||||
} else {
|
||||
ids.add(SpdyHttpHeaders.getStreamId((HttpHeader) msg));
|
||||
ids.add(SpdyHttpHeaders.getStreamId((HttpMessage) msg));
|
||||
}
|
||||
} else if (msg instanceof SpdyRstStreamFrame) {
|
||||
ids.remove(((SpdyRstStreamFrame) msg).getStreamId());
|
||||
|
@ -22,25 +22,25 @@ public class DefaultHttpRequestTest {
|
||||
|
||||
@Test
|
||||
public void testHeaderRemoval() {
|
||||
HttpHeader m = new DefaultHttpRequestHeader(
|
||||
HttpMessage m = new DefaultHttpRequest(
|
||||
HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
|
||||
|
||||
// Insert sample keys.
|
||||
for (int i = 0; i < 1000; i ++) {
|
||||
m.setHeader(String.valueOf(i), "");
|
||||
m.headers().set(String.valueOf(i), "");
|
||||
}
|
||||
|
||||
// Remove in reversed order.
|
||||
for (int i = 999; i >= 0; i --) {
|
||||
m.removeHeader(String.valueOf(i));
|
||||
m.headers().remove(String.valueOf(i));
|
||||
}
|
||||
|
||||
// Check if random access returns nothing.
|
||||
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.
|
||||
Assert.assertTrue(m.getHeaders().isEmpty());
|
||||
Assert.assertTrue(m.headers().isEmpty());
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.CompositeByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
@ -23,20 +22,21 @@ import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.embedded.EmbeddedMessageChannel;
|
||||
import io.netty.handler.codec.TooLongFrameException;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.easymock.EasyMock;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HttpObjectAggregatorTest {
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class FullHttpMessageDecoderTest {
|
||||
|
||||
@Test
|
||||
public void testAggregate() {
|
||||
HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024);
|
||||
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");
|
||||
HttpHeaders.setHeader(message, "X-Test", true);
|
||||
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
|
||||
assertTrue(embedder.writeInbound(chunk3));
|
||||
assertTrue(embedder.finish());
|
||||
DefaultHttpRequest aggratedMessage = (DefaultHttpRequest) embedder.readInbound();
|
||||
DefaultFullHttpRequest aggratedMessage = (DefaultFullHttpRequest) embedder.readInbound();
|
||||
assertNotNull(aggratedMessage);
|
||||
|
||||
assertEquals(chunk1.getContent().readableBytes() + chunk2.getContent().readableBytes(), HttpHeaders.getContentLength(aggratedMessage));
|
||||
assertEquals(aggratedMessage.getHeader("X-Test"), Boolean.TRUE.toString());
|
||||
assertEquals(chunk1.data().readableBytes() + chunk2.data().readableBytes(), HttpHeaders.getContentLength(aggratedMessage));
|
||||
assertEquals(aggratedMessage.headers().get("X-Test"), Boolean.TRUE.toString());
|
||||
checkContentBuffer(aggratedMessage);
|
||||
assertNull(embedder.readInbound());
|
||||
|
||||
}
|
||||
|
||||
private static void checkContentBuffer(DefaultHttpRequest aggregatedMessage) {
|
||||
CompositeByteBuf buffer = (CompositeByteBuf) aggregatedMessage.getContent();
|
||||
private static void checkContentBuffer(DefaultFullHttpRequest aggregatedMessage) {
|
||||
CompositeByteBuf buffer = (CompositeByteBuf) aggregatedMessage.data();
|
||||
assertEquals(2, buffer.numComponents());
|
||||
List<ByteBuf> buffers = buffer.decompose(0, buffer.capacity());
|
||||
assertEquals(2, buffers.size());
|
||||
@ -74,14 +74,14 @@ public class HttpObjectAggregatorTest {
|
||||
public void testAggregateWithTrailer() {
|
||||
HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024);
|
||||
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");
|
||||
HttpHeaders.setHeader(message, "X-Test", true);
|
||||
HttpHeaders.setTransferEncodingChunked(message);
|
||||
HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII));
|
||||
HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII));
|
||||
LastHttpContent trailer = new DefaultLastHttpContent();
|
||||
trailer.setHeader("X-Trailer", true);
|
||||
trailer.trailingHeaders().set("X-Trailer", true);
|
||||
|
||||
assertFalse(embedder.writeInbound(message));
|
||||
assertFalse(embedder.writeInbound(chunk1));
|
||||
@ -90,12 +90,12 @@ public class HttpObjectAggregatorTest {
|
||||
// this should trigger a messageReceived event so return true
|
||||
assertTrue(embedder.writeInbound(trailer));
|
||||
assertTrue(embedder.finish());
|
||||
DefaultHttpRequest aggratedMessage = (DefaultHttpRequest) embedder.readInbound();
|
||||
DefaultFullHttpRequest aggratedMessage = (DefaultFullHttpRequest) embedder.readInbound();
|
||||
assertNotNull(aggratedMessage);
|
||||
|
||||
assertEquals(chunk1.getContent().readableBytes() + chunk2.getContent().readableBytes(), HttpHeaders.getContentLength(aggratedMessage));
|
||||
assertEquals(aggratedMessage.getHeader("X-Test"), Boolean.TRUE.toString());
|
||||
assertEquals(aggratedMessage.getHeader("X-Trailer"), Boolean.TRUE.toString());
|
||||
assertEquals(chunk1.data().readableBytes() + chunk2.data().readableBytes(), HttpHeaders.getContentLength(aggratedMessage));
|
||||
assertEquals(aggratedMessage.headers().get("X-Test"), Boolean.TRUE.toString());
|
||||
assertEquals(aggratedMessage.headers().get("X-Trailer"), Boolean.TRUE.toString());
|
||||
checkContentBuffer(aggratedMessage);
|
||||
|
||||
assertNull(embedder.readInbound());
|
||||
@ -107,7 +107,7 @@ public class HttpObjectAggregatorTest {
|
||||
public void testTooLongFrameException() {
|
||||
HttpObjectAggregator aggr = new HttpObjectAggregator(4);
|
||||
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");
|
||||
HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII));
|
||||
HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII));
|
@ -15,15 +15,15 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.embedded.EmbeddedByteChannel;
|
||||
import io.netty.handler.codec.CodecException;
|
||||
import io.netty.handler.codec.PrematureChannelClosureException;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
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"
|
||||
@ -38,7 +38,7 @@ public class HttpClientCodecTest {
|
||||
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true);
|
||||
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.finish();
|
||||
}
|
||||
@ -48,7 +48,7 @@ public class HttpClientCodecTest {
|
||||
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true);
|
||||
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.finish();
|
||||
}
|
||||
@ -58,7 +58,7 @@ public class HttpClientCodecTest {
|
||||
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true);
|
||||
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());
|
||||
|
||||
try {
|
||||
@ -75,7 +75,7 @@ public class HttpClientCodecTest {
|
||||
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true);
|
||||
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));
|
||||
|
||||
try {
|
||||
|
@ -33,8 +33,8 @@ public class HttpInvalidMessageTest {
|
||||
public void testRequestWithBadInitialLine() throws Exception {
|
||||
EmbeddedByteChannel ch = new EmbeddedByteChannel(new HttpRequestDecoder());
|
||||
ch.writeInbound(Unpooled.copiedBuffer("GET / HTTP/1.0 with extra\r\n", CharsetUtil.UTF_8));
|
||||
HttpRequestHeader req = (HttpRequestHeader) ch.readInbound();
|
||||
DecoderResult dr = req.getDecoderResult();
|
||||
HttpRequest req = (HttpRequest) ch.readInbound();
|
||||
DecoderResult dr = req.decoderResult();
|
||||
Assert.assertFalse(dr.isSuccess());
|
||||
Assert.assertFalse(dr.isPartialFailure());
|
||||
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("Bad=Name: Bad Value\r\n", CharsetUtil.UTF_8));
|
||||
ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.UTF_8));
|
||||
HttpRequestHeader req = (HttpRequestHeader) ch.readInbound();
|
||||
DecoderResult dr = req.getDecoderResult();
|
||||
HttpRequest req = (HttpRequest) ch.readInbound();
|
||||
DecoderResult dr = req.decoderResult();
|
||||
Assert.assertFalse(dr.isSuccess());
|
||||
Assert.assertTrue(dr.isPartialFailure());
|
||||
Assert.assertEquals("Good Value", req.getHeader("Good_Name"));
|
||||
Assert.assertEquals("/maybe-something", req.getUri());
|
||||
Assert.assertEquals("Good Value", req.headers().get("Good_Name"));
|
||||
Assert.assertEquals("/maybe-something", req.uri());
|
||||
ensureInboundTrafficDiscarded(ch);
|
||||
}
|
||||
|
||||
@ -60,8 +60,8 @@ public class HttpInvalidMessageTest {
|
||||
public void testResponseWithBadInitialLine() throws Exception {
|
||||
EmbeddedByteChannel ch = new EmbeddedByteChannel(new HttpResponseDecoder());
|
||||
ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.0 BAD_CODE Bad Server\r\n", CharsetUtil.UTF_8));
|
||||
HttpResponseHeader res = (HttpResponseHeader) ch.readInbound();
|
||||
DecoderResult dr = res.getDecoderResult();
|
||||
HttpResponse res = (HttpResponse) ch.readInbound();
|
||||
DecoderResult dr = res.decoderResult();
|
||||
Assert.assertFalse(dr.isSuccess());
|
||||
Assert.assertFalse(dr.isPartialFailure());
|
||||
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("Bad=Name: Bad Value\r\n", CharsetUtil.UTF_8));
|
||||
ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.UTF_8));
|
||||
HttpResponseHeader res = (HttpResponseHeader) ch.readInbound();
|
||||
DecoderResult dr = res.getDecoderResult();
|
||||
HttpResponse res = (HttpResponse) ch.readInbound();
|
||||
DecoderResult dr = res.decoderResult();
|
||||
Assert.assertFalse(dr.isSuccess());
|
||||
Assert.assertTrue(dr.isPartialFailure());
|
||||
Assert.assertEquals("Maybe OK", res.getStatus().getReasonPhrase());
|
||||
Assert.assertEquals("Good Value", res.getHeader("Good_Name"));
|
||||
Assert.assertEquals("Maybe OK", res.status().reasonPhrase());
|
||||
Assert.assertEquals("Good Value", res.headers().get("Good_Name"));
|
||||
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("BAD_LENGTH\r\n", CharsetUtil.UTF_8));
|
||||
|
||||
HttpRequestHeader req = (HttpRequestHeader) ch.readInbound();
|
||||
Assert.assertTrue(req.getDecoderResult().isSuccess());
|
||||
HttpRequest req = (HttpRequest) ch.readInbound();
|
||||
Assert.assertTrue(req.decoderResult().isSuccess());
|
||||
|
||||
HttpContent chunk = (HttpContent) ch.readInbound();
|
||||
DecoderResult dr = chunk.getDecoderResult();
|
||||
DecoderResult dr = chunk.decoderResult();
|
||||
Assert.assertFalse(dr.isSuccess());
|
||||
Assert.assertFalse(dr.isPartialFailure());
|
||||
ensureInboundTrafficDiscarded(ch);
|
||||
|
@ -15,14 +15,14 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class HttpRequestEncoderTest {
|
||||
@ -31,7 +31,7 @@ public class HttpRequestEncoderTest {
|
||||
public void testUriWithoutPath() throws Exception {
|
||||
HttpRequestEncoder encoder = new HttpRequestEncoder();
|
||||
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"));
|
||||
String req = buffer.toString(Charset.forName("US-ASCII"));
|
||||
assertEquals("GET http://localhost/ HTTP/1.1\r\n", req);
|
||||
@ -42,7 +42,7 @@ public class HttpRequestEncoderTest {
|
||||
public void testUriWithPath() throws Exception {
|
||||
HttpRequestEncoder encoder = new HttpRequestEncoder();
|
||||
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/"));
|
||||
String req = buffer.toString(Charset.forName("US-ASCII"));
|
||||
assertEquals("GET http://localhost/ HTTP/1.1\r\n", req);
|
||||
|
@ -19,7 +19,6 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.embedded.EmbeddedByteChannel;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -44,8 +43,8 @@ public class HttpServerCodecTest {
|
||||
decoderEmbedder.writeInbound(prepareDataChunk(offeredContentLength));
|
||||
decoderEmbedder.finish();
|
||||
|
||||
HttpHeader httpMessage = (HttpHeader) decoderEmbedder.readInbound();
|
||||
//Assert.assertSame(HttpTransferEncoding.STREAMED, httpMessage.getTransferEncoding());
|
||||
HttpMessage httpMessage = (HttpMessage) decoderEmbedder.readInbound();
|
||||
Assert.assertNotNull(httpMessage);
|
||||
|
||||
boolean empty = true;
|
||||
int totalBytesPolled = 0;
|
||||
@ -55,7 +54,7 @@ public class HttpServerCodecTest {
|
||||
break;
|
||||
}
|
||||
empty = false;
|
||||
totalBytesPolled += httpChunk.getContent().readableBytes();
|
||||
totalBytesPolled += httpChunk.data().readableBytes();
|
||||
Assert.assertFalse(httpChunk instanceof LastHttpContent);
|
||||
}
|
||||
Assert.assertFalse(empty);
|
||||
|
@ -15,15 +15,15 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http.websocketx;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Values.WEBSOCKET;
|
||||
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
||||
|
||||
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.Names;
|
||||
import io.netty.handler.codec.http.HttpMethod;
|
||||
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.HttpHeaders.Names;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Values.*;
|
||||
import static io.netty.handler.codec.http.HttpVersion.*;
|
||||
|
||||
public class WebSocketRequestBuilder {
|
||||
|
||||
@ -96,30 +96,30 @@ public class WebSocketRequestBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest build() {
|
||||
HttpRequest req = new DefaultHttpRequest(httpVersion, method, uri);
|
||||
public FullHttpRequest build() {
|
||||
FullHttpRequest req = new DefaultFullHttpRequest(httpVersion, method, uri);
|
||||
if (host != null) {
|
||||
req.setHeader(Names.HOST, host);
|
||||
req.headers().set(Names.HOST, host);
|
||||
}
|
||||
if (upgrade != null) {
|
||||
req.setHeader(Names.UPGRADE, upgrade);
|
||||
req.headers().set(Names.UPGRADE, upgrade);
|
||||
}
|
||||
if (connection != null) {
|
||||
req.setHeader(Names.CONNECTION, connection);
|
||||
req.headers().set(Names.CONNECTION, connection);
|
||||
}
|
||||
if (key != null) {
|
||||
req.setHeader(Names.SEC_WEBSOCKET_KEY, key);
|
||||
req.headers().set(Names.SEC_WEBSOCKET_KEY, key);
|
||||
}
|
||||
if (origin != null) {
|
||||
req.setHeader(Names.SEC_WEBSOCKET_ORIGIN, origin);
|
||||
req.headers().set(Names.SEC_WEBSOCKET_ORIGIN, origin);
|
||||
}
|
||||
if (version != null) {
|
||||
req.setHeader(Names.SEC_WEBSOCKET_VERSION, version.toHttpHeaderValue());
|
||||
req.headers().set(Names.SEC_WEBSOCKET_VERSION, version.toHttpHeaderValue());
|
||||
}
|
||||
return req;
|
||||
}
|
||||
|
||||
public static HttpRequestHeader sucessful() {
|
||||
public static HttpRequest sucessful() {
|
||||
return new WebSocketRequestBuilder().httpVersion(HTTP_1_1)
|
||||
.method(HttpMethod.GET)
|
||||
.uri("/test")
|
||||
|
@ -15,26 +15,26 @@
|
||||
*/
|
||||
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.Unpooled;
|
||||
import io.netty.channel.embedded.EmbeddedByteChannel;
|
||||
import io.netty.handler.codec.http.DefaultHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||
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.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseDecoder;
|
||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
import io.netty.handler.codec.http.HttpResponseHeader;
|
||||
import io.netty.handler.codec.http.LastHttpContent;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Values.*;
|
||||
import static io.netty.handler.codec.http.HttpVersion.*;
|
||||
|
||||
public class WebSocketServerHandshaker00Test {
|
||||
|
||||
@Test
|
||||
@ -42,17 +42,16 @@ public class WebSocketServerHandshaker00Test {
|
||||
EmbeddedByteChannel ch = new EmbeddedByteChannel(
|
||||
new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder());
|
||||
|
||||
HttpRequest req = new DefaultHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat");
|
||||
req.setHeader(Names.HOST, "server.example.com");
|
||||
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");
|
||||
FullHttpRequest req = new DefaultFullHttpRequest(
|
||||
HTTP_1_1, HttpMethod.GET, "/chat", Unpooled.copiedBuffer("^n:ds[4U", CharsetUtil.US_ASCII));
|
||||
|
||||
ByteBuf buffer = Unpooled.copiedBuffer("^n:ds[4U", CharsetUtil.US_ASCII);
|
||||
req.setContent(buffer);
|
||||
req.headers().set(Names.HOST, "server.example.com");
|
||||
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(
|
||||
"ws://example.com/chat", "chat", Integer.MAX_VALUE).handshake(ch, req);
|
||||
@ -61,12 +60,12 @@ public class WebSocketServerHandshaker00Test {
|
||||
|
||||
EmbeddedByteChannel ch2 = new EmbeddedByteChannel(new HttpResponseDecoder());
|
||||
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("chat", res.getHeader(Names.SEC_WEBSOCKET_PROTOCOL));
|
||||
Assert.assertEquals("ws://example.com/chat", res.headers().get(Names.SEC_WEBSOCKET_LOCATION));
|
||||
Assert.assertEquals("chat", res.headers().get(Names.SEC_WEBSOCKET_PROTOCOL));
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -15,23 +15,23 @@
|
||||
*/
|
||||
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.channel.embedded.EmbeddedByteChannel;
|
||||
import io.netty.handler.codec.http.DefaultHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||
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.HttpResponseHeader;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseDecoder;
|
||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Values.*;
|
||||
import static io.netty.handler.codec.http.HttpVersion.*;
|
||||
|
||||
public class WebSocketServerHandshaker08Test {
|
||||
|
||||
@Test
|
||||
@ -39,14 +39,14 @@ public class WebSocketServerHandshaker08Test {
|
||||
EmbeddedByteChannel ch = new EmbeddedByteChannel(
|
||||
new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder());
|
||||
|
||||
HttpRequest req = new DefaultHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat");
|
||||
req.setHeader(Names.HOST, "server.example.com");
|
||||
req.setHeader(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
||||
req.setHeader(Names.CONNECTION, "Upgrade");
|
||||
req.setHeader(Names.SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ==");
|
||||
req.setHeader(Names.SEC_WEBSOCKET_ORIGIN, "http://example.com");
|
||||
req.setHeader(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
|
||||
req.setHeader(Names.SEC_WEBSOCKET_VERSION, "8");
|
||||
FullHttpRequest req = new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat");
|
||||
req.headers().set(Names.HOST, "server.example.com");
|
||||
req.headers().set(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
||||
req.headers().set(Names.CONNECTION, "Upgrade");
|
||||
req.headers().set(Names.SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ==");
|
||||
req.headers().set(Names.SEC_WEBSOCKET_ORIGIN, "http://example.com");
|
||||
req.headers().set(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
|
||||
req.headers().set(Names.SEC_WEBSOCKET_VERSION, "8");
|
||||
|
||||
new WebSocketServerHandshaker08(
|
||||
"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());
|
||||
ch2.writeInbound(resBuf);
|
||||
HttpResponseHeader res = (HttpResponseHeader) ch2.readInbound();
|
||||
HttpResponse res = (HttpResponse) ch2.readInbound();
|
||||
|
||||
Assert.assertEquals(
|
||||
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.getHeader(Names.SEC_WEBSOCKET_ACCEPT));
|
||||
Assert.assertEquals("chat", res.getHeader(Names.SEC_WEBSOCKET_PROTOCOL));
|
||||
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.headers().get(Names.SEC_WEBSOCKET_ACCEPT));
|
||||
Assert.assertEquals("chat", res.headers().get(Names.SEC_WEBSOCKET_PROTOCOL));
|
||||
}
|
||||
}
|
||||
|
@ -15,23 +15,23 @@
|
||||
*/
|
||||
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.channel.embedded.EmbeddedByteChannel;
|
||||
import io.netty.handler.codec.http.DefaultHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||
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.HttpResponseHeader;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseDecoder;
|
||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Values.*;
|
||||
import static io.netty.handler.codec.http.HttpVersion.*;
|
||||
|
||||
public class WebSocketServerHandshaker13Test {
|
||||
|
||||
@Test
|
||||
@ -39,14 +39,14 @@ public class WebSocketServerHandshaker13Test {
|
||||
EmbeddedByteChannel ch = new EmbeddedByteChannel(
|
||||
new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder());
|
||||
|
||||
HttpRequest req = new DefaultHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat");
|
||||
req.setHeader(Names.HOST, "server.example.com");
|
||||
req.setHeader(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
||||
req.setHeader(Names.CONNECTION, "Upgrade");
|
||||
req.setHeader(Names.SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ==");
|
||||
req.setHeader(Names.SEC_WEBSOCKET_ORIGIN, "http://example.com");
|
||||
req.setHeader(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
|
||||
req.setHeader(Names.SEC_WEBSOCKET_VERSION, "13");
|
||||
FullHttpRequest req = new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat");
|
||||
req.headers().set(Names.HOST, "server.example.com");
|
||||
req.headers().set(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
||||
req.headers().set(Names.CONNECTION, "Upgrade");
|
||||
req.headers().set(Names.SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ==");
|
||||
req.headers().set(Names.SEC_WEBSOCKET_ORIGIN, "http://example.com");
|
||||
req.headers().set(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
|
||||
req.headers().set(Names.SEC_WEBSOCKET_VERSION, "13");
|
||||
|
||||
new WebSocketServerHandshaker13(
|
||||
"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());
|
||||
ch2.writeInbound(resBuf);
|
||||
HttpResponseHeader res = (HttpResponseHeader) ch2.readInbound();
|
||||
HttpResponse res = (HttpResponse) ch2.readInbound();
|
||||
|
||||
Assert.assertEquals(
|
||||
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.getHeader(Names.SEC_WEBSOCKET_ACCEPT));
|
||||
Assert.assertEquals("chat", res.getHeader(Names.SEC_WEBSOCKET_PROTOCOL));
|
||||
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.headers().get(Names.SEC_WEBSOCKET_ACCEPT));
|
||||
Assert.assertEquals("chat", res.headers().get(Names.SEC_WEBSOCKET_PROTOCOL));
|
||||
}
|
||||
}
|
||||
|
@ -22,13 +22,13 @@ import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||
import io.netty.channel.ChannelOutboundMessageHandlerAdapter;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
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.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpRequestHeader;
|
||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseHeader;
|
||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -46,7 +46,7 @@ public class WebSocketServerProtocolHandlerTest {
|
||||
|
||||
writeUpgradeRequest(ch);
|
||||
|
||||
assertEquals(SWITCHING_PROTOCOLS, ((HttpResponseHeader) ch.outboundMessageBuffer().poll()).getStatus());
|
||||
assertEquals(SWITCHING_PROTOCOLS, ((HttpResponse) ch.outboundMessageBuffer().poll()).status());
|
||||
assertNotNull(WebSocketServerProtocolHandler.getHandshaker(handshakerCtx));
|
||||
}
|
||||
|
||||
@ -55,16 +55,16 @@ public class WebSocketServerProtocolHandlerTest {
|
||||
EmbeddedMessageChannel ch = createChannel(new MockOutboundHandler());
|
||||
|
||||
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"));
|
||||
assertEquals(FORBIDDEN, ((HttpResponseHeader) ch.outboundMessageBuffer().poll()).getStatus());
|
||||
ch.writeInbound(new DefaultHttpRequest(HTTP_1_1, HttpMethod.GET, "/test"));
|
||||
assertEquals(FORBIDDEN, ((HttpResponse) ch.outboundMessageBuffer().poll()).status());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpUpgradeRequestInvalidUpgradeHeader() {
|
||||
EmbeddedMessageChannel ch = createChannel();
|
||||
HttpRequest httpRequest = new WebSocketRequestBuilder().httpVersion(HTTP_1_1)
|
||||
FullHttpRequest httpRequestWithEntity = new WebSocketRequestBuilder().httpVersion(HTTP_1_1)
|
||||
.method(HttpMethod.GET)
|
||||
.uri("/test")
|
||||
.connection("Upgrade")
|
||||
@ -72,10 +72,10 @@ public class WebSocketServerProtocolHandlerTest {
|
||||
.upgrade("BogusSocket")
|
||||
.build();
|
||||
|
||||
ch.writeInbound(httpRequest);
|
||||
ch.writeInbound(httpRequestWithEntity);
|
||||
|
||||
HttpResponse response = getHttpResponse(ch);
|
||||
assertEquals(BAD_REQUEST, response.getStatus());
|
||||
FullHttpResponse response = getHttpResponse(ch);
|
||||
assertEquals(BAD_REQUEST, response.status());
|
||||
assertEquals("not a WebSocket handshake request: missing upgrade", getResponseMessage(response));
|
||||
|
||||
}
|
||||
@ -83,7 +83,7 @@ public class WebSocketServerProtocolHandlerTest {
|
||||
@Test
|
||||
public void testHttpUpgradeRequestMissingWSKeyHeader() {
|
||||
EmbeddedMessageChannel ch = createChannel();
|
||||
HttpRequestHeader httpRequest = new WebSocketRequestBuilder().httpVersion(HTTP_1_1)
|
||||
HttpRequest httpRequest = new WebSocketRequestBuilder().httpVersion(HTTP_1_1)
|
||||
.method(HttpMethod.GET)
|
||||
.uri("/test")
|
||||
.key(null)
|
||||
@ -94,8 +94,8 @@ public class WebSocketServerProtocolHandlerTest {
|
||||
|
||||
ch.writeInbound(httpRequest);
|
||||
|
||||
HttpResponse response = getHttpResponse(ch);
|
||||
assertEquals(BAD_REQUEST, response.getStatus());
|
||||
FullHttpResponse response = getHttpResponse(ch);
|
||||
assertEquals(BAD_REQUEST, response.status());
|
||||
assertEquals("not a WebSocket request: missing key", getResponseMessage(response));
|
||||
}
|
||||
|
||||
@ -129,13 +129,13 @@ public class WebSocketServerProtocolHandlerTest {
|
||||
ch.writeInbound(WebSocketRequestBuilder.sucessful());
|
||||
}
|
||||
|
||||
private static String getResponseMessage(HttpResponse response) {
|
||||
return new String(response.getContent().array());
|
||||
private static String getResponseMessage(FullHttpResponse response) {
|
||||
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();
|
||||
return (HttpResponse) outbound.poll();
|
||||
return (FullHttpResponse) outbound.poll();
|
||||
}
|
||||
|
||||
private static class MockOutboundHandler extends ChannelOutboundMessageHandlerAdapter<Object> {
|
||||
|
@ -20,10 +20,10 @@ import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
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.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.stream.ChunkedFile;
|
||||
import io.netty.util.CharsetUtil;
|
||||
@ -94,7 +94,7 @@ import static io.netty.handler.codec.http.HttpVersion.*;
|
||||
*
|
||||
* </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_GMT_TIMEZONE = "GMT";
|
||||
@ -102,19 +102,19 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
|
||||
|
||||
@Override
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.getMethod() != GET) {
|
||||
if (request.method() != GET) {
|
||||
sendError(ctx, METHOD_NOT_ALLOWED);
|
||||
return;
|
||||
}
|
||||
|
||||
final String uri = request.getUri();
|
||||
final String uri = request.uri();
|
||||
final String path = sanitizeUri(uri);
|
||||
if (path == null) {
|
||||
sendError(ctx, FORBIDDEN);
|
||||
@ -142,7 +142,7 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
|
||||
}
|
||||
|
||||
// Cache Validation
|
||||
String ifModifiedSince = request.getHeader(IF_MODIFIED_SINCE);
|
||||
String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE);
|
||||
if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) {
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
|
||||
Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);
|
||||
@ -166,12 +166,12 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
|
||||
}
|
||||
long fileLength = raf.length();
|
||||
|
||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
|
||||
setContentLength(response, fileLength);
|
||||
setContentTypeHeader(response, file);
|
||||
setDateAndCacheHeaders(response, file);
|
||||
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.
|
||||
@ -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 void sendListing(ChannelHandlerContext ctx, File dir) {
|
||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
|
||||
response.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8");
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
|
||||
response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
|
||||
|
||||
StringBuilder buf = new StringBuilder();
|
||||
String dirPath = dir.getPath();
|
||||
@ -270,26 +270,24 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
|
||||
|
||||
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.
|
||||
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
private static void sendRedirect(ChannelHandlerContext ctx, String newUri) {
|
||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, FOUND);
|
||||
response.setHeader(LOCATION, newUri);
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND);
|
||||
response.headers().set(LOCATION, newUri);
|
||||
|
||||
// Close the connection as soon as the error message is sent.
|
||||
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
|
||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, status);
|
||||
response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||
response.setContent(Unpooled.copiedBuffer(
|
||||
"Failure: " + status.toString() + "\r\n",
|
||||
CharsetUtil.UTF_8));
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||
HTTP_1_1, status, Unpooled.copiedBuffer("Failure: " + status.toString() + "\r\n", CharsetUtil.UTF_8));
|
||||
response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||
|
||||
// Close the connection as soon as the error message is sent.
|
||||
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
|
||||
@ -302,7 +300,7 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
|
||||
* Context
|
||||
*/
|
||||
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);
|
||||
|
||||
// Close the connection as soon as the error message is sent.
|
||||
@ -315,12 +313,12 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
|
||||
* @param response
|
||||
* HTTP response
|
||||
*/
|
||||
private static void setDateHeader(HttpResponse response) {
|
||||
private static void setDateHeader(FullHttpResponse response) {
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));
|
||||
|
||||
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
|
||||
* 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);
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));
|
||||
|
||||
// Date header
|
||||
Calendar time = new GregorianCalendar();
|
||||
response.setHeader(DATE, dateFormatter.format(time.getTime()));
|
||||
response.headers().set(DATE, dateFormatter.format(time.getTime()));
|
||||
|
||||
// Add cache headers
|
||||
time.add(Calendar.SECOND, HTTP_CACHE_SECONDS);
|
||||
response.setHeader(EXPIRES, dateFormatter.format(time.getTime()));
|
||||
response.setHeader(CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);
|
||||
response.setHeader(
|
||||
response.headers().set(EXPIRES, dateFormatter.format(time.getTime()));
|
||||
response.headers().set(CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);
|
||||
response.headers().set(
|
||||
LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified())));
|
||||
}
|
||||
|
||||
@ -355,9 +353,9 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
|
||||
* @param file
|
||||
* 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();
|
||||
response.setHeader(CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath()));
|
||||
response.headers().set(CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath()));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,10 +21,10 @@ import io.netty.channel.socket.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.http.ClientCookieEncoder;
|
||||
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.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpRequestHeader;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
@ -73,14 +73,14 @@ public class HttpSnoopClient {
|
||||
Channel ch = b.connect().sync().channel();
|
||||
|
||||
// Prepare the HTTP request.
|
||||
HttpRequestHeader request = new DefaultHttpRequestHeader(
|
||||
HttpRequest request = new DefaultHttpRequest(
|
||||
HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getRawPath());
|
||||
request.setHeader(HttpHeaders.Names.HOST, host);
|
||||
request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
|
||||
request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
|
||||
request.headers().set(HttpHeaders.Names.HOST, host);
|
||||
request.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
|
||||
request.headers().set(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
|
||||
|
||||
// Set some example cookies.
|
||||
request.setHeader(
|
||||
request.headers().set(
|
||||
HttpHeaders.Names.COOKIE,
|
||||
ClientCookieEncoder.encode(
|
||||
new DefaultCookie("my-cookie", "foo"),
|
||||
|
@ -19,7 +19,7 @@ import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||
import io.netty.handler.codec.http.HttpContent;
|
||||
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.util.CharsetUtil;
|
||||
|
||||
@ -27,16 +27,16 @@ public class HttpSnoopClientHandler extends ChannelInboundMessageHandlerAdapter<
|
||||
|
||||
@Override
|
||||
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
if (msg instanceof HttpResponseHeader) {
|
||||
HttpResponseHeader response = (HttpResponseHeader) msg;
|
||||
if (msg instanceof HttpResponse) {
|
||||
HttpResponse response = (HttpResponse) msg;
|
||||
|
||||
System.out.println("STATUS: " + response.getStatus());
|
||||
System.out.println("VERSION: " + response.getProtocolVersion());
|
||||
System.out.println("STATUS: " + response.status());
|
||||
System.out.println("VERSION: " + response.protocolVersion());
|
||||
System.out.println();
|
||||
|
||||
if (!response.getHeaderNames().isEmpty()) {
|
||||
for (String name: response.getHeaderNames()) {
|
||||
for (String value: response.getHeaders(name)) {
|
||||
if (!response.headers().isEmpty()) {
|
||||
for (String name: response.headers().names()) {
|
||||
for (String value: response.headers().getAll(name)) {
|
||||
System.out.println("HEADER: " + name + " = " + value);
|
||||
}
|
||||
}
|
||||
@ -52,7 +52,7 @@ public class HttpSnoopClientHandler extends ChannelInboundMessageHandlerAdapter<
|
||||
if (msg instanceof HttpContent) {
|
||||
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();
|
||||
|
||||
if (content instanceof LastHttpContent) {
|
||||
|
@ -24,15 +24,15 @@ import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.handler.codec.http.Cookie;
|
||||
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.DefaultHttpResponseHeader;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpContent;
|
||||
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.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.ServerCookieEncoder;
|
||||
import io.netty.util.CharsetUtil;
|
||||
@ -49,15 +49,14 @@ import static io.netty.handler.codec.http.HttpVersion.*;
|
||||
|
||||
public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<Object> {
|
||||
|
||||
private HttpRequestHeader request;
|
||||
private boolean readingChunks;
|
||||
private HttpRequest request;
|
||||
/** Buffer that stores the response content */
|
||||
private final StringBuilder buf = new StringBuilder();
|
||||
|
||||
@Override
|
||||
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
if (msg instanceof HttpRequestHeader) {
|
||||
HttpRequestHeader request = this.request = (HttpRequestHeader) msg;
|
||||
if (msg instanceof HttpRequest) {
|
||||
HttpRequest request = this.request = (HttpRequest) msg;
|
||||
|
||||
if (is100ContinueExpected(request)) {
|
||||
send100Continue(ctx);
|
||||
@ -67,13 +66,13 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
|
||||
buf.append("WELCOME TO THE WILD WILD WEB SERVER\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("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()) {
|
||||
for (Map.Entry<String, String> h: request.getHeaders()) {
|
||||
for (Map.Entry<String, String> h: request.headers().entries()) {
|
||||
String key = h.getKey();
|
||||
String value = h.getValue();
|
||||
buf.append("HEADER: ").append(key).append(" = ").append(value).append("\r\n");
|
||||
@ -81,7 +80,7 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
|
||||
buf.append("\r\n");
|
||||
}
|
||||
|
||||
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri());
|
||||
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri());
|
||||
Map<String, List<String>> params = queryStringDecoder.getParameters();
|
||||
if (!params.isEmpty()) {
|
||||
for (Entry<String, List<String>> p: params.entrySet()) {
|
||||
@ -94,40 +93,28 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
|
||||
buf.append("\r\n");
|
||||
}
|
||||
|
||||
if (isTransferEncodingChunked(request)) {
|
||||
readingChunks = true;
|
||||
} else {
|
||||
appendDecoderResult(buf, request);
|
||||
}
|
||||
appendDecoderResult(buf, request);
|
||||
}
|
||||
|
||||
if (msg instanceof HttpContent) {
|
||||
HttpContent httpContent = (HttpContent) msg;
|
||||
|
||||
ByteBuf content = httpContent.getContent();
|
||||
ByteBuf content = httpContent.data();
|
||||
if (content.readable()) {
|
||||
buf.append("CONTENT: ");
|
||||
buf.append(content.toString(CharsetUtil.UTF_8));
|
||||
buf.append("\r\n");
|
||||
appendDecoderResult(buf, request);
|
||||
}
|
||||
appendDecoderResult(buf, request);
|
||||
writeResponse(ctx, request);
|
||||
|
||||
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");
|
||||
|
||||
LastHttpContent trailer = (LastHttpContent) msg;
|
||||
if (!trailer.getHeaderNames().isEmpty()) {
|
||||
if (!trailer.trailingHeaders().isEmpty()) {
|
||||
buf.append("\r\n");
|
||||
for (String name: trailer.getHeaderNames()) {
|
||||
for (String value: trailer.getHeaders(name)) {
|
||||
for (String name: trailer.trailingHeaders().names()) {
|
||||
for (String value: trailer.trailingHeaders().getAll(name)) {
|
||||
buf.append("TRAILING HEADER: ");
|
||||
buf.append(name).append(" = ").append(value).append("\r\n");
|
||||
}
|
||||
@ -135,20 +122,13 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
|
||||
buf.append("\r\n");
|
||||
}
|
||||
|
||||
appendDecoderResult(buf, 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) {
|
||||
DecoderResult result = o.getDecoderResult();
|
||||
DecoderResult result = o.decoderResult();
|
||||
if (result.isSuccess()) {
|
||||
return;
|
||||
}
|
||||
@ -167,34 +147,34 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
|
||||
boolean keepAlive = isKeepAlive(request);
|
||||
|
||||
// Build the response object.
|
||||
HttpResponse response = new DefaultHttpResponse(
|
||||
HTTP_1_1, currentObj.getDecoderResult().isSuccess()? OK : BAD_REQUEST);
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||
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.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||
response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||
|
||||
if (keepAlive) {
|
||||
// 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:
|
||||
// - 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.
|
||||
String cookieString = request.getHeader(COOKIE);
|
||||
String cookieString = request.headers().get(COOKIE);
|
||||
if (cookieString != null) {
|
||||
Set<Cookie> cookies = CookieDecoder.decode(cookieString);
|
||||
if (!cookies.isEmpty()) {
|
||||
// Reset the cookies if necessary.
|
||||
for (Cookie cookie: cookies) {
|
||||
response.addHeader(SET_COOKIE, ServerCookieEncoder.encode(cookie));
|
||||
response.headers().add(SET_COOKIE, ServerCookieEncoder.encode(cookie));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Browser sent no cookie. Add some.
|
||||
response.addHeader(SET_COOKIE, ServerCookieEncoder.encode("key1", "value1"));
|
||||
response.addHeader(SET_COOKIE, ServerCookieEncoder.encode("key2", "value2"));
|
||||
response.headers().add(SET_COOKIE, ServerCookieEncoder.encode("key1", "value1"));
|
||||
response.headers().add(SET_COOKIE, ServerCookieEncoder.encode("key2", "value2"));
|
||||
}
|
||||
|
||||
// Write the response.
|
||||
@ -207,7 +187,7 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@ package io.netty.example.http.snoop;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
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.HttpResponseEncoder;
|
||||
|
||||
@ -38,7 +37,7 @@ public class HttpSnoopServerInitializer extends ChannelInitializer<SocketChannel
|
||||
//pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
|
||||
p.addLast("encoder", new HttpResponseEncoder());
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
@ -21,10 +21,10 @@ import io.netty.channel.socket.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.http.ClientCookieEncoder;
|
||||
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.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import io.netty.handler.codec.http.QueryStringEncoder;
|
||||
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
|
||||
@ -178,33 +178,31 @@ public class HttpUploadClient {
|
||||
return null;
|
||||
}
|
||||
|
||||
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uriGet.toASCIIString());
|
||||
request.setHeader(HttpHeaders.Names.HOST, host);
|
||||
request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
|
||||
request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP + ','
|
||||
FullHttpRequest request =
|
||||
new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uriGet.toASCIIString());
|
||||
HttpHeaders headers = request.headers();
|
||||
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);
|
||||
|
||||
request.setHeader(HttpHeaders.Names.ACCEPT_CHARSET, "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
|
||||
request.setHeader(HttpHeaders.Names.ACCEPT_LANGUAGE, "fr");
|
||||
request.setHeader(HttpHeaders.Names.REFERER, uriSimple.toString());
|
||||
request.setHeader(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_CHARSET, "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
|
||||
headers.set(HttpHeaders.Names.ACCEPT_LANGUAGE, "fr");
|
||||
headers.set(HttpHeaders.Names.REFERER, uriSimple.toString());
|
||||
headers.set(HttpHeaders.Names.USER_AGENT, "Netty Simple Http Client side");
|
||||
headers.set(HttpHeaders.Names.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
|
||||
|
||||
// connection will not close but needed
|
||||
// request.setHeader("Connection","keep-alive");
|
||||
// request.setHeader("Keep-Alive","300");
|
||||
|
||||
request.setHeader(HttpHeaders.Names.COOKIE, ClientCookieEncoder.encode(new DefaultCookie("my-cookie", "foo"),
|
||||
headers.set(HttpHeaders.Names.COOKIE, ClientCookieEncoder.encode(new DefaultCookie("my-cookie", "foo"),
|
||||
new DefaultCookie("another-cookie", "bar")));
|
||||
|
||||
// send request
|
||||
List<Entry<String, String>> headers = request.getHeaders();
|
||||
channel.write(request);
|
||||
List<Entry<String, String>> entries = headers.entries();
|
||||
channel.write(request).sync();
|
||||
|
||||
// Wait for the server to close the connection.
|
||||
channel.closeFuture().sync();
|
||||
|
||||
return headers;
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -220,7 +218,8 @@ public class HttpUploadClient {
|
||||
Channel channel = bootstrap.connect().sync().channel();
|
||||
|
||||
// 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
|
||||
HttpPostRequestEncoder bodyRequestEncoder = null;
|
||||
@ -236,7 +235,7 @@ public class HttpUploadClient {
|
||||
|
||||
// it is legal to add directly header or cookie into the request until finalize
|
||||
for (Entry<String, String> entry : headers) {
|
||||
request.setHeader(entry.getKey(), entry.getValue());
|
||||
request.headers().set(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
// add Form attribute
|
||||
@ -304,7 +303,8 @@ public class HttpUploadClient {
|
||||
Channel channel = bootstrap.connect().sync().channel();
|
||||
|
||||
// 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
|
||||
HttpPostRequestEncoder bodyRequestEncoder = null;
|
||||
@ -320,7 +320,7 @@ public class HttpUploadClient {
|
||||
|
||||
// it is legal to add directly header or cookie into the request until finalize
|
||||
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()
|
||||
|
@ -19,7 +19,7 @@ import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||
import io.netty.handler.codec.http.HttpContent;
|
||||
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.logging.InternalLogger;
|
||||
import io.netty.logging.InternalLoggerFactory;
|
||||
@ -36,21 +36,21 @@ public class HttpUploadClientHandler extends ChannelInboundMessageHandlerAdapter
|
||||
|
||||
@Override
|
||||
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
if (msg instanceof HttpResponseHeader) {
|
||||
HttpResponseHeader response = (HttpResponseHeader) msg;
|
||||
if (msg instanceof HttpResponse) {
|
||||
HttpResponse response = (HttpResponse) msg;
|
||||
|
||||
logger.info("STATUS: " + response.getStatus());
|
||||
logger.info("VERSION: " + response.getProtocolVersion());
|
||||
logger.info("STATUS: " + response.status());
|
||||
logger.info("VERSION: " + response.protocolVersion());
|
||||
|
||||
if (!response.getHeaderNames().isEmpty()) {
|
||||
for (String name : response.getHeaderNames()) {
|
||||
for (String value : response.getHeaders(name)) {
|
||||
if (!response.headers().isEmpty()) {
|
||||
for (String name : response.headers().names()) {
|
||||
for (String value : response.headers().getAll(name)) {
|
||||
logger.info("HEADER: " + name + " = " + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (response.getStatus().getCode() == 200 && HttpHeaders.isTransferEncodingChunked(response)) {
|
||||
if (response.status().code() == 200 && HttpHeaders.isTransferEncodingChunked(response)) {
|
||||
readingChunks = true;
|
||||
logger.info("CHUNKED CONTENT {");
|
||||
} else {
|
||||
@ -59,7 +59,7 @@ public class HttpUploadClientHandler extends ChannelInboundMessageHandlerAdapter
|
||||
}
|
||||
if (msg instanceof HttpContent) {
|
||||
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 (readingChunks) {
|
||||
@ -69,7 +69,7 @@ public class HttpUploadClientHandler extends ChannelInboundMessageHandlerAdapter
|
||||
}
|
||||
readingChunks = false;
|
||||
} else {
|
||||
logger.info(chunk.getContent().toString(CharsetUtil.UTF_8));
|
||||
logger.info(chunk.data().toString(CharsetUtil.UTF_8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,11 +23,11 @@ import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||
import io.netty.handler.codec.http.Cookie;
|
||||
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.HttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpRequestHeader;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
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 HttpRequestHeader request;
|
||||
private HttpRequest request;
|
||||
|
||||
private boolean readingChunks;
|
||||
|
||||
@ -95,15 +95,15 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
|
||||
|
||||
@Override
|
||||
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
if (msg instanceof HttpRequestHeader) {
|
||||
if (msg instanceof HttpRequest) {
|
||||
// clean previous FileUpload if Any
|
||||
if (decoder != null) {
|
||||
decoder.cleanFiles();
|
||||
decoder = null;
|
||||
}
|
||||
|
||||
HttpRequestHeader request = this.request = (HttpRequestHeader) msg;
|
||||
URI uri = new URI(request.getUri());
|
||||
HttpRequest request = this.request = (HttpRequest) msg;
|
||||
URI uri = new URI(request.uri());
|
||||
if (!uri.getPath().startsWith("/form")) {
|
||||
// Write Menu
|
||||
writeMenu(ctx);
|
||||
@ -113,13 +113,13 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
|
||||
responseContent.append("WELCOME TO THE WILD WILD WEB SERVER\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");
|
||||
|
||||
// new method
|
||||
List<Entry<String, String>> headers = request.getHeaders();
|
||||
List<Entry<String, String>> headers = request.headers().entries();
|
||||
for (Entry<String, String> entry : headers) {
|
||||
responseContent.append("HEADER: " + entry.getKey() + '=' + entry.getValue() + "\r\n");
|
||||
}
|
||||
@ -127,7 +127,7 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
|
||||
|
||||
// new method
|
||||
Set<Cookie> cookies;
|
||||
String value = request.getHeader(COOKIE);
|
||||
String value = request.headers().get(COOKIE);
|
||||
if (value == null) {
|
||||
cookies = Collections.emptySet();
|
||||
} else {
|
||||
@ -138,7 +138,7 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
|
||||
}
|
||||
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();
|
||||
for (Entry<String, List<String>> attr: uriAttributes.entrySet()) {
|
||||
for (String attrVal: attr.getValue()) {
|
||||
@ -300,23 +300,23 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
|
||||
responseContent.setLength(0);
|
||||
|
||||
// Decide whether to close the connection or not.
|
||||
boolean close = HttpHeaders.Values.CLOSE.equalsIgnoreCase(request.getHeader(CONNECTION))
|
||||
|| request.getProtocolVersion().equals(HttpVersion.HTTP_1_0)
|
||||
&& !HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(request.getHeader(CONNECTION));
|
||||
boolean close = HttpHeaders.Values.CLOSE.equalsIgnoreCase(request.headers().get(CONNECTION))
|
||||
|| request.protocolVersion().equals(HttpVersion.HTTP_1_0)
|
||||
&& !HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(request.headers().get(CONNECTION));
|
||||
|
||||
// Build the response object.
|
||||
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
|
||||
response.setContent(buf);
|
||||
response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf);
|
||||
response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||
|
||||
if (!close) {
|
||||
// There's no need to add 'Content-Length' header
|
||||
// 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;
|
||||
String value = request.getHeader(COOKIE);
|
||||
String value = request.headers().get(COOKIE);
|
||||
if (value == null) {
|
||||
cookies = Collections.emptySet();
|
||||
} else {
|
||||
@ -325,7 +325,7 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
|
||||
if (!cookies.isEmpty()) {
|
||||
// Reset the cookies if necessary.
|
||||
for (Cookie cookie : cookies) {
|
||||
response.addHeader(SET_COOKIE, ServerCookieEncoder.encode(cookie));
|
||||
response.headers().add(SET_COOKIE, ServerCookieEncoder.encode(cookie));
|
||||
}
|
||||
}
|
||||
// Write the response.
|
||||
@ -410,10 +410,12 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
|
||||
|
||||
ByteBuf buf = copiedBuffer(responseContent.toString(), CharsetUtil.UTF_8);
|
||||
// Build the response object.
|
||||
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
|
||||
response.setContent(buf);
|
||||
response.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8");
|
||||
response.setHeader(CONTENT_LENGTH, String.valueOf(buf.readableBytes()));
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf);
|
||||
|
||||
response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
|
||||
response.headers().set(CONTENT_LENGTH, String.valueOf(buf.readableBytes()));
|
||||
|
||||
// Write the response.
|
||||
ctx.channel().write(response);
|
||||
}
|
||||
|
@ -20,10 +20,10 @@ import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
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.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame;
|
||||
@ -52,23 +52,23 @@ public class AutobahnServerHandler extends ChannelInboundMessageHandlerAdapter<O
|
||||
|
||||
@Override
|
||||
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
if (msg instanceof HttpRequest) {
|
||||
handleHttpRequest(ctx, (HttpRequest) msg);
|
||||
if (msg instanceof FullHttpRequest) {
|
||||
handleHttpRequest(ctx, (FullHttpRequest) msg);
|
||||
} else if (msg instanceof WebSocketFrame) {
|
||||
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.
|
||||
if (!req.getDecoderResult().isSuccess()) {
|
||||
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, BAD_REQUEST));
|
||||
if (!req.decoderResult().isSuccess()) {
|
||||
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow only GET methods.
|
||||
if (req.getMethod() != GET) {
|
||||
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN));
|
||||
if (req.method() != GET) {
|
||||
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
|
||||
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).
|
||||
if (res.getStatus().getCode() != 200) {
|
||||
res.setContent(Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8));
|
||||
setContentLength(res, res.getContent().readableBytes());
|
||||
if (res.status().code() != 200) {
|
||||
res.data().writeBytes(Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8));
|
||||
setContentLength(res, res.data().readableBytes());
|
||||
}
|
||||
|
||||
// Send the response and close the connection if necessary.
|
||||
ChannelFuture f = ctx.channel().write(res);
|
||||
if (!isKeepAlive(req) || res.getStatus().getCode() != 200) {
|
||||
if (!isKeepAlive(req) || res.status().code() != 200) {
|
||||
f.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
@ -132,7 +133,7 @@ public class AutobahnServerHandler extends ChannelInboundMessageHandlerAdapter<O
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
private static String getWebSocketLocation(HttpRequest req) {
|
||||
return "ws://" + req.getHeader(HttpHeaders.Names.HOST);
|
||||
private static String getWebSocketLocation(FullHttpRequest req) {
|
||||
return "ws://" + req.headers().get(HttpHeaders.Names.HOST);
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioEventLoopGroup;
|
||||
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.HttpResponseDecoder;
|
||||
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
|
||||
@ -91,6 +92,7 @@ public class WebSocketClient {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
pipeline.addLast("decoder", new HttpResponseDecoder());
|
||||
pipeline.addLast("encoder", new HttpRequestEncoder());
|
||||
pipeline.addLast("aggregator", new HttpObjectAggregator(8192));
|
||||
pipeline.addLast("ws-handler", handler);
|
||||
}
|
||||
});
|
||||
@ -101,7 +103,7 @@ public class WebSocketClient {
|
||||
|
||||
// Send 10 messages and wait for responses
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||
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.PongWebSocketFrame;
|
||||
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 {
|
||||
Channel ch = ctx.channel();
|
||||
if (!handshaker.isHandshakeComplete()) {
|
||||
handshaker.finishHandshake(ch, (HttpResponse) msg);
|
||||
handshaker.finishHandshake(ch, (FullHttpResponse) msg);
|
||||
System.out.println("WebSocket Client connected!");
|
||||
handshakeFuture.setSuccess();
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg instanceof HttpResponse) {
|
||||
HttpResponse response = (HttpResponse) msg;
|
||||
throw new Exception("Unexpected HttpResponse (status=" + response.getStatus() + ", content="
|
||||
+ response.getContent().toString(CharsetUtil.UTF_8) + ')');
|
||||
if (msg instanceof FullHttpResponse) {
|
||||
FullHttpResponse response = (FullHttpResponse) msg;
|
||||
throw new Exception("Unexpected FullHttpResponse (status=" + response.status() + ", content="
|
||||
+ response.data().toString(CharsetUtil.UTF_8) + ')');
|
||||
}
|
||||
|
||||
WebSocketFrame frame = (WebSocketFrame) msg;
|
||||
|
@ -21,9 +21,9 @@ import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
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.websocketx.CloseWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
|
||||
@ -53,41 +53,39 @@ public class WebSocketServerHandler extends ChannelInboundMessageHandlerAdapter<
|
||||
|
||||
@Override
|
||||
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
if (msg instanceof HttpRequest) {
|
||||
handleHttpRequest(ctx, (HttpRequest) msg);
|
||||
if (msg instanceof FullHttpRequest) {
|
||||
handleHttpRequest(ctx, (FullHttpRequest) msg);
|
||||
} else if (msg instanceof WebSocketFrame) {
|
||||
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.
|
||||
if (!req.getDecoderResult().isSuccess()) {
|
||||
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, BAD_REQUEST));
|
||||
if (!req.decoderResult().isSuccess()) {
|
||||
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow only GET methods.
|
||||
if (req.getMethod() != GET) {
|
||||
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN));
|
||||
if (req.method() != GET) {
|
||||
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the demo page and favicon.ico
|
||||
if ("/".equals(req.getUri())) {
|
||||
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, OK);
|
||||
|
||||
if ("/".equals(req.uri())) {
|
||||
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());
|
||||
|
||||
res.setContent(content);
|
||||
sendHttpResponse(ctx, req, res);
|
||||
return;
|
||||
}
|
||||
if ("/favicon.ico".equals(req.getUri())) {
|
||||
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, NOT_FOUND);
|
||||
if ("/favicon.ico".equals(req.uri())) {
|
||||
FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND);
|
||||
sendHttpResponse(ctx, req, res);
|
||||
return;
|
||||
}
|
||||
@ -127,16 +125,17 @@ public class WebSocketServerHandler extends ChannelInboundMessageHandlerAdapter<
|
||||
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).
|
||||
if (res.getStatus().getCode() != 200) {
|
||||
res.setContent(Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8));
|
||||
setContentLength(res, res.getContent().readableBytes());
|
||||
if (res.status().code() != 200) {
|
||||
res.data().writeBytes(Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8));
|
||||
setContentLength(res, res.data().readableBytes());
|
||||
}
|
||||
|
||||
// Send the response and close the connection if necessary.
|
||||
ChannelFuture f = ctx.channel().write(res);
|
||||
if (!isKeepAlive(req) || res.getStatus().getCode() != 200) {
|
||||
if (!isKeepAlive(req) || res.status().code() != 200) {
|
||||
f.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
@ -147,7 +146,7 @@ public class WebSocketServerHandler extends ChannelInboundMessageHandlerAdapter<
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
private static String getWebSocketLocation(HttpRequest req) {
|
||||
return "ws://" + req.getHeader(HOST) + WEBSOCKET_PATH;
|
||||
private static String getWebSocketLocation(FullHttpRequest req) {
|
||||
return "ws://" + req.headers().get(HOST) + WEBSOCKET_PATH;
|
||||
}
|
||||
}
|
||||
|
@ -22,9 +22,9 @@ import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||
import io.netty.example.http.websocketx.server.WebSocketServerIndexPage;
|
||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
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.websocketx.CloseWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
|
||||
@ -54,42 +54,40 @@ public class WebSocketSslServerHandler extends ChannelInboundMessageHandlerAdapt
|
||||
|
||||
@Override
|
||||
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
if (msg instanceof HttpRequest) {
|
||||
handleHttpRequest(ctx, (HttpRequest) msg);
|
||||
if (msg instanceof FullHttpRequest) {
|
||||
handleHttpRequest(ctx, (FullHttpRequest) msg);
|
||||
} else if (msg instanceof WebSocketFrame) {
|
||||
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.
|
||||
if (!req.getDecoderResult().isSuccess()) {
|
||||
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, BAD_REQUEST));
|
||||
if (!req.decoderResult().isSuccess()) {
|
||||
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow only GET methods.
|
||||
if (req.getMethod() != GET) {
|
||||
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN));
|
||||
if (req.method() != GET) {
|
||||
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the demo page and favicon.ico
|
||||
if ("/".equals(req.getUri())) {
|
||||
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, OK);
|
||||
|
||||
if ("/".equals(req.uri())) {
|
||||
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());
|
||||
|
||||
res.setContent(content);
|
||||
sendHttpResponse(ctx, req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("/favicon.ico".equals(req.getUri())) {
|
||||
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, NOT_FOUND);
|
||||
if ("/favicon.ico".equals(req.uri())) {
|
||||
FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND);
|
||||
sendHttpResponse(ctx, req, res);
|
||||
return;
|
||||
}
|
||||
@ -129,16 +127,17 @@ public class WebSocketSslServerHandler extends ChannelInboundMessageHandlerAdapt
|
||||
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).
|
||||
if (res.getStatus().getCode() != 200) {
|
||||
res.setContent(Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8));
|
||||
setContentLength(res, res.getContent().readableBytes());
|
||||
if (res.status().code() != 200) {
|
||||
res.data().writeBytes(Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8));
|
||||
setContentLength(res, res.data().readableBytes());
|
||||
}
|
||||
|
||||
// Send the response and close the connection if necessary.
|
||||
ChannelFuture f = ctx.channel().write(res);
|
||||
if (!isKeepAlive(req) || res.getStatus().getCode() != 200) {
|
||||
if (!isKeepAlive(req) || res.status().code() != 200) {
|
||||
f.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
@ -149,7 +148,7 @@ public class WebSocketSslServerHandler extends ChannelInboundMessageHandlerAdapt
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
private static String getWebSocketLocation(HttpRequest req) {
|
||||
return "wss://" + req.getHeader(HOST) + WEBSOCKET_PATH;
|
||||
private static String getWebSocketLocation(FullHttpRequest req) {
|
||||
return "wss://" + req.headers().get(HOST) + WEBSOCKET_PATH;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user