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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
if (isFreed()) {
|
||||||
|
return getClass().getSimpleName() + "(freed)";
|
||||||
|
}
|
||||||
|
|
||||||
return getClass().getSimpleName() + '(' +
|
return getClass().getSimpleName() + '(' +
|
||||||
"ridx=" + readerIndex + ", " +
|
"ridx=" + readerIndex + ", " +
|
||||||
"widx=" + writerIndex + ", " +
|
"widx=" + writerIndex + ", " +
|
||||||
|
|
|
@ -57,9 +57,6 @@ public class DefaultByteBufHolder implements ByteBufHolder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (isFreed()) {
|
return getClass().getSimpleName() + '(' + data().toString() + ')';
|
||||||
return "Message{data=(FREED)}";
|
|
||||||
}
|
|
||||||
return "Message{data=" + ByteBufUtil.hexDump(data()) + '}';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import static io.netty.handler.codec.http.CookieEncoderUtil.*;
|
||||||
* the HTTP cookie version 0, 1, and 2.
|
* the HTTP cookie version 0, 1, and 2.
|
||||||
* <pre>
|
* <pre>
|
||||||
* // Example
|
* // Example
|
||||||
* {@link HttpRequestHeader} req = ...;
|
* {@link HttpRequest} req = ...;
|
||||||
* res.setHeader("Cookie", {@link ClientCookieEncoder}.encode("JSESSIONID", "1234"));
|
* res.setHeader("Cookie", {@link ClientCookieEncoder}.encode("JSESSIONID", "1234"));
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
|
|
|
@ -29,7 +29,7 @@ import java.util.TreeSet;
|
||||||
* the HTTP cookie version 0, 1, and 2.
|
* the HTTP cookie version 0, 1, and 2.
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* {@link HttpRequestHeader} req = ...;
|
* {@link HttpRequest} req = ...;
|
||||||
* String value = req.getHeader("Cookie");
|
* String value = req.getHeader("Cookie");
|
||||||
* Set<{@link Cookie}> cookies = new {@link CookieDecoder}().decode(value);
|
* Set<{@link Cookie}> cookies = new {@link CookieDecoder}().decode(value);
|
||||||
* </pre>
|
* </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 {
|
public class DefaultHttpContent extends DefaultHttpObject implements HttpContent {
|
||||||
|
|
||||||
private ByteBuf content;
|
private final ByteBuf content;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance with the specified chunk content.
|
* Creates a new instance with the specified chunk content.
|
||||||
*/
|
*/
|
||||||
public DefaultHttpContent(ByteBuf content) {
|
public DefaultHttpContent(ByteBuf content) {
|
||||||
setContent(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ByteBuf getContent() {
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setContent(ByteBuf content) {
|
|
||||||
if (content == null) {
|
if (content == null) {
|
||||||
throw new NullPointerException("content");
|
throw new NullPointerException("content");
|
||||||
}
|
}
|
||||||
this.content = content;
|
this.content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBuf data() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpContent copy() {
|
||||||
|
return new DefaultHttpContent(data().copy());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFreed() {
|
||||||
|
return content.isFreed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void free() {
|
||||||
|
content.free();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder buf = new StringBuilder();
|
return getClass().getSimpleName() + "(data: " + data() + ", decoderResult: " + decoderResult() + ')';
|
||||||
buf.append(getClass().getSimpleName());
|
|
||||||
|
|
||||||
buf.append(" size: ");
|
|
||||||
buf.append(getContent().readableBytes());
|
|
||||||
|
|
||||||
buf.append(", decodeResult: ");
|
|
||||||
buf.append(getDecoderResult());
|
|
||||||
buf.append(')');
|
|
||||||
|
|
||||||
return buf.toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.util.internal.StringUtil;
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Combination of {@link HttpHeader} and {@link LastHttpContent} which can be used to <i>combine</i> the headers and
|
* The default {@link HttpMessage} implementation.
|
||||||
* the actual content. {@link HttpObjectAggregator} makes use of this.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public abstract class DefaultHttpMessage extends DefaultHttpHeader implements LastHttpContent {
|
public abstract class DefaultHttpMessage extends DefaultHttpObject implements HttpMessage {
|
||||||
private ByteBuf content = Unpooled.EMPTY_BUFFER;
|
|
||||||
|
|
||||||
public DefaultHttpMessage(HttpVersion version) {
|
private final HttpVersion version;
|
||||||
super(version);
|
private final HttpHeaders headers = new DefaultHttpHeaders();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public ByteBuf getContent() {
|
* Creates a new instance.
|
||||||
return content;
|
*/
|
||||||
}
|
protected DefaultHttpMessage(final HttpVersion version) {
|
||||||
|
if (version == null) {
|
||||||
@Override
|
throw new NullPointerException("version");
|
||||||
public void setContent(ByteBuf content) {
|
}
|
||||||
if (content == null) {
|
this.version = version;
|
||||||
throw new NullPointerException("content");
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpHeaders headers() {
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpVersion protocolVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
buf.append(getClass().getSimpleName());
|
||||||
|
buf.append("(version: ");
|
||||||
|
buf.append(protocolVersion().getText());
|
||||||
|
buf.append(", keepAlive: ");
|
||||||
|
buf.append(HttpHeaders.isKeepAlive(this));
|
||||||
|
buf.append(')');
|
||||||
|
buf.append(StringUtil.NEWLINE);
|
||||||
|
appendHeaders(buf);
|
||||||
|
|
||||||
|
// Remove the last newline.
|
||||||
|
buf.setLength(buf.length() - StringUtil.NEWLINE.length());
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void appendHeaders(StringBuilder buf) {
|
||||||
|
for (Map.Entry<String, String> e: headers().entries()) {
|
||||||
|
buf.append(e.getKey());
|
||||||
|
buf.append(": ");
|
||||||
|
buf.append(e.getValue());
|
||||||
|
buf.append(StringUtil.NEWLINE);
|
||||||
}
|
}
|
||||||
this.content = content;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,22 +19,22 @@ import io.netty.handler.codec.DecoderResult;
|
||||||
|
|
||||||
public class DefaultHttpObject implements HttpObject {
|
public class DefaultHttpObject implements HttpObject {
|
||||||
|
|
||||||
private DecoderResult decodeResult = DecoderResult.SUCCESS;
|
private DecoderResult decoderResult = DecoderResult.SUCCESS;
|
||||||
|
|
||||||
protected DefaultHttpObject() {
|
protected DefaultHttpObject() {
|
||||||
// Disallow direct instantiation
|
// Disallow direct instantiation
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DecoderResult getDecoderResult() {
|
public DecoderResult decoderResult() {
|
||||||
return decodeResult;
|
return decoderResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDecoderResult(DecoderResult result) {
|
public void updateDecoderResult(DecoderResult decoderResult) {
|
||||||
if (result == null) {
|
if (decoderResult == null) {
|
||||||
throw new NullPointerException("result");
|
throw new NullPointerException("decoderResult");
|
||||||
}
|
}
|
||||||
decodeResult = result;
|
this.decoderResult = decoderResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013 The Netty Project
|
* Copyright 2012 The Netty Project
|
||||||
*
|
*
|
||||||
* The Netty Project licenses this file to you under the Apache License,
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
@ -15,34 +15,63 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.util.internal.StringUtil;
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default implementation of {@link HttpRequest}.
|
* The default {@link HttpRequest} implementation.
|
||||||
*/
|
*/
|
||||||
public class DefaultHttpRequest extends DefaultHttpRequestHeader implements HttpRequest {
|
public class DefaultHttpRequest extends DefaultHttpMessage implements HttpRequest {
|
||||||
private ByteBuf content = Unpooled.EMPTY_BUFFER;
|
|
||||||
|
|
||||||
|
private final HttpMethod method;
|
||||||
|
private final String uri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
*
|
||||||
|
* @param httpVersion the HTTP version of the request
|
||||||
|
* @param method the HTTP method of the request
|
||||||
|
* @param uri the URI or path of the request
|
||||||
|
*/
|
||||||
public DefaultHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri) {
|
public DefaultHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri) {
|
||||||
this(httpVersion, method, uri, Unpooled.EMPTY_BUFFER);
|
super(httpVersion);
|
||||||
}
|
if (method == null) {
|
||||||
|
throw new NullPointerException("method");
|
||||||
public DefaultHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri, ByteBuf content) {
|
|
||||||
super(httpVersion, method, uri);
|
|
||||||
setContent(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ByteBuf getContent() {
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setContent(ByteBuf content) {
|
|
||||||
if (content == null) {
|
|
||||||
throw new NullPointerException("content");
|
|
||||||
}
|
}
|
||||||
this.content = content;
|
if (uri == null) {
|
||||||
|
throw new NullPointerException("uri");
|
||||||
|
}
|
||||||
|
this.method = method;
|
||||||
|
this.uri = uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpMethod method() {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String uri() {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
buf.append(getClass().getSimpleName());
|
||||||
|
buf.append(", decodeResult: ");
|
||||||
|
buf.append(decoderResult());
|
||||||
|
buf.append(')');
|
||||||
|
buf.append(StringUtil.NEWLINE);
|
||||||
|
buf.append(method().toString());
|
||||||
|
buf.append(' ');
|
||||||
|
buf.append(uri());
|
||||||
|
buf.append(' ');
|
||||||
|
buf.append(protocolVersion().getText());
|
||||||
|
buf.append(StringUtil.NEWLINE);
|
||||||
|
appendHeaders(buf);
|
||||||
|
|
||||||
|
// Remove the last newline.
|
||||||
|
buf.setLength(buf.length() - StringUtil.NEWLINE.length());
|
||||||
|
return buf.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
@ -15,35 +15,50 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.util.internal.StringUtil;
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default implementation of a {@link HttpResponse}.
|
* The default {@link HttpResponse} implementation.
|
||||||
*/
|
*/
|
||||||
public class DefaultHttpResponse extends DefaultHttpResponseHeader implements HttpResponse {
|
public class DefaultHttpResponse extends DefaultHttpMessage implements HttpResponse {
|
||||||
private ByteBuf content = Unpooled.EMPTY_BUFFER;
|
|
||||||
|
|
||||||
|
private final HttpResponseStatus status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
*
|
||||||
|
* @param version the HTTP version of this response
|
||||||
|
* @param status the status of this response
|
||||||
|
*/
|
||||||
public DefaultHttpResponse(HttpVersion version, HttpResponseStatus status) {
|
public DefaultHttpResponse(HttpVersion version, HttpResponseStatus status) {
|
||||||
this(version, status, Unpooled.EMPTY_BUFFER);
|
super(version);
|
||||||
}
|
if (status == null) {
|
||||||
|
throw new NullPointerException("status");
|
||||||
public DefaultHttpResponse(HttpVersion version, HttpResponseStatus status, ByteBuf content) {
|
|
||||||
super(version, status);
|
|
||||||
setContent(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ByteBuf getContent() {
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setContent(ByteBuf content) {
|
|
||||||
if (content == null) {
|
|
||||||
throw new NullPointerException("content");
|
|
||||||
}
|
}
|
||||||
this.content = content;
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpResponseStatus status() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
buf.append(getClass().getSimpleName());
|
||||||
|
buf.append(", decodeResult: ");
|
||||||
|
buf.append(decoderResult());
|
||||||
|
buf.append(')');
|
||||||
|
buf.append(StringUtil.NEWLINE);
|
||||||
|
buf.append(protocolVersion().getText());
|
||||||
|
buf.append(' ');
|
||||||
|
buf.append(status().toString());
|
||||||
|
buf.append(StringUtil.NEWLINE);
|
||||||
|
appendHeaders(buf);
|
||||||
|
|
||||||
|
// Remove the last newline.
|
||||||
|
buf.setLength(buf.length() - StringUtil.NEWLINE.length());
|
||||||
|
return buf.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.buffer.Unpooled;
|
||||||
import io.netty.util.internal.StringUtil;
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default {@link LastHttpContent} implementation.
|
* The default {@link LastHttpContent} implementation.
|
||||||
*/
|
*/
|
||||||
public class DefaultLastHttpContent extends DefaultHttpContent implements LastHttpContent {
|
public class DefaultLastHttpContent extends DefaultHttpContent implements LastHttpContent {
|
||||||
|
|
||||||
private final HttpHeaders headers = new HttpHeaders() {
|
private final HttpHeaders trailingHeaders = new DefaultHttpHeaders() {
|
||||||
@Override
|
@Override
|
||||||
void validateHeaderName0(String name) {
|
void validateHeaderName0(String name) {
|
||||||
super.validateHeaderName0(name);
|
super.validateHeaderName0(name);
|
||||||
|
@ -42,7 +40,7 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
|
||||||
};
|
};
|
||||||
|
|
||||||
public DefaultLastHttpContent() {
|
public DefaultLastHttpContent() {
|
||||||
this(Unpooled.EMPTY_BUFFER);
|
this(Unpooled.buffer(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DefaultLastHttpContent(ByteBuf content) {
|
public DefaultLastHttpContent(ByteBuf content) {
|
||||||
|
@ -50,64 +48,20 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addHeader(final String name, final Object value) {
|
public LastHttpContent copy() {
|
||||||
headers.addHeader(name, value);
|
DefaultLastHttpContent copy = new DefaultLastHttpContent(data().copy());
|
||||||
|
copy.trailingHeaders().set(trailingHeaders());
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setHeader(final String name, final Object value) {
|
public HttpHeaders trailingHeaders() {
|
||||||
headers.setHeader(name, value);
|
return trailingHeaders;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setHeader(final String name, final Iterable<?> values) {
|
|
||||||
headers.setHeader(name, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeHeader(final String name) {
|
|
||||||
headers.removeHeader(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearHeaders() {
|
|
||||||
headers.clearHeaders();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getHeader(final String name) {
|
|
||||||
return headers.getHeader(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getHeaders(final String name) {
|
|
||||||
return headers.getHeaders(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Map.Entry<String, String>> getHeaders() {
|
|
||||||
return headers.getHeaders();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsHeader(final String name) {
|
|
||||||
return headers.containsHeader(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<String> getHeaderNames() {
|
|
||||||
return headers.getHeaderNames();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder buf = new StringBuilder();
|
StringBuilder buf = new StringBuilder(super.toString());
|
||||||
buf.append(getClass().getSimpleName());
|
|
||||||
buf.append(", size: ");
|
|
||||||
buf.append(getContent().readableBytes());
|
|
||||||
buf.append(", decodeResult: ");
|
|
||||||
buf.append(getDecoderResult());
|
|
||||||
buf.append(')');
|
|
||||||
buf.append(StringUtil.NEWLINE);
|
buf.append(StringUtil.NEWLINE);
|
||||||
appendHeaders(buf);
|
appendHeaders(buf);
|
||||||
|
|
||||||
|
@ -117,7 +71,7 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendHeaders(StringBuilder buf) {
|
private void appendHeaders(StringBuilder buf) {
|
||||||
for (Map.Entry<String, String> e: getHeaders()) {
|
for (Map.Entry<String, String> e: trailingHeaders()) {
|
||||||
buf.append(e.getKey());
|
buf.append(e.getKey());
|
||||||
buf.append(": ");
|
buf.append(": ");
|
||||||
buf.append(e.getValue());
|
buf.append(e.getValue());
|
||||||
|
|
|
@ -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
|
@Override
|
||||||
protected void encode(
|
protected void encode(
|
||||||
ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
|
ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
|
||||||
if (msg instanceof HttpRequestHeader && !done) {
|
if (msg instanceof HttpRequest && !done) {
|
||||||
queue.offer(((HttpRequestHeader) msg).getMethod());
|
queue.offer(((HttpRequest) msg).method());
|
||||||
}
|
}
|
||||||
|
|
||||||
super.encode(ctx, msg, out);
|
super.encode(ctx, msg, out);
|
||||||
|
@ -137,8 +137,8 @@ public class HttpClientCodec extends CombinedChannelHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isContentAlwaysEmpty(HttpHeader msg) {
|
protected boolean isContentAlwaysEmpty(HttpMessage msg) {
|
||||||
final int statusCode = ((HttpResponseHeader) msg).getStatus().getCode();
|
final int statusCode = ((HttpResponse) msg).status().code();
|
||||||
if (statusCode == 100) {
|
if (statusCode == 100) {
|
||||||
// 100-continue response should be excluded from paired comparison.
|
// 100-continue response should be excluded from paired comparison.
|
||||||
return true;
|
return true;
|
||||||
|
@ -148,7 +148,7 @@ public class HttpClientCodec extends CombinedChannelHandler {
|
||||||
// current response.
|
// current response.
|
||||||
HttpMethod method = queue.poll();
|
HttpMethod method = queue.poll();
|
||||||
|
|
||||||
char firstChar = method.getName().charAt(0);
|
char firstChar = method.name().charAt(0);
|
||||||
switch (firstChar) {
|
switch (firstChar) {
|
||||||
case 'H':
|
case 'H':
|
||||||
// According to 4.3, RFC2616:
|
// According to 4.3, RFC2616:
|
||||||
|
|
|
@ -15,134 +15,19 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBufHolder;
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.handler.codec.DecoderResult;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An HTTP chunk which is used for HTTP chunked transfer-encoding.
|
* An HTTP chunk which is used for HTTP chunked transfer-encoding.
|
||||||
* {@link HttpObjectDecoder} generates {@link HttpContent} after
|
* {@link HttpObjectDecoder} generates {@link HttpContent} after
|
||||||
* {@link HttpHeader} when the content is large or the encoding of the content
|
* {@link HttpMessage} when the content is large or the encoding of the content
|
||||||
* is 'chunked. If you prefer not to receive {@link HttpContent} in your handler,
|
* is 'chunked. If you prefer not to receive {@link HttpContent} in your handler,
|
||||||
* please {@link HttpObjectAggregator} after {@link HttpObjectDecoder} in the
|
* place {@link HttpObjectAggregator} after {@link HttpObjectDecoder} in the
|
||||||
* {@link ChannelPipeline}.
|
* {@link ChannelPipeline}.
|
||||||
* @apiviz.landmark
|
* @apiviz.landmark
|
||||||
*/
|
*/
|
||||||
public interface HttpContent extends HttpObject {
|
public interface HttpContent extends HttpObject, ByteBufHolder {
|
||||||
|
@Override
|
||||||
HttpContent EMPTY = new HttpContent() {
|
HttpContent copy();
|
||||||
|
|
||||||
@Override
|
|
||||||
public ByteBuf getContent() {
|
|
||||||
return Unpooled.EMPTY_BUFFER;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setContent(ByteBuf content) {
|
|
||||||
throw new IllegalStateException("read-only");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DecoderResult getDecoderResult() {
|
|
||||||
return DecoderResult.SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setDecoderResult(DecoderResult result) {
|
|
||||||
throw new IllegalStateException("read-only");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The 'end of content' marker in chunked encoding.
|
|
||||||
*/
|
|
||||||
LastHttpContent LAST_CONTENT = new LastHttpContent() {
|
|
||||||
@Override
|
|
||||||
public ByteBuf getContent() {
|
|
||||||
return Unpooled.EMPTY_BUFFER;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setContent(ByteBuf content) {
|
|
||||||
throw new IllegalStateException("read-only");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addHeader(String name, Object value) {
|
|
||||||
throw new IllegalStateException("read-only");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearHeaders() {
|
|
||||||
// NOOP
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsHeader(String name) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getHeader(String name) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<String> getHeaderNames() {
|
|
||||||
return Collections.emptySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getHeaders(String name) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Map.Entry<String, String>> getHeaders() {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeHeader(String name) {
|
|
||||||
// NOOP
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setHeader(String name, Object value) {
|
|
||||||
throw new IllegalStateException("read-only");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setHeader(String name, Iterable<?> values) {
|
|
||||||
throw new IllegalStateException("read-only");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DecoderResult getDecoderResult() {
|
|
||||||
return DecoderResult.SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setDecoderResult(DecoderResult result) {
|
|
||||||
throw new IllegalStateException("read-only");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the content of this chunk. If this is the 'end of content'
|
|
||||||
* marker, {@link Unpooled#EMPTY_BUFFER} will be returned.
|
|
||||||
*/
|
|
||||||
ByteBuf getContent();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the content of this chunk. If an empty buffer is specified,
|
|
||||||
* this chunk becomes the 'end of content' marker.
|
|
||||||
*/
|
|
||||||
void setContent(ByteBuf content);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ import io.netty.handler.codec.compression.ZlibWrapper;
|
||||||
import io.netty.util.internal.StringUtil;
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compresses an {@link HttpHeader} and an {@link HttpContent} in {@code gzip} or
|
* Compresses an {@link HttpMessage} and an {@link HttpContent} in {@code gzip} or
|
||||||
* {@code deflate} encoding while respecting the {@code "Accept-Encoding"} header.
|
* {@code deflate} encoding while respecting the {@code "Accept-Encoding"} header.
|
||||||
* If there is no matching encoding, no compression is done. For more
|
* If there is no matching encoding, no compression is done. For more
|
||||||
* information on how this handler modifies the message, please refer to
|
* information on how this handler modifies the message, please refer to
|
||||||
|
@ -93,8 +93,8 @@ public class HttpContentCompressor extends HttpContentEncoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Result beginEncode(HttpHeader header, HttpContent msg, String acceptEncoding) throws Exception {
|
protected Result beginEncode(HttpMessage header, HttpContent msg, String acceptEncoding) throws Exception {
|
||||||
String contentEncoding = header.getHeader(HttpHeaders.Names.CONTENT_ENCODING);
|
String contentEncoding = header.headers().get(HttpHeaders.Names.CONTENT_ENCODING);
|
||||||
if (contentEncoding != null &&
|
if (contentEncoding != null &&
|
||||||
!HttpHeaders.Values.IDENTITY.equalsIgnoreCase(contentEncoding)) {
|
!HttpHeaders.Values.IDENTITY.equalsIgnoreCase(contentEncoding)) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -16,13 +16,14 @@
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufHolder;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.embedded.EmbeddedByteChannel;
|
import io.netty.channel.embedded.EmbeddedByteChannel;
|
||||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes the content of the received {@link HttpRequestHeader} and {@link HttpContent}.
|
* Decodes the content of the received {@link HttpRequest} and {@link HttpContent}.
|
||||||
* The original content is replaced with the new content decoded by the
|
* The original content is replaced with the new content decoded by the
|
||||||
* {@link EmbeddedByteChannel}, which is created by {@link #newContentDecoder(String)}.
|
* {@link EmbeddedByteChannel}, which is created by {@link #newContentDecoder(String)}.
|
||||||
* Once decoding is finished, the value of the <tt>'Content-Encoding'</tt>
|
* Once decoding is finished, the value of the <tt>'Content-Encoding'</tt>
|
||||||
|
@ -43,7 +44,7 @@ import io.netty.handler.codec.MessageToMessageDecoder;
|
||||||
public abstract class HttpContentDecoder extends MessageToMessageDecoder<Object> {
|
public abstract class HttpContentDecoder extends MessageToMessageDecoder<Object> {
|
||||||
|
|
||||||
private EmbeddedByteChannel decoder;
|
private EmbeddedByteChannel decoder;
|
||||||
private HttpHeader header;
|
private HttpMessage header;
|
||||||
private boolean decodeStarted;
|
private boolean decodeStarted;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,13 +56,13 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<Object>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object decode(ChannelHandlerContext ctx, Object msg) throws Exception {
|
protected Object decode(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
if (msg instanceof HttpResponseHeader && ((HttpResponseHeader) msg).getStatus().getCode() == 100) {
|
if (msg instanceof HttpResponse && ((HttpResponse) msg).status().code() == 100) {
|
||||||
// 100-continue response must be passed through.
|
// 100-continue response must be passed through.
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
if (msg instanceof HttpHeader) {
|
if (msg instanceof HttpMessage) {
|
||||||
assert header == null;
|
assert header == null;
|
||||||
header = (HttpHeader) msg;
|
header = (HttpMessage) msg;
|
||||||
|
|
||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
|
@ -71,11 +72,11 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<Object>
|
||||||
|
|
||||||
if (!decodeStarted) {
|
if (!decodeStarted) {
|
||||||
decodeStarted = true;
|
decodeStarted = true;
|
||||||
HttpHeader header = this.header;
|
HttpMessage header = this.header;
|
||||||
this.header = null;
|
this.header = null;
|
||||||
|
|
||||||
// Determine the content encoding.
|
// Determine the content encoding.
|
||||||
String contentEncoding = header.getHeader(HttpHeaders.Names.CONTENT_ENCODING);
|
String contentEncoding = header.headers().get(HttpHeaders.Names.CONTENT_ENCODING);
|
||||||
if (contentEncoding != null) {
|
if (contentEncoding != null) {
|
||||||
contentEncoding = contentEncoding.trim();
|
contentEncoding = contentEncoding.trim();
|
||||||
} else {
|
} else {
|
||||||
|
@ -89,17 +90,17 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<Object>
|
||||||
if (HttpHeaders.Values.IDENTITY.equals(targetContentEncoding)) {
|
if (HttpHeaders.Values.IDENTITY.equals(targetContentEncoding)) {
|
||||||
// Do NOT set the 'Content-Encoding' header if the target encoding is 'identity'
|
// Do NOT set the 'Content-Encoding' header if the target encoding is 'identity'
|
||||||
// as per: http://tools.ietf.org/html/rfc2616#section-14.11
|
// as per: http://tools.ietf.org/html/rfc2616#section-14.11
|
||||||
header.removeHeader(HttpHeaders.Names.CONTENT_ENCODING);
|
header.headers().remove(HttpHeaders.Names.CONTENT_ENCODING);
|
||||||
} else {
|
} else {
|
||||||
header.setHeader(HttpHeaders.Names.CONTENT_ENCODING, targetContentEncoding);
|
header.headers().set(HttpHeaders.Names.CONTENT_ENCODING, targetContentEncoding);
|
||||||
}
|
}
|
||||||
Object[] decoded = decodeContent(header, c);
|
Object[] decoded = decodeContent(header, c);
|
||||||
|
|
||||||
// Replace the content.
|
// Replace the content.
|
||||||
if (header.containsHeader(HttpHeaders.Names.CONTENT_LENGTH)) {
|
if (header.headers().contains(HttpHeaders.Names.CONTENT_LENGTH)) {
|
||||||
header.setHeader(
|
header.headers().set(
|
||||||
HttpHeaders.Names.CONTENT_LENGTH,
|
HttpHeaders.Names.CONTENT_LENGTH,
|
||||||
Integer.toString(((HttpContent) decoded[1]).getContent().readableBytes()));
|
Integer.toString(((ByteBufHolder) decoded[1]).data().readableBytes()));
|
||||||
}
|
}
|
||||||
return decoded;
|
return decoded;
|
||||||
}
|
}
|
||||||
|
@ -108,13 +109,13 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<Object>
|
||||||
return decodeContent(null, c);
|
return decodeContent(null, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Because HttpMessage and HttpChunk is a mutable object, we can simply forward it.
|
// Because FullHttpMessage and HttpChunk is a mutable object, we can simply forward it.
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object[] decodeContent(HttpHeader header, HttpContent c) {
|
private Object[] decodeContent(HttpMessage header, HttpContent c) {
|
||||||
ByteBuf newContent = Unpooled.buffer();
|
ByteBuf newContent = Unpooled.buffer();
|
||||||
ByteBuf content = c.getContent();
|
ByteBuf content = c.data();
|
||||||
decode(content, newContent);
|
decode(content, newContent);
|
||||||
|
|
||||||
if (c instanceof LastHttpContent) {
|
if (c instanceof LastHttpContent) {
|
||||||
|
|
|
@ -20,7 +20,7 @@ import io.netty.handler.codec.compression.ZlibCodecFactory;
|
||||||
import io.netty.handler.codec.compression.ZlibWrapper;
|
import io.netty.handler.codec.compression.ZlibWrapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decompresses an {@link HttpHeader} and an {@link HttpContent} compressed in
|
* Decompresses an {@link HttpMessage} and an {@link HttpContent} compressed in
|
||||||
* {@code gzip} or {@code deflate} encoding. For more information on how this
|
* {@code gzip} or {@code deflate} encoding. For more information on how this
|
||||||
* handler modifies the message, please refer to {@link HttpContentDecoder}.
|
* handler modifies the message, please refer to {@link HttpContentDecoder}.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufHolder;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.embedded.EmbeddedByteChannel;
|
import io.netty.channel.embedded.EmbeddedByteChannel;
|
||||||
|
@ -25,20 +26,20 @@ import java.util.ArrayDeque;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes the content of the outbound {@link HttpResponseHeader} and {@link HttpContent}.
|
* Encodes the content of the outbound {@link HttpResponse} and {@link HttpContent}.
|
||||||
* The original content is replaced with the new content encoded by the
|
* The original content is replaced with the new content encoded by the
|
||||||
* {@link EmbeddedByteChannel}, which is created by {@link #beginEncode(HttpHeader, HttpContent, String)}.
|
* {@link EmbeddedByteChannel}, which is created by {@link #beginEncode(HttpMessage, HttpContent, String)}.
|
||||||
* Once encoding is finished, the value of the <tt>'Content-Encoding'</tt> header
|
* Once encoding is finished, the value of the <tt>'Content-Encoding'</tt> header
|
||||||
* is set to the target content encoding, as returned by
|
* is set to the target content encoding, as returned by
|
||||||
* {@link #beginEncode(HttpHeader, HttpContent, String)}.
|
* {@link #beginEncode(HttpMessage, HttpContent, String)}.
|
||||||
* Also, the <tt>'Content-Length'</tt> header is updated to the length of the
|
* Also, the <tt>'Content-Length'</tt> header is updated to the length of the
|
||||||
* encoded content. If there is no supported or allowed encoding in the
|
* encoded content. If there is no supported or allowed encoding in the
|
||||||
* corresponding {@link HttpRequestHeader}'s {@code "Accept-Encoding"} header,
|
* corresponding {@link HttpRequest}'s {@code "Accept-Encoding"} header,
|
||||||
* {@link #beginEncode(HttpHeader, HttpContent, String)} should return {@code null} so that
|
* {@link #beginEncode(HttpMessage, HttpContent, String)} should return {@code null} so that
|
||||||
* no encoding occurs (i.e. pass-through).
|
* no encoding occurs (i.e. pass-through).
|
||||||
* <p>
|
* <p>
|
||||||
* Please note that this is an abstract class. You have to extend this class
|
* Please note that this is an abstract class. You have to extend this class
|
||||||
* and implement {@link #beginEncode(HttpHeader, HttpContent, String)} properly to make
|
* and implement {@link #beginEncode(HttpMessage, HttpContent, String)} properly to make
|
||||||
* this class functional. For example, refer to the source code of
|
* this class functional. For example, refer to the source code of
|
||||||
* {@link HttpContentCompressor}.
|
* {@link HttpContentCompressor}.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -46,11 +47,11 @@ import java.util.Queue;
|
||||||
* so that this handler can intercept HTTP responses before {@link HttpObjectEncoder}
|
* so that this handler can intercept HTTP responses before {@link HttpObjectEncoder}
|
||||||
* converts them into {@link ByteBuf}s.
|
* converts them into {@link ByteBuf}s.
|
||||||
*/
|
*/
|
||||||
public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeader, Object> {
|
public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpMessage, Object> {
|
||||||
|
|
||||||
private final Queue<String> acceptEncodingQueue = new ArrayDeque<String>();
|
private final Queue<String> acceptEncodingQueue = new ArrayDeque<String>();
|
||||||
private EmbeddedByteChannel encoder;
|
private EmbeddedByteChannel encoder;
|
||||||
private HttpHeader header;
|
private HttpMessage header;
|
||||||
private boolean encodeStarted;
|
private boolean encodeStarted;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,14 +59,14 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeade
|
||||||
*/
|
*/
|
||||||
protected HttpContentEncoder() {
|
protected HttpContentEncoder() {
|
||||||
super(
|
super(
|
||||||
new Class<?>[] { HttpHeader.class },
|
new Class<?>[] { HttpMessage.class },
|
||||||
new Class<?>[] { HttpObject.class });
|
new Class<?>[] { HttpObject.class });
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object decode(ChannelHandlerContext ctx, HttpHeader msg)
|
protected Object decode(ChannelHandlerContext ctx, HttpMessage msg)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
String acceptedEncoding = msg.getHeader(HttpHeaders.Names.ACCEPT_ENCODING);
|
String acceptedEncoding = msg.headers().get(HttpHeaders.Names.ACCEPT_ENCODING);
|
||||||
if (acceptedEncoding == null) {
|
if (acceptedEncoding == null) {
|
||||||
acceptedEncoding = HttpHeaders.Values.IDENTITY;
|
acceptedEncoding = HttpHeaders.Values.IDENTITY;
|
||||||
}
|
}
|
||||||
|
@ -78,31 +79,31 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeade
|
||||||
public Object encode(ChannelHandlerContext ctx, Object msg)
|
public Object encode(ChannelHandlerContext ctx, Object msg)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
if (msg instanceof HttpResponseHeader && ((HttpResponseHeader) msg).getStatus().getCode() == 100) {
|
if (msg instanceof HttpResponse && ((HttpResponse) msg).status().code() == 100) {
|
||||||
// 100-continue response must be passed through.
|
// 100-continue response must be passed through.
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
if (msg instanceof HttpHeader) {
|
if (msg instanceof HttpMessage) {
|
||||||
assert header == null;
|
assert header == null;
|
||||||
|
|
||||||
// check if this message is also of type HttpContent is such case just make a safe copy of the headers
|
// check if this message is also of type HttpContent is such case just make a safe copy of the headers
|
||||||
// as the content will get handled later and this simplify the handling
|
// as the content will get handled later and this simplify the handling
|
||||||
if (msg instanceof HttpContent) {
|
if (msg instanceof HttpContent) {
|
||||||
if (msg instanceof HttpRequestHeader) {
|
if (msg instanceof HttpRequest) {
|
||||||
HttpRequestHeader reqHeader = (HttpRequestHeader) msg;
|
HttpRequest reqHeader = (HttpRequest) msg;
|
||||||
header = new DefaultHttpRequestHeader(reqHeader.getProtocolVersion(), reqHeader.getMethod(),
|
header = new DefaultHttpRequest(reqHeader.protocolVersion(), reqHeader.method(),
|
||||||
reqHeader.getUri());
|
reqHeader.uri());
|
||||||
HttpHeaders.setHeaders(reqHeader, header);
|
HttpHeaders.setHeaders(reqHeader, header);
|
||||||
} else if (msg instanceof HttpResponseHeader) {
|
} else if (msg instanceof HttpResponse) {
|
||||||
HttpResponseHeader responseHeader = (HttpResponseHeader) msg;
|
HttpResponse responseHeader = (HttpResponse) msg;
|
||||||
header = new DefaultHttpResponseHeader(responseHeader.getProtocolVersion(),
|
header = new DefaultHttpResponse(responseHeader.protocolVersion(),
|
||||||
responseHeader.getStatus());
|
responseHeader.status());
|
||||||
HttpHeaders.setHeaders(responseHeader, header);
|
HttpHeaders.setHeaders(responseHeader, header);
|
||||||
} else {
|
} else {
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
header = (HttpHeader) msg;
|
header = (HttpMessage) msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup();
|
cleanup();
|
||||||
|
@ -113,7 +114,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeade
|
||||||
|
|
||||||
if (!encodeStarted) {
|
if (!encodeStarted) {
|
||||||
encodeStarted = true;
|
encodeStarted = true;
|
||||||
HttpHeader header = this.header;
|
HttpMessage header = this.header;
|
||||||
this.header = null;
|
this.header = null;
|
||||||
|
|
||||||
// Determine the content encoding.
|
// Determine the content encoding.
|
||||||
|
@ -124,25 +125,29 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeade
|
||||||
Result result = beginEncode(header, c, acceptEncoding);
|
Result result = beginEncode(header, c, acceptEncoding);
|
||||||
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
return new Object[] { header, c };
|
if (c instanceof LastHttpContent) {
|
||||||
|
return new Object[] { header, new DefaultLastHttpContent(c.data()) };
|
||||||
|
} else {
|
||||||
|
return new Object[] { header, new DefaultHttpContent(c.data()) };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
encoder = result.getContentEncoder();
|
encoder = result.getContentEncoder();
|
||||||
|
|
||||||
// Encode the content and remove or replace the existing headers
|
// Encode the content and remove or replace the existing headers
|
||||||
// so that the message looks like a decoded message.
|
// so that the message looks like a decoded message.
|
||||||
header.setHeader(
|
header.headers().set(
|
||||||
HttpHeaders.Names.CONTENT_ENCODING,
|
HttpHeaders.Names.CONTENT_ENCODING,
|
||||||
result.getTargetContentEncoding());
|
result.getTargetContentEncoding());
|
||||||
|
|
||||||
Object[] encoded = encodeContent(header, c);
|
Object[] encoded = encodeContent(header, c);
|
||||||
|
|
||||||
if (!HttpHeaders.isTransferEncodingChunked(header) && encoded.length == 3) {
|
if (!HttpHeaders.isTransferEncodingChunked(header) && encoded.length == 3) {
|
||||||
if (header.containsHeader(HttpHeaders.Names.CONTENT_LENGTH)) {
|
if (header.headers().contains(HttpHeaders.Names.CONTENT_LENGTH)) {
|
||||||
long length = ((HttpContent) encoded[1]).getContent().readableBytes() +
|
long length = ((ByteBufHolder) encoded[1]).data().readableBytes() +
|
||||||
((HttpContent) encoded[2]).getContent().readableBytes();
|
((ByteBufHolder) encoded[2]).data().readableBytes();
|
||||||
|
|
||||||
header.setHeader(
|
header.headers().set(
|
||||||
HttpHeaders.Names.CONTENT_LENGTH,
|
HttpHeaders.Names.CONTENT_LENGTH,
|
||||||
Long.toString(length));
|
Long.toString(length));
|
||||||
}
|
}
|
||||||
|
@ -157,9 +162,9 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeade
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object[] encodeContent(HttpHeader header, HttpContent c) {
|
private Object[] encodeContent(HttpMessage header, HttpContent c) {
|
||||||
ByteBuf newContent = Unpooled.buffer();
|
ByteBuf newContent = Unpooled.buffer();
|
||||||
ByteBuf content = c.getContent();
|
ByteBuf content = c.data();
|
||||||
encode(content, newContent);
|
encode(content, newContent);
|
||||||
|
|
||||||
if (c instanceof LastHttpContent) {
|
if (c instanceof LastHttpContent) {
|
||||||
|
@ -200,7 +205,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpHeade
|
||||||
* {@code null} if {@code acceptEncoding} is unsupported or rejected
|
* {@code null} if {@code acceptEncoding} is unsupported or rejected
|
||||||
* and thus the content should be handled as-is (i.e. no encoding).
|
* and thus the content should be handled as-is (i.e. no encoding).
|
||||||
*/
|
*/
|
||||||
protected abstract Result beginEncode(HttpHeader header, HttpContent msg, String acceptEncoding) throws Exception;
|
protected abstract Result beginEncode(HttpMessage header, HttpContent msg, String acceptEncoding) throws Exception;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterRemove(ChannelHandlerContext ctx) throws Exception {
|
public void afterRemove(ChannelHandlerContext ctx) throws Exception {
|
||||||
|
|
|
@ -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.text.ParseException;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.LinkedList;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the constants for the standard HTTP header names and values and
|
* Provides the constants for the standard HTTP header names and values and
|
||||||
* commonly used utility methods that accesses an {@link HttpHeader}.
|
* commonly used utility methods that accesses an {@link HttpMessage}.
|
||||||
* @apiviz.landmark
|
* @apiviz.landmark
|
||||||
* @apiviz.stereotype static
|
* @apiviz.stereotype static
|
||||||
*/
|
*/
|
||||||
public class HttpHeaders {
|
public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>> {
|
||||||
|
|
||||||
|
public static final HttpHeaders EMPTY_HEADERS = new HttpHeaders() {
|
||||||
|
@Override
|
||||||
|
public String get(String name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getAll(String name) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Entry<String, String>> entries() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(String name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> names() {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(String name, Object value) {
|
||||||
|
throw new UnsupportedOperationException("read only");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(String name, Iterable<?> values) {
|
||||||
|
throw new UnsupportedOperationException("read only");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(String name, Object value) {
|
||||||
|
throw new UnsupportedOperationException("read only");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(String name, Iterable<?> values) {
|
||||||
|
throw new UnsupportedOperationException("read only");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(String name) {
|
||||||
|
throw new UnsupportedOperationException("read only");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
throw new UnsupportedOperationException("read only");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Entry<String, String>> iterator() {
|
||||||
|
return entries().iterator();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Standard and CORS HTTP header names.
|
* Standard and CORS HTTP header names.
|
||||||
|
@ -483,13 +551,13 @@ public class HttpHeaders {
|
||||||
* {@code "Connection"} header first and then the return value of
|
* {@code "Connection"} header first and then the return value of
|
||||||
* {@link HttpVersion#isKeepAliveDefault()}.
|
* {@link HttpVersion#isKeepAliveDefault()}.
|
||||||
*/
|
*/
|
||||||
public static boolean isKeepAlive(HttpHeader message) {
|
public static boolean isKeepAlive(HttpMessage message) {
|
||||||
String connection = message.getHeader(Names.CONNECTION);
|
String connection = message.headers().get(Names.CONNECTION);
|
||||||
if (Values.CLOSE.equalsIgnoreCase(connection)) {
|
if (Values.CLOSE.equalsIgnoreCase(connection)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.getProtocolVersion().isKeepAliveDefault()) {
|
if (message.protocolVersion().isKeepAliveDefault()) {
|
||||||
return !Values.CLOSE.equalsIgnoreCase(connection);
|
return !Values.CLOSE.equalsIgnoreCase(connection);
|
||||||
} else {
|
} else {
|
||||||
return Values.KEEP_ALIVE.equalsIgnoreCase(connection);
|
return Values.KEEP_ALIVE.equalsIgnoreCase(connection);
|
||||||
|
@ -515,18 +583,18 @@ public class HttpHeaders {
|
||||||
* </ul></li>
|
* </ul></li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public static void setKeepAlive(HttpHeader message, boolean keepAlive) {
|
public static void setKeepAlive(HttpMessage message, boolean keepAlive) {
|
||||||
if (message.getProtocolVersion().isKeepAliveDefault()) {
|
if (message.protocolVersion().isKeepAliveDefault()) {
|
||||||
if (keepAlive) {
|
if (keepAlive) {
|
||||||
message.removeHeader(Names.CONNECTION);
|
message.headers().remove(Names.CONNECTION);
|
||||||
} else {
|
} else {
|
||||||
message.setHeader(Names.CONNECTION, Values.CLOSE);
|
message.headers().set(Names.CONNECTION, Values.CLOSE);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (keepAlive) {
|
if (keepAlive) {
|
||||||
message.setHeader(Names.CONNECTION, Values.KEEP_ALIVE);
|
message.headers().set(Names.CONNECTION, Values.KEEP_ALIVE);
|
||||||
} else {
|
} else {
|
||||||
message.removeHeader(Names.CONNECTION);
|
message.headers().remove(Names.CONNECTION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -538,8 +606,8 @@ public class HttpHeaders {
|
||||||
*
|
*
|
||||||
* @return the header value or {@code null} if there is no such header
|
* @return the header value or {@code null} if there is no such header
|
||||||
*/
|
*/
|
||||||
public static String getHeader(HttpHeader message, String name) {
|
public static String getHeader(HttpMessage message, String name) {
|
||||||
return message.getHeader(name);
|
return message.headers().get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -550,8 +618,8 @@ public class HttpHeaders {
|
||||||
* @return the header value or the {@code defaultValue} if there is no such
|
* @return the header value or the {@code defaultValue} if there is no such
|
||||||
* header
|
* header
|
||||||
*/
|
*/
|
||||||
public static String getHeader(HttpHeader message, String name, String defaultValue) {
|
public static String getHeader(HttpMessage message, String name, String defaultValue) {
|
||||||
String value = message.getHeader(name);
|
String value = message.headers().get(name);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
@ -566,8 +634,8 @@ public class HttpHeaders {
|
||||||
* and {@link Calendar} which are formatted to the date format defined in
|
* and {@link Calendar} which are formatted to the date format defined in
|
||||||
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
|
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
|
||||||
*/
|
*/
|
||||||
public static void setHeader(HttpHeader message, String name, Object value) {
|
public static void setHeader(HttpMessage message, String name, Object value) {
|
||||||
message.setHeader(name, value);
|
message.headers().set(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -584,8 +652,8 @@ public class HttpHeaders {
|
||||||
* }
|
* }
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
public static void setHeader(HttpHeader message, String name, Iterable<?> values) {
|
public static void setHeader(HttpMessage message, String name, Iterable<?> values) {
|
||||||
message.setHeader(name, values);
|
message.headers().set(name, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -595,22 +663,22 @@ public class HttpHeaders {
|
||||||
* and {@link Calendar} which are formatted to the date format defined in
|
* and {@link Calendar} which are formatted to the date format defined in
|
||||||
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
|
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
|
||||||
*/
|
*/
|
||||||
public static void addHeader(HttpHeader message, String name, Object value) {
|
public static void addHeader(HttpMessage message, String name, Object value) {
|
||||||
message.addHeader(name, value);
|
message.headers().add(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the header with the specified name.
|
* Removes the header with the specified name.
|
||||||
*/
|
*/
|
||||||
public static void removeHeader(HttpHeader message, String name) {
|
public static void removeHeader(HttpMessage message, String name) {
|
||||||
message.removeHeader(name);
|
message.headers().remove(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes all headers from the specified message.
|
* Removes all headers from the specified message.
|
||||||
*/
|
*/
|
||||||
public static void clearHeaders(HttpHeader message) {
|
public static void clearHeaders(HttpMessage message) {
|
||||||
message.clearHeaders();
|
message.headers().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -622,7 +690,7 @@ public class HttpHeaders {
|
||||||
* @throws NumberFormatException
|
* @throws NumberFormatException
|
||||||
* if there is no such header or the header value is not a number
|
* if there is no such header or the header value is not a number
|
||||||
*/
|
*/
|
||||||
public static int getIntHeader(HttpHeader message, String name) {
|
public static int getIntHeader(HttpMessage message, String name) {
|
||||||
String value = getHeader(message, name);
|
String value = getHeader(message, name);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
throw new NumberFormatException("header not found: " + name);
|
throw new NumberFormatException("header not found: " + name);
|
||||||
|
@ -638,7 +706,7 @@ public class HttpHeaders {
|
||||||
* @return the header value or the {@code defaultValue} if there is no such
|
* @return the header value or the {@code defaultValue} if there is no such
|
||||||
* header or the header value is not a number
|
* header or the header value is not a number
|
||||||
*/
|
*/
|
||||||
public static int getIntHeader(HttpHeader message, String name, int defaultValue) {
|
public static int getIntHeader(HttpMessage message, String name, int defaultValue) {
|
||||||
String value = getHeader(message, name);
|
String value = getHeader(message, name);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
|
@ -655,23 +723,23 @@ public class HttpHeaders {
|
||||||
* Sets a new integer header with the specified name and value. If there
|
* Sets a new integer header with the specified name and value. If there
|
||||||
* is an existing header with the same name, the existing header is removed.
|
* is an existing header with the same name, the existing header is removed.
|
||||||
*/
|
*/
|
||||||
public static void setIntHeader(HttpHeader message, String name, int value) {
|
public static void setIntHeader(HttpMessage message, String name, int value) {
|
||||||
message.setHeader(name, value);
|
message.headers().set(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a new integer header with the specified name and values. If there
|
* Sets a new integer header with the specified name and values. If there
|
||||||
* is an existing header with the same name, the existing header is removed.
|
* is an existing header with the same name, the existing header is removed.
|
||||||
*/
|
*/
|
||||||
public static void setIntHeader(HttpHeader message, String name, Iterable<Integer> values) {
|
public static void setIntHeader(HttpMessage message, String name, Iterable<Integer> values) {
|
||||||
message.setHeader(name, values);
|
message.headers().set(name, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new integer header with the specified name and value.
|
* Adds a new integer header with the specified name and value.
|
||||||
*/
|
*/
|
||||||
public static void addIntHeader(HttpHeader message, String name, int value) {
|
public static void addIntHeader(HttpMessage message, String name, int value) {
|
||||||
message.addHeader(name, value);
|
message.headers().add(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -683,7 +751,7 @@ public class HttpHeaders {
|
||||||
* @throws ParseException
|
* @throws ParseException
|
||||||
* if there is no such header or the header value is not a formatted date
|
* if there is no such header or the header value is not a formatted date
|
||||||
*/
|
*/
|
||||||
public static Date getDateHeader(HttpHeader message, String name) throws ParseException {
|
public static Date getDateHeader(HttpMessage message, String name) throws ParseException {
|
||||||
String value = getHeader(message, name);
|
String value = getHeader(message, name);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
throw new ParseException("header not found: " + name, 0);
|
throw new ParseException("header not found: " + name, 0);
|
||||||
|
@ -699,7 +767,7 @@ public class HttpHeaders {
|
||||||
* @return the header value or the {@code defaultValue} if there is no such
|
* @return the header value or the {@code defaultValue} if there is no such
|
||||||
* header or the header value is not a formatted date
|
* header or the header value is not a formatted date
|
||||||
*/
|
*/
|
||||||
public static Date getDateHeader(HttpHeader message, String name, Date defaultValue) {
|
public static Date getDateHeader(HttpMessage message, String name, Date defaultValue) {
|
||||||
final String value = getHeader(message, name);
|
final String value = getHeader(message, name);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
|
@ -718,11 +786,11 @@ public class HttpHeaders {
|
||||||
* The specified value is formatted as defined in
|
* The specified value is formatted as defined in
|
||||||
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>
|
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>
|
||||||
*/
|
*/
|
||||||
public static void setDateHeader(HttpHeader message, String name, Date value) {
|
public static void setDateHeader(HttpMessage message, String name, Date value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
message.setHeader(name, new HttpHeaderDateFormat().format(value));
|
message.headers().set(name, new HttpHeaderDateFormat().format(value));
|
||||||
} else {
|
} else {
|
||||||
message.setHeader(name, null);
|
message.headers().set(name, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -732,8 +800,8 @@ public class HttpHeaders {
|
||||||
* The specified values are formatted as defined in
|
* The specified values are formatted as defined in
|
||||||
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>
|
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>
|
||||||
*/
|
*/
|
||||||
public static void setDateHeader(HttpHeader message, String name, Iterable<Date> values) {
|
public static void setDateHeader(HttpMessage message, String name, Iterable<Date> values) {
|
||||||
message.setHeader(name, values);
|
message.headers().set(name, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -741,13 +809,13 @@ public class HttpHeaders {
|
||||||
* value is formatted as defined in
|
* value is formatted as defined in
|
||||||
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>
|
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>
|
||||||
*/
|
*/
|
||||||
public static void addDateHeader(HttpHeader message, String name, Date value) {
|
public static void addDateHeader(HttpMessage message, String name, Date value) {
|
||||||
message.addHeader(name, value);
|
message.headers().add(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the length of the content. Please note that this value is
|
* Returns the length of the content. Please note that this value is
|
||||||
* not retrieved from {@link HttpContent#getContent()} but from the
|
* not retrieved from {@link HttpContent#data()} but from the
|
||||||
* {@code "Content-Length"} header, and thus they are independent from each
|
* {@code "Content-Length"} header, and thus they are independent from each
|
||||||
* other.
|
* other.
|
||||||
*
|
*
|
||||||
|
@ -757,7 +825,7 @@ public class HttpHeaders {
|
||||||
* if the message does not have the {@code "Content-Length"} header
|
* if the message does not have the {@code "Content-Length"} header
|
||||||
* or its value is not a number
|
* or its value is not a number
|
||||||
*/
|
*/
|
||||||
public static long getContentLength(HttpHeader message) {
|
public static long getContentLength(HttpMessage message) {
|
||||||
String value = getHeader(message, Names.CONTENT_LENGTH);
|
String value = getHeader(message, Names.CONTENT_LENGTH);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
return Long.parseLong(value);
|
return Long.parseLong(value);
|
||||||
|
@ -776,7 +844,7 @@ public class HttpHeaders {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the length of the content. Please note that this value is
|
* Returns the length of the content. Please note that this value is
|
||||||
* not retrieved from {@link HttpContent#getContent()} but from the
|
* not retrieved from {@link HttpContent#data()} but from the
|
||||||
* {@code "Content-Length"} header, and thus they are independent from each
|
* {@code "Content-Length"} header, and thus they are independent from each
|
||||||
* other.
|
* other.
|
||||||
*
|
*
|
||||||
|
@ -784,8 +852,8 @@ public class HttpHeaders {
|
||||||
* not have the {@code "Content-Length"} header or its value is not
|
* not have the {@code "Content-Length"} header or its value is not
|
||||||
* a number
|
* a number
|
||||||
*/
|
*/
|
||||||
public static long getContentLength(HttpHeader message, long defaultValue) {
|
public static long getContentLength(HttpMessage message, long defaultValue) {
|
||||||
String contentLength = message.getHeader(Names.CONTENT_LENGTH);
|
String contentLength = message.headers().get(Names.CONTENT_LENGTH);
|
||||||
if (contentLength != null) {
|
if (contentLength != null) {
|
||||||
try {
|
try {
|
||||||
return Long.parseLong(contentLength);
|
return Long.parseLong(contentLength);
|
||||||
|
@ -809,20 +877,20 @@ public class HttpHeaders {
|
||||||
* Returns the content length of the specified web socket message. If the
|
* Returns the content length of the specified web socket message. If the
|
||||||
* specified message is not a web socket message, {@code -1} is returned.
|
* specified message is not a web socket message, {@code -1} is returned.
|
||||||
*/
|
*/
|
||||||
private static int getWebSocketContentLength(HttpHeader message) {
|
private static int getWebSocketContentLength(HttpMessage message) {
|
||||||
// WebSockset messages have constant content-lengths.
|
// WebSockset messages have constant content-lengths.
|
||||||
if (message instanceof HttpRequestHeader) {
|
if (message instanceof HttpRequest) {
|
||||||
HttpRequestHeader req = (HttpRequestHeader) message;
|
HttpRequest req = (HttpRequest) message;
|
||||||
if (HttpMethod.GET.equals(req.getMethod()) &&
|
if (HttpMethod.GET.equals(req.method()) &&
|
||||||
req.containsHeader(Names.SEC_WEBSOCKET_KEY1) &&
|
req.headers().contains(Names.SEC_WEBSOCKET_KEY1) &&
|
||||||
req.containsHeader(Names.SEC_WEBSOCKET_KEY2)) {
|
req.headers().contains(Names.SEC_WEBSOCKET_KEY2)) {
|
||||||
return 8;
|
return 8;
|
||||||
}
|
}
|
||||||
} else if (message instanceof HttpResponseHeader) {
|
} else if (message instanceof HttpResponse) {
|
||||||
HttpResponseHeader res = (HttpResponseHeader) message;
|
HttpResponse res = (HttpResponse) message;
|
||||||
if (res.getStatus().getCode() == 101 &&
|
if (res.status().code() == 101 &&
|
||||||
res.containsHeader(Names.SEC_WEBSOCKET_ORIGIN) &&
|
res.headers().contains(Names.SEC_WEBSOCKET_ORIGIN) &&
|
||||||
res.containsHeader(Names.SEC_WEBSOCKET_LOCATION)) {
|
res.headers().contains(Names.SEC_WEBSOCKET_LOCATION)) {
|
||||||
return 16;
|
return 16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -834,30 +902,30 @@ public class HttpHeaders {
|
||||||
/**
|
/**
|
||||||
* Sets the {@code "Content-Length"} header.
|
* Sets the {@code "Content-Length"} header.
|
||||||
*/
|
*/
|
||||||
public static void setContentLength(HttpHeader message, long length) {
|
public static void setContentLength(HttpMessage message, long length) {
|
||||||
message.setHeader(Names.CONTENT_LENGTH, length);
|
message.headers().set(Names.CONTENT_LENGTH, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value of the {@code "Host"} header.
|
* Returns the value of the {@code "Host"} header.
|
||||||
*/
|
*/
|
||||||
public static String getHost(HttpHeader message) {
|
public static String getHost(HttpMessage message) {
|
||||||
return message.getHeader(Names.HOST);
|
return message.headers().get(Names.HOST);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value of the {@code "Host"} header. If there is no such
|
* Returns the value of the {@code "Host"} header. If there is no such
|
||||||
* header, the {@code defaultValue} is returned.
|
* header, the {@code defaultValue} is returned.
|
||||||
*/
|
*/
|
||||||
public static String getHost(HttpHeader message, String defaultValue) {
|
public static String getHost(HttpMessage message, String defaultValue) {
|
||||||
return getHeader(message, Names.HOST, defaultValue);
|
return getHeader(message, Names.HOST, defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@code "Host"} header.
|
* Sets the {@code "Host"} header.
|
||||||
*/
|
*/
|
||||||
public static void setHost(HttpHeader message, String value) {
|
public static void setHost(HttpMessage message, String value) {
|
||||||
message.setHeader(Names.HOST, value);
|
message.headers().set(Names.HOST, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -866,7 +934,7 @@ public class HttpHeaders {
|
||||||
* @throws ParseException
|
* @throws ParseException
|
||||||
* if there is no such header or the header value is not a formatted date
|
* if there is no such header or the header value is not a formatted date
|
||||||
*/
|
*/
|
||||||
public static Date getDate(HttpHeader message) throws ParseException {
|
public static Date getDate(HttpMessage message) throws ParseException {
|
||||||
return getDateHeader(message, Names.DATE);
|
return getDateHeader(message, Names.DATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -875,18 +943,18 @@ public class HttpHeaders {
|
||||||
* header or the header is not a formatted date, the {@code defaultValue}
|
* header or the header is not a formatted date, the {@code defaultValue}
|
||||||
* is returned.
|
* is returned.
|
||||||
*/
|
*/
|
||||||
public static Date getDate(HttpHeader message, Date defaultValue) {
|
public static Date getDate(HttpMessage message, Date defaultValue) {
|
||||||
return getDateHeader(message, Names.DATE, defaultValue);
|
return getDateHeader(message, Names.DATE, defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@code "Date"} header.
|
* Sets the {@code "Date"} header.
|
||||||
*/
|
*/
|
||||||
public static void setDate(HttpHeader message, Date value) {
|
public static void setDate(HttpMessage message, Date value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
message.setHeader(Names.DATE, new HttpHeaderDateFormat().format(value));
|
message.headers().set(Names.DATE, new HttpHeaderDateFormat().format(value));
|
||||||
} else {
|
} else {
|
||||||
message.setHeader(Names.DATE, null);
|
message.headers().set(Names.DATE, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -894,19 +962,19 @@ public class HttpHeaders {
|
||||||
* Returns {@code true} if and only if the specified message contains the
|
* Returns {@code true} if and only if the specified message contains the
|
||||||
* {@code "Expect: 100-continue"} header.
|
* {@code "Expect: 100-continue"} header.
|
||||||
*/
|
*/
|
||||||
public static boolean is100ContinueExpected(HttpHeader message) {
|
public static boolean is100ContinueExpected(HttpMessage message) {
|
||||||
// Expect: 100-continue is for requests only.
|
// Expect: 100-continue is for requests only.
|
||||||
if (!(message instanceof HttpRequestHeader)) {
|
if (!(message instanceof HttpRequest)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// It works only on HTTP/1.1 or later.
|
// It works only on HTTP/1.1 or later.
|
||||||
if (message.getProtocolVersion().compareTo(HttpVersion.HTTP_1_1) < 0) {
|
if (message.protocolVersion().compareTo(HttpVersion.HTTP_1_1) < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// In most cases, there will be one or zero 'Expect' header.
|
// In most cases, there will be one or zero 'Expect' header.
|
||||||
String value = message.getHeader(Names.EXPECT);
|
String value = message.headers().get(Names.EXPECT);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -915,7 +983,7 @@ public class HttpHeaders {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiple 'Expect' headers. Search through them.
|
// Multiple 'Expect' headers. Search through them.
|
||||||
for (String v: message.getHeaders(Names.EXPECT)) {
|
for (String v: message.headers().getAll(Names.EXPECT)) {
|
||||||
if (Values.CONTINUE.equalsIgnoreCase(v)) {
|
if (Values.CONTINUE.equalsIgnoreCase(v)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -928,7 +996,7 @@ public class HttpHeaders {
|
||||||
* If there is any existing {@code "Expect"} header, they are replaced with
|
* If there is any existing {@code "Expect"} header, they are replaced with
|
||||||
* the new one.
|
* the new one.
|
||||||
*/
|
*/
|
||||||
public static void set100ContinueExpected(HttpHeader message) {
|
public static void set100ContinueExpected(HttpMessage message) {
|
||||||
set100ContinueExpected(message, true);
|
set100ContinueExpected(message, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -939,20 +1007,20 @@ public class HttpHeaders {
|
||||||
* {@code "Expect"} headers are removed. Otherwise, all {@code "Expect"}
|
* {@code "Expect"} headers are removed. Otherwise, all {@code "Expect"}
|
||||||
* headers are removed completely.
|
* headers are removed completely.
|
||||||
*/
|
*/
|
||||||
public static void set100ContinueExpected(HttpHeader message, boolean set) {
|
public static void set100ContinueExpected(HttpMessage message, boolean set) {
|
||||||
if (set) {
|
if (set) {
|
||||||
message.setHeader(Names.EXPECT, Values.CONTINUE);
|
message.headers().set(Names.EXPECT, Values.CONTINUE);
|
||||||
} else {
|
} else {
|
||||||
message.removeHeader(Names.EXPECT);
|
message.headers().remove(Names.EXPECT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the headers on the dst like they are set on the src
|
* Set the headers on the dst like they are set on the src
|
||||||
*/
|
*/
|
||||||
public static void setHeaders(HttpHeader src, HttpHeader dst) {
|
public static void setHeaders(HttpMessage src, HttpMessage dst) {
|
||||||
for (String name: src.getHeaderNames()) {
|
for (String name: src.headers().names()) {
|
||||||
dst.setHeader(name, src.getHeaders(name));
|
dst.headers().set(name, src.headers().getAll(name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -1065,13 +1133,13 @@ public class HttpHeaders {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks to see if the transfer encoding in a specified {@link HttpHeader} is chunked
|
* Checks to see if the transfer encoding in a specified {@link HttpMessage} is chunked
|
||||||
*
|
*
|
||||||
* @param message The message to check
|
* @param message The message to check
|
||||||
* @return True if transfer encoding is chunked, otherwise false
|
* @return True if transfer encoding is chunked, otherwise false
|
||||||
*/
|
*/
|
||||||
public static boolean isTransferEncodingChunked(HttpHeader message) {
|
public static boolean isTransferEncodingChunked(HttpMessage message) {
|
||||||
List<String> transferEncodingHeaders = message.getHeaders(Names.TRANSFER_ENCODING);
|
List<String> transferEncodingHeaders = message.headers().getAll(Names.TRANSFER_ENCODING);
|
||||||
if (transferEncodingHeaders.isEmpty()) {
|
if (transferEncodingHeaders.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1084,322 +1152,167 @@ public class HttpHeaders {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void removeTransferEncodingChunked(HttpHeader m) {
|
public static void removeTransferEncodingChunked(HttpMessage m) {
|
||||||
List<String> values = m.getHeaders(Names.TRANSFER_ENCODING);
|
List<String> values = m.headers().getAll(Names.TRANSFER_ENCODING);
|
||||||
values.remove(Values.CHUNKED);
|
values.remove(Values.CHUNKED);
|
||||||
if (values.isEmpty()) {
|
if (values.isEmpty()) {
|
||||||
m.removeHeader(Names.TRANSFER_ENCODING);
|
m.headers().remove(Names.TRANSFER_ENCODING);
|
||||||
} else {
|
} else {
|
||||||
m.setHeader(Names.TRANSFER_ENCODING, values);
|
m.headers().set(Names.TRANSFER_ENCODING, values);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setTransferEncodingChunked(HttpHeader m) {
|
public static void setTransferEncodingChunked(HttpMessage m) {
|
||||||
addHeader(m, Names.TRANSFER_ENCODING, Values.CHUNKED);
|
addHeader(m, Names.TRANSFER_ENCODING, Values.CHUNKED);
|
||||||
removeHeader(m, Names.CONTENT_LENGTH);
|
removeHeader(m, Names.CONTENT_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isContentLengthSet(HttpHeader m) {
|
public static boolean isContentLengthSet(HttpMessage m) {
|
||||||
List<String> contentLength = m.getHeaders(Names.CONTENT_LENGTH);
|
List<String> contentLength = m.headers().getAll(Names.CONTENT_LENGTH);
|
||||||
return !contentLength.isEmpty();
|
return !contentLength.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int BUCKET_SIZE = 17;
|
protected HttpHeaders() { }
|
||||||
|
|
||||||
private static int hash(String name) {
|
/**
|
||||||
int h = 0;
|
* Returns the value of a header with the specified name. If there are
|
||||||
for (int i = name.length() - 1; i >= 0; i --) {
|
* more than one values for the specified name, the first value is returned.
|
||||||
char c = name.charAt(i);
|
*
|
||||||
if (c >= 'A' && c <= 'Z') {
|
* @param name The name of the header to search
|
||||||
c += 32;
|
* @return The first header value or {@code null} if there is no such header
|
||||||
}
|
*/
|
||||||
h = 31 * h + c;
|
public abstract String get(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the values of headers with the specified name
|
||||||
|
*
|
||||||
|
* @param name The name of the headers to search
|
||||||
|
* @return A {@link List} of header values which will be empty if no values
|
||||||
|
* are found
|
||||||
|
*/
|
||||||
|
public abstract List<String> getAll(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the all headers that this message contains.
|
||||||
|
*
|
||||||
|
* @return A {@link List} of the header name-value entries, which will be
|
||||||
|
* empty if no pairs are found
|
||||||
|
*/
|
||||||
|
public abstract List<Map.Entry<String, String>> entries();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if there is a header with the specified name
|
||||||
|
*
|
||||||
|
* @param name The name of the header to search for
|
||||||
|
* @return True if at least one header is found
|
||||||
|
*/
|
||||||
|
public abstract boolean contains(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if no header exists.
|
||||||
|
*/
|
||||||
|
public abstract boolean isEmpty();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a {@link Set} of all header names that this message contains
|
||||||
|
*
|
||||||
|
* @return A {@link Set} of all header names
|
||||||
|
*/
|
||||||
|
public abstract Set<String> names();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new header with the specified name and value.
|
||||||
|
*
|
||||||
|
* If the specified value is not a {@link String}, it is converted
|
||||||
|
* into a {@link String} by {@link Object#toString()}, except in the cases
|
||||||
|
* of {@link Date} and {@link Calendar}, which are formatted to the date
|
||||||
|
* format defined in <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
|
||||||
|
*
|
||||||
|
* @param name The name of the header being added
|
||||||
|
* @param value The value of the header being added
|
||||||
|
*/
|
||||||
|
public abstract void add(String name, Object value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new header with the specified name and values.
|
||||||
|
*
|
||||||
|
* This method can be represented approximately as the following code:
|
||||||
|
* <pre>
|
||||||
|
* for (Object v: values) {
|
||||||
|
* if (v == null) {
|
||||||
|
* break;
|
||||||
|
* }
|
||||||
|
* headers.add(name, v);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param name The name of the headers being set
|
||||||
|
* @param values The values of the headers being set
|
||||||
|
*/
|
||||||
|
public abstract void add(String name, Iterable<?> values);
|
||||||
|
|
||||||
|
public void add(HttpHeaders headers) {
|
||||||
|
if (headers == null) {
|
||||||
|
throw new NullPointerException("headers");
|
||||||
}
|
}
|
||||||
|
for (Map.Entry<String, String> e: headers) {
|
||||||
if (h > 0) {
|
add(e.getKey(), e.getValue());
|
||||||
return h;
|
|
||||||
} else if (h == Integer.MIN_VALUE) {
|
|
||||||
return Integer.MAX_VALUE;
|
|
||||||
} else {
|
|
||||||
return -h;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean eq(String name1, String name2) {
|
/**
|
||||||
int nameLen = name1.length();
|
* Sets a header with the specified name and value.
|
||||||
if (nameLen != name2.length()) {
|
*
|
||||||
return false;
|
* If there is an existing header with the same name, it is removed.
|
||||||
|
* If the specified value is not a {@link String}, it is converted into a
|
||||||
|
* {@link String} by {@link Object#toString()}, except for {@link Date}
|
||||||
|
* and {@link Calendar}, which are formatted to the date format defined in
|
||||||
|
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
|
||||||
|
*
|
||||||
|
* @param name The name of the header being set
|
||||||
|
* @param value The value of the header being set
|
||||||
|
*/
|
||||||
|
public abstract void set(String name, Object value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a header with the specified name and values.
|
||||||
|
*
|
||||||
|
* If there is an existing header with the same name, it is removed.
|
||||||
|
* This method can be represented approximately as the following code:
|
||||||
|
* <pre>
|
||||||
|
* headers.remove(name);
|
||||||
|
* for (Object v: values) {
|
||||||
|
* if (v == null) {
|
||||||
|
* break;
|
||||||
|
* }
|
||||||
|
* headers.add(name, v);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param name The name of the headers being set
|
||||||
|
* @param values The values of the headers being set
|
||||||
|
*/
|
||||||
|
public abstract void set(String name, Iterable<?> values);
|
||||||
|
|
||||||
|
public void set(HttpHeaders headers) {
|
||||||
|
if (headers == null) {
|
||||||
|
throw new NullPointerException("headers");
|
||||||
}
|
}
|
||||||
|
clear();
|
||||||
for (int i = nameLen - 1; i >= 0; i --) {
|
for (Map.Entry<String, String> e: headers) {
|
||||||
char c1 = name1.charAt(i);
|
add(e.getKey(), e.getValue());
|
||||||
char c2 = name2.charAt(i);
|
|
||||||
if (c1 != c2) {
|
|
||||||
if (c1 >= 'A' && c1 <= 'Z') {
|
|
||||||
c1 += 32;
|
|
||||||
}
|
|
||||||
if (c2 >= 'A' && c2 <= 'Z') {
|
|
||||||
c2 += 32;
|
|
||||||
}
|
|
||||||
if (c1 != c2) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int index(int hash) {
|
|
||||||
return hash % BUCKET_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final HeaderEntry[] entries = new HeaderEntry[BUCKET_SIZE];
|
|
||||||
private final HeaderEntry head = new HeaderEntry(-1, null, null);
|
|
||||||
|
|
||||||
HttpHeaders() {
|
|
||||||
head.before = head.after = head;
|
|
||||||
}
|
|
||||||
|
|
||||||
void validateHeaderName0(String headerName) {
|
|
||||||
validateHeaderName(headerName);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addHeader(final String name, final Object value) {
|
|
||||||
validateHeaderName0(name);
|
|
||||||
String strVal = toString(value);
|
|
||||||
validateHeaderValue(strVal);
|
|
||||||
int h = hash(name);
|
|
||||||
int i = index(h);
|
|
||||||
addHeader0(h, i, name, strVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addHeader0(int h, int i, final String name, final String value) {
|
|
||||||
// Update the hash table.
|
|
||||||
HeaderEntry e = entries[i];
|
|
||||||
HeaderEntry newEntry;
|
|
||||||
entries[i] = newEntry = new HeaderEntry(h, name, value);
|
|
||||||
newEntry.next = e;
|
|
||||||
|
|
||||||
// Update the linked list.
|
|
||||||
newEntry.addBefore(head);
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeHeader(final String name) {
|
|
||||||
if (name == null) {
|
|
||||||
throw new NullPointerException("name");
|
|
||||||
}
|
|
||||||
int h = hash(name);
|
|
||||||
int i = index(h);
|
|
||||||
removeHeader0(h, i, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeHeader0(int h, int i, String name) {
|
|
||||||
HeaderEntry e = entries[i];
|
|
||||||
if (e == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
if (e.hash == h && eq(name, e.key)) {
|
|
||||||
e.remove();
|
|
||||||
HeaderEntry next = e.next;
|
|
||||||
if (next != null) {
|
|
||||||
entries[i] = next;
|
|
||||||
e = next;
|
|
||||||
} else {
|
|
||||||
entries[i] = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
HeaderEntry next = e.next;
|
|
||||||
if (next == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (next.hash == h && eq(name, next.key)) {
|
|
||||||
e.next = next.next;
|
|
||||||
next.remove();
|
|
||||||
} else {
|
|
||||||
e = next;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setHeader(final String name, final Object value) {
|
/**
|
||||||
validateHeaderName0(name);
|
* Removes the header with the specified name.
|
||||||
String strVal = toString(value);
|
*
|
||||||
validateHeaderValue(strVal);
|
* @param name The name of the header to remove
|
||||||
int h = hash(name);
|
*/
|
||||||
int i = index(h);
|
public abstract void remove(String name);
|
||||||
removeHeader0(h, i, name);
|
|
||||||
addHeader0(h, i, name, strVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setHeader(final String name, final Iterable<?> values) {
|
/**
|
||||||
if (values == null) {
|
* Removes all headers from this {@link HttpMessage}.
|
||||||
throw new NullPointerException("values");
|
*/
|
||||||
}
|
public abstract void clear();
|
||||||
|
|
||||||
validateHeaderName0(name);
|
|
||||||
|
|
||||||
int h = hash(name);
|
|
||||||
int i = index(h);
|
|
||||||
|
|
||||||
removeHeader0(h, i, name);
|
|
||||||
for (Object v: values) {
|
|
||||||
if (v == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
String strVal = toString(v);
|
|
||||||
validateHeaderValue(strVal);
|
|
||||||
addHeader0(h, i, name, strVal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearHeaders() {
|
|
||||||
for (int i = 0; i < entries.length; i ++) {
|
|
||||||
entries[i] = null;
|
|
||||||
}
|
|
||||||
head.before = head.after = head;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getHeader(final String name) {
|
|
||||||
if (name == null) {
|
|
||||||
throw new NullPointerException("name");
|
|
||||||
}
|
|
||||||
|
|
||||||
int h = hash(name);
|
|
||||||
int i = index(h);
|
|
||||||
HeaderEntry e = entries[i];
|
|
||||||
while (e != null) {
|
|
||||||
if (e.hash == h && eq(name, e.key)) {
|
|
||||||
return e.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
e = e.next;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> getHeaders(final String name) {
|
|
||||||
if (name == null) {
|
|
||||||
throw new NullPointerException("name");
|
|
||||||
}
|
|
||||||
|
|
||||||
LinkedList<String> values = new LinkedList<String>();
|
|
||||||
|
|
||||||
int h = hash(name);
|
|
||||||
int i = index(h);
|
|
||||||
HeaderEntry e = entries[i];
|
|
||||||
while (e != null) {
|
|
||||||
if (e.hash == h && eq(name, e.key)) {
|
|
||||||
values.addFirst(e.value);
|
|
||||||
}
|
|
||||||
e = e.next;
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Map.Entry<String, String>> getHeaders() {
|
|
||||||
List<Map.Entry<String, String>> all =
|
|
||||||
new LinkedList<Map.Entry<String, String>>();
|
|
||||||
|
|
||||||
HeaderEntry e = head.after;
|
|
||||||
while (e != head) {
|
|
||||||
all.add(e);
|
|
||||||
e = e.after;
|
|
||||||
}
|
|
||||||
return all;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean containsHeader(String name) {
|
|
||||||
return getHeader(name) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<String> getHeaderNames() {
|
|
||||||
|
|
||||||
Set<String> names = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
|
|
||||||
|
|
||||||
HeaderEntry e = head.after;
|
|
||||||
while (e != head) {
|
|
||||||
names.add(e.key);
|
|
||||||
e = e.after;
|
|
||||||
}
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String toString(Object value) {
|
|
||||||
if (value == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (value instanceof String) {
|
|
||||||
return (String) value;
|
|
||||||
}
|
|
||||||
if (value instanceof Number) {
|
|
||||||
return value.toString();
|
|
||||||
}
|
|
||||||
if (value instanceof Date) {
|
|
||||||
return new HttpHeaderDateFormat().format((Date) value);
|
|
||||||
}
|
|
||||||
if (value instanceof Calendar) {
|
|
||||||
return new HttpHeaderDateFormat().format(((Calendar) value).getTime());
|
|
||||||
}
|
|
||||||
return value.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class HeaderEntry implements Map.Entry<String, String> {
|
|
||||||
final int hash;
|
|
||||||
final String key;
|
|
||||||
String value;
|
|
||||||
HeaderEntry next;
|
|
||||||
HeaderEntry before, after;
|
|
||||||
|
|
||||||
HeaderEntry(int hash, String key, String value) {
|
|
||||||
this.hash = hash;
|
|
||||||
this.key = key;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove() {
|
|
||||||
before.after = after;
|
|
||||||
after.before = before;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addBefore(HeaderEntry e) {
|
|
||||||
after = e;
|
|
||||||
before = e.before;
|
|
||||||
before.after = this;
|
|
||||||
after.before = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String setValue(String value) {
|
|
||||||
if (value == null) {
|
|
||||||
throw new NullPointerException("value");
|
|
||||||
}
|
|
||||||
validateHeaderValue(value);
|
|
||||||
String oldValue = this.value;
|
|
||||||
this.value = value;
|
|
||||||
return oldValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return key + '=' + value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013 The Netty Project
|
* Copyright 2012 The Netty Project
|
||||||
*
|
*
|
||||||
* The Netty Project licenses this file to you under the Apache License,
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
@ -15,9 +15,28 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Combines {@link HttpMessage} and {@link LastHttpContent} into one
|
* An interface that defines a HTTP message, providing common properties for
|
||||||
* message. So it represent a <i>complete</i> http message.
|
* {@link HttpRequest} and {@link HttpResponse}.
|
||||||
|
* @see HttpResponse
|
||||||
|
* @see HttpRequest
|
||||||
|
* @see HttpHeaders
|
||||||
|
*
|
||||||
|
* @apiviz.landmark
|
||||||
|
* @apiviz.has io.netty.handler.codec.http.HttpChunk oneway - - is followed by
|
||||||
*/
|
*/
|
||||||
public interface HttpMessage extends HttpHeader, LastHttpContent {
|
public interface HttpMessage extends HttpObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the protocol version of this {@link HttpMessage}
|
||||||
|
*
|
||||||
|
* @return The protocol version
|
||||||
|
*/
|
||||||
|
HttpVersion protocolVersion();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the headers of this message.
|
||||||
|
*/
|
||||||
|
HttpHeaders headers();
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,13 +154,13 @@ public class HttpMethod implements Comparable<HttpMethod> {
|
||||||
/**
|
/**
|
||||||
* Returns the name of this method.
|
* Returns the name of this method.
|
||||||
*/
|
*/
|
||||||
public String getName() {
|
public String name() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return getName().hashCode();
|
return name().hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -170,16 +170,16 @@ public class HttpMethod implements Comparable<HttpMethod> {
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpMethod that = (HttpMethod) o;
|
HttpMethod that = (HttpMethod) o;
|
||||||
return getName().equals(that.getName());
|
return name().equals(that.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getName();
|
return name();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(HttpMethod o) {
|
public int compareTo(HttpMethod o) {
|
||||||
return getName().compareTo(o.getName());
|
return name().compareTo(o.name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,14 @@ package io.netty.handler.codec.http;
|
||||||
import io.netty.handler.codec.DecoderResult;
|
import io.netty.handler.codec.DecoderResult;
|
||||||
|
|
||||||
public interface HttpObject {
|
public interface HttpObject {
|
||||||
DecoderResult getDecoderResult();
|
/**
|
||||||
void setDecoderResult(DecoderResult result);
|
* Returns the result of decoding this message.
|
||||||
|
*/
|
||||||
|
DecoderResult decoderResult();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the result of decoding this message. This method is supposed to be invoked by {@link HttpObjectDecoder}.
|
||||||
|
* Do not call this method unless you know what you are doing.
|
||||||
|
*/
|
||||||
|
void updateDecoderResult(DecoderResult result);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http.HttpHeaders.*;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.CompositeByteBuf;
|
import io.netty.buffer.CompositeByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
|
@ -29,9 +28,11 @@ import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import static io.netty.handler.codec.http.HttpHeaders.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link ChannelHandler} that aggregates an {@link HttpHeader}
|
* A {@link ChannelHandler} that aggregates an {@link HttpMessage}
|
||||||
* and its following {@link HttpContent}s into a single {@link HttpHeader} with
|
* and its following {@link HttpContent}s into a single {@link HttpMessage} with
|
||||||
* no following {@link HttpContent}s. It is useful when you don't want to take
|
* no following {@link HttpContent}s. It is useful when you don't want to take
|
||||||
* care of HTTP messages whose transfer encoding is 'chunked'. Insert this
|
* care of HTTP messages whose transfer encoding is 'chunked'. Insert this
|
||||||
* handler after {@link HttpObjectDecoder} in the {@link ChannelPipeline}:
|
* handler after {@link HttpObjectDecoder} in the {@link ChannelPipeline}:
|
||||||
|
@ -53,7 +54,7 @@ public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
|
||||||
"HTTP/1.1 100 Continue\r\n\r\n", CharsetUtil.US_ASCII);
|
"HTTP/1.1 100 Continue\r\n\r\n", CharsetUtil.US_ASCII);
|
||||||
|
|
||||||
private final int maxContentLength;
|
private final int maxContentLength;
|
||||||
private HttpMessage currentMessage;
|
private FullHttpMessage currentMessage;
|
||||||
|
|
||||||
private int maxCumulationBufferComponents = DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS;
|
private int maxCumulationBufferComponents = DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS;
|
||||||
private ChannelHandlerContext ctx;
|
private ChannelHandlerContext ctx;
|
||||||
|
@ -111,10 +112,12 @@ public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object decode(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
|
protected Object decode(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
|
||||||
HttpMessage currentMessage = this.currentMessage;
|
FullHttpMessage currentMessage = this.currentMessage;
|
||||||
|
|
||||||
if (msg instanceof HttpHeader) {
|
if (msg instanceof HttpMessage) {
|
||||||
HttpHeader m = (HttpHeader) msg;
|
assert currentMessage == null;
|
||||||
|
|
||||||
|
HttpMessage m = (HttpMessage) msg;
|
||||||
|
|
||||||
// Handle the 'Expect: 100-continue' header if necessary.
|
// Handle the 'Expect: 100-continue' header if necessary.
|
||||||
// TODO: Respond with 413 Request Entity Too Large
|
// TODO: Respond with 413 Request Entity Too Large
|
||||||
|
@ -125,41 +128,40 @@ public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
|
||||||
ctx.write(CONTINUE.duplicate());
|
ctx.write(CONTINUE.duplicate());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m.getDecoderResult().isSuccess()) {
|
if (!m.decoderResult().isSuccess()) {
|
||||||
removeTransferEncodingChunked(m);
|
removeTransferEncodingChunked(m);
|
||||||
this.currentMessage = null;
|
this.currentMessage = null;
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
if (msg instanceof HttpRequestHeader) {
|
if (msg instanceof HttpRequest) {
|
||||||
HttpRequestHeader header = (HttpRequestHeader) msg;
|
HttpRequest header = (HttpRequest) msg;
|
||||||
this.currentMessage = new DefaultHttpRequest(header.getProtocolVersion(),
|
this.currentMessage = currentMessage = new DefaultFullHttpRequest(header.protocolVersion(),
|
||||||
header.getMethod(), header.getUri());
|
header.method(), header.uri(), Unpooled.compositeBuffer(maxCumulationBufferComponents));
|
||||||
} else {
|
} else if (msg instanceof HttpResponse) {
|
||||||
HttpResponseHeader header = (HttpResponseHeader) msg;
|
HttpResponse header = (HttpResponse) msg;
|
||||||
this.currentMessage = new DefaultHttpResponse(header.getProtocolVersion(), header.getStatus());
|
this.currentMessage = currentMessage = new DefaultFullHttpResponse(
|
||||||
|
header.protocolVersion(), header.status(),
|
||||||
|
Unpooled.compositeBuffer(maxCumulationBufferComponents));
|
||||||
|
} else {
|
||||||
|
throw new Error();
|
||||||
}
|
}
|
||||||
for (String name: m.getHeaderNames()) {
|
|
||||||
this.currentMessage.setHeader(name, m.getHeaders(name));
|
HttpHeaders headers = currentMessage.headers();
|
||||||
|
for (String name: m.headers().names()) {
|
||||||
|
headers.set(name, m.headers().get(name));
|
||||||
}
|
}
|
||||||
// A streamed message - initialize the cumulative buffer, and wait for incoming chunks.
|
// A streamed message - initialize the cumulative buffer, and wait for incoming chunks.
|
||||||
removeTransferEncodingChunked(m);
|
removeTransferEncodingChunked(currentMessage);
|
||||||
this.currentMessage.setContent(Unpooled.compositeBuffer(maxCumulationBufferComponents));
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
} else if (msg instanceof HttpContent) {
|
} else if (msg instanceof HttpContent) {
|
||||||
// Sanity check
|
assert currentMessage != null;
|
||||||
if (currentMessage == null) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"received " + HttpContent.class.getSimpleName() +
|
|
||||||
" without " + HttpHeader.class.getSimpleName() +
|
|
||||||
" or last message's transfer encoding was 'SINGLE'");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge the received chunk into the content of the current message.
|
// Merge the received chunk into the content of the current message.
|
||||||
HttpContent chunk = (HttpContent) msg;
|
HttpContent chunk = (HttpContent) msg;
|
||||||
ByteBuf content = currentMessage.getContent();
|
CompositeByteBuf content = (CompositeByteBuf) currentMessage.data();
|
||||||
|
|
||||||
if (content.readableBytes() > maxContentLength - chunk.getContent().readableBytes()) {
|
if (content.readableBytes() > maxContentLength - chunk.data().readableBytes()) {
|
||||||
// TODO: Respond with 413 Request Entity Too Large
|
// TODO: Respond with 413 Request Entity Too Large
|
||||||
// and discard the traffic or close the connection.
|
// and discard the traffic or close the connection.
|
||||||
// No need to notify the upstream handlers - just log.
|
// No need to notify the upstream handlers - just log.
|
||||||
|
@ -170,12 +172,17 @@ public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append the content of the chunk
|
// Append the content of the chunk
|
||||||
appendToCumulation(chunk.getContent());
|
if (chunk.data().readable()) {
|
||||||
|
content.addComponent(chunk.data());
|
||||||
|
content.writerIndex(content.writerIndex() + chunk.data().readableBytes());
|
||||||
|
} else {
|
||||||
|
chunk.free();
|
||||||
|
}
|
||||||
|
|
||||||
final boolean last;
|
final boolean last;
|
||||||
if (!chunk.getDecoderResult().isSuccess()) {
|
if (!chunk.decoderResult().isSuccess()) {
|
||||||
currentMessage.setDecoderResult(
|
currentMessage.updateDecoderResult(
|
||||||
DecoderResult.partialFailure(chunk.getDecoderResult().cause()));
|
DecoderResult.partialFailure(chunk.decoderResult().cause()));
|
||||||
last = true;
|
last = true;
|
||||||
} else {
|
} else {
|
||||||
last = msg instanceof LastHttpContent;
|
last = msg instanceof LastHttpContent;
|
||||||
|
@ -187,13 +194,13 @@ public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
|
||||||
// Merge trailing headers into the message.
|
// Merge trailing headers into the message.
|
||||||
if (chunk instanceof LastHttpContent) {
|
if (chunk instanceof LastHttpContent) {
|
||||||
LastHttpContent trailer = (LastHttpContent) chunk;
|
LastHttpContent trailer = (LastHttpContent) chunk;
|
||||||
for (Entry<String, String> header: trailer.getHeaders()) {
|
for (Entry<String, String> header: trailer.trailingHeaders()) {
|
||||||
currentMessage.setHeader(header.getKey(), header.getValue());
|
currentMessage.headers().add(header.getKey(), header.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the 'Content-Length' header.
|
// Set the 'Content-Length' header.
|
||||||
currentMessage.setHeader(
|
currentMessage.headers().set(
|
||||||
HttpHeaders.Names.CONTENT_LENGTH,
|
HttpHeaders.Names.CONTENT_LENGTH,
|
||||||
String.valueOf(content.readableBytes()));
|
String.valueOf(content.readableBytes()));
|
||||||
|
|
||||||
|
@ -203,21 +210,12 @@ public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException(
|
throw new Error();
|
||||||
"Only " + HttpHeader.class.getSimpleName() + " and " +
|
|
||||||
HttpContent.class.getSimpleName() + " are accepted: " + msg.getClass().getName());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendToCumulation(ByteBuf input) {
|
|
||||||
CompositeByteBuf cumulation = (CompositeByteBuf) currentMessage.getContent();
|
|
||||||
cumulation.addComponent(input);
|
|
||||||
cumulation.writerIndex(cumulation.capacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeAdd(ChannelHandlerContext ctx) throws Exception {
|
public void beforeAdd(ChannelHandlerContext ctx) throws Exception {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ import io.netty.handler.codec.TooLongFrameException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes {@link ByteBuf}s into {@link HttpHeader}s and
|
* Decodes {@link ByteBuf}s into {@link HttpMessage}s and
|
||||||
* {@link HttpContent}s.
|
* {@link HttpContent}s.
|
||||||
*
|
*
|
||||||
* <h3>Parameters that prevents excessive memory consumption</h3>
|
* <h3>Parameters that prevents excessive memory consumption</h3>
|
||||||
|
@ -59,7 +59,7 @@ import java.util.List;
|
||||||
*
|
*
|
||||||
* If the content of an HTTP message is greater than {@code maxChunkSize} or
|
* If the content of an HTTP message is greater than {@code maxChunkSize} or
|
||||||
* the transfer encoding of the HTTP message is 'chunked', this decoder
|
* the transfer encoding of the HTTP message is 'chunked', this decoder
|
||||||
* generates one {@link HttpHeader} instance and its following
|
* generates one {@link HttpMessage} instance and its following
|
||||||
* {@link HttpContent}s per single HTTP message to avoid excessive memory
|
* {@link HttpContent}s per single HTTP message to avoid excessive memory
|
||||||
* consumption. For example, the following HTTP message:
|
* consumption. For example, the following HTTP message:
|
||||||
* <pre>
|
* <pre>
|
||||||
|
@ -76,7 +76,7 @@ import java.util.List;
|
||||||
* </pre>
|
* </pre>
|
||||||
* triggers {@link HttpRequestDecoder} to generate 3 objects:
|
* triggers {@link HttpRequestDecoder} to generate 3 objects:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>An {@link HttpRequestHeader},</li>
|
* <li>An {@link HttpRequest},</li>
|
||||||
* <li>The first {@link HttpContent} whose content is {@code 'abcdefghijklmnopqrstuvwxyz'},</li>
|
* <li>The first {@link HttpContent} whose content is {@code 'abcdefghijklmnopqrstuvwxyz'},</li>
|
||||||
* <li>The second {@link LastHttpContent} whose content is {@code '1234567890abcdef'}, which marks
|
* <li>The second {@link LastHttpContent} whose content is {@code '1234567890abcdef'}, which marks
|
||||||
* the end of the content.</li>
|
* the end of the content.</li>
|
||||||
|
@ -103,7 +103,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||||
private final int maxHeaderSize;
|
private final int maxHeaderSize;
|
||||||
private final int maxChunkSize;
|
private final int maxChunkSize;
|
||||||
private ByteBuf content;
|
private ByteBuf content;
|
||||||
private HttpHeader message;
|
private HttpMessage message;
|
||||||
private long chunkSize;
|
private long chunkSize;
|
||||||
private int headerSize;
|
private int headerSize;
|
||||||
private int contentRead;
|
private int contentRead;
|
||||||
|
@ -211,7 +211,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||||
switch (nextState) {
|
switch (nextState) {
|
||||||
case READ_FIXED_LENGTH_CONTENT:
|
case READ_FIXED_LENGTH_CONTENT:
|
||||||
if (contentLength > maxChunkSize || HttpHeaders.is100ContinueExpected(message)) {
|
if (contentLength > maxChunkSize || HttpHeaders.is100ContinueExpected(message)) {
|
||||||
// Generate HttpMessage first. HttpChunks will follow.
|
// Generate FullHttpMessage first. HttpChunks will follow.
|
||||||
checkpoint(State.READ_FIXED_LENGTH_CONTENT_AS_CHUNKS);
|
checkpoint(State.READ_FIXED_LENGTH_CONTENT_AS_CHUNKS);
|
||||||
// chunkSize will be decreased as the READ_FIXED_LENGTH_CONTENT_AS_CHUNKS
|
// chunkSize will be decreased as the READ_FIXED_LENGTH_CONTENT_AS_CHUNKS
|
||||||
// state reads data chunk by chunk.
|
// state reads data chunk by chunk.
|
||||||
|
@ -221,7 +221,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||||
break;
|
break;
|
||||||
case READ_VARIABLE_LENGTH_CONTENT:
|
case READ_VARIABLE_LENGTH_CONTENT:
|
||||||
if (buffer.readableBytes() > maxChunkSize || HttpHeaders.is100ContinueExpected(message)) {
|
if (buffer.readableBytes() > maxChunkSize || HttpHeaders.is100ContinueExpected(message)) {
|
||||||
// Generate HttpMessage first. HttpChunks will follow.
|
// Generate FullHttpMessage first. HttpChunks will follow.
|
||||||
checkpoint(State.READ_VARIABLE_LENGTH_CONTENT_AS_CHUNKS);
|
checkpoint(State.READ_VARIABLE_LENGTH_CONTENT_AS_CHUNKS);
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
@ -396,10 +396,10 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isContentAlwaysEmpty(HttpHeader msg) {
|
protected boolean isContentAlwaysEmpty(HttpMessage msg) {
|
||||||
if (msg instanceof HttpResponseHeader) {
|
if (msg instanceof HttpResponse) {
|
||||||
HttpResponseHeader res = (HttpResponseHeader) msg;
|
HttpResponse res = (HttpResponse) msg;
|
||||||
int code = res.getStatus().getCode();
|
int code = res.status().code();
|
||||||
|
|
||||||
// Correctly handle return codes of 1xx.
|
// Correctly handle return codes of 1xx.
|
||||||
//
|
//
|
||||||
|
@ -407,7 +407,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||||
// - http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html Section 4.4
|
// - http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html Section 4.4
|
||||||
// - https://github.com/netty/netty/issues/222
|
// - https://github.com/netty/netty/issues/222
|
||||||
if (code >= 100 && code < 200) {
|
if (code >= 100 && code < 200) {
|
||||||
if (code == 101 && !res.containsHeader(HttpHeaders.Names.SEC_WEBSOCKET_ACCEPT)) {
|
if (code == 101 && !res.headers().contains(HttpHeaders.Names.SEC_WEBSOCKET_ACCEPT)) {
|
||||||
// It's Hixie 76 websocket handshake response
|
// It's Hixie 76 websocket handshake response
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -423,12 +423,12 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object reset() {
|
private Object reset() {
|
||||||
HttpHeader message = this.message;
|
HttpMessage message = this.message;
|
||||||
ByteBuf content = this.content;
|
ByteBuf content = this.content;
|
||||||
LastHttpContent httpContent;
|
LastHttpContent httpContent;
|
||||||
|
|
||||||
if (content == null || !content.readable()) {
|
if (content == null || !content.readable()) {
|
||||||
httpContent = HttpContent.LAST_CONTENT;
|
httpContent = LastHttpContent.EMPTY_LAST_CONTENT;
|
||||||
} else {
|
} else {
|
||||||
httpContent = new DefaultLastHttpContent(content);
|
httpContent = new DefaultLastHttpContent(content);
|
||||||
}
|
}
|
||||||
|
@ -441,13 +441,13 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpHeader invalidMessage(Exception cause) {
|
private HttpMessage invalidMessage(Exception cause) {
|
||||||
checkpoint(State.BAD_MESSAGE);
|
checkpoint(State.BAD_MESSAGE);
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
message.setDecoderResult(DecoderResult.partialFailure(cause));
|
message.updateDecoderResult(DecoderResult.partialFailure(cause));
|
||||||
} else {
|
} else {
|
||||||
message = createInvalidMessage();
|
message = createInvalidMessage();
|
||||||
message.setDecoderResult(DecoderResult.failure(cause));
|
message.updateDecoderResult(DecoderResult.failure(cause));
|
||||||
}
|
}
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
@ -455,7 +455,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||||
private HttpContent invalidChunk(Exception cause) {
|
private HttpContent invalidChunk(Exception cause) {
|
||||||
checkpoint(State.BAD_MESSAGE);
|
checkpoint(State.BAD_MESSAGE);
|
||||||
HttpContent chunk = new DefaultHttpContent(Unpooled.EMPTY_BUFFER);
|
HttpContent chunk = new DefaultHttpContent(Unpooled.EMPTY_BUFFER);
|
||||||
chunk.setDecoderResult(DecoderResult.failure(cause));
|
chunk.updateDecoderResult(DecoderResult.failure(cause));
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,19 +492,19 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||||
|
|
||||||
private State readHeaders(ByteBuf buffer) {
|
private State readHeaders(ByteBuf buffer) {
|
||||||
headerSize = 0;
|
headerSize = 0;
|
||||||
final HttpHeader message = this.message;
|
final HttpMessage message = this.message;
|
||||||
String line = readHeader(buffer);
|
String line = readHeader(buffer);
|
||||||
String name = null;
|
String name = null;
|
||||||
String value = null;
|
String value = null;
|
||||||
if (!line.isEmpty()) {
|
if (!line.isEmpty()) {
|
||||||
message.clearHeaders();
|
message.headers().clear();
|
||||||
do {
|
do {
|
||||||
char firstChar = line.charAt(0);
|
char firstChar = line.charAt(0);
|
||||||
if (name != null && (firstChar == ' ' || firstChar == '\t')) {
|
if (name != null && (firstChar == ' ' || firstChar == '\t')) {
|
||||||
value = value + ' ' + line.trim();
|
value = value + ' ' + line.trim();
|
||||||
} else {
|
} else {
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
message.addHeader(name, value);
|
message.headers().add(name, value);
|
||||||
}
|
}
|
||||||
String[] header = splitHeader(line);
|
String[] header = splitHeader(line);
|
||||||
name = header[0];
|
name = header[0];
|
||||||
|
@ -516,7 +516,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||||
|
|
||||||
// Add the last header.
|
// Add the last header.
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
message.addHeader(name, value);
|
message.headers().add(name, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -544,7 +544,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||||
do {
|
do {
|
||||||
char firstChar = line.charAt(0);
|
char firstChar = line.charAt(0);
|
||||||
if (lastHeader != null && (firstChar == ' ' || firstChar == '\t')) {
|
if (lastHeader != null && (firstChar == ' ' || firstChar == '\t')) {
|
||||||
List<String> current = trailer.getHeaders(lastHeader);
|
List<String> current = trailer.trailingHeaders().getAll(lastHeader);
|
||||||
if (!current.isEmpty()) {
|
if (!current.isEmpty()) {
|
||||||
int lastPos = current.size() - 1;
|
int lastPos = current.size() - 1;
|
||||||
String newString = current.get(lastPos) + line.trim();
|
String newString = current.get(lastPos) + line.trim();
|
||||||
|
@ -558,7 +558,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||||
if (!name.equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH) &&
|
if (!name.equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH) &&
|
||||||
!name.equalsIgnoreCase(HttpHeaders.Names.TRANSFER_ENCODING) &&
|
!name.equalsIgnoreCase(HttpHeaders.Names.TRANSFER_ENCODING) &&
|
||||||
!name.equalsIgnoreCase(HttpHeaders.Names.TRAILER)) {
|
!name.equalsIgnoreCase(HttpHeaders.Names.TRAILER)) {
|
||||||
trailer.addHeader(name, header[1]);
|
trailer.trailingHeaders().add(name, header[1]);
|
||||||
}
|
}
|
||||||
lastHeader = name;
|
lastHeader = name;
|
||||||
}
|
}
|
||||||
|
@ -569,7 +569,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||||
return trailer;
|
return trailer;
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpContent.LAST_CONTENT;
|
return LastHttpContent.EMPTY_LAST_CONTENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String readHeader(ByteBuf buffer) {
|
private String readHeader(ByteBuf buffer) {
|
||||||
|
@ -612,8 +612,8 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract boolean isDecodingRequest();
|
protected abstract boolean isDecodingRequest();
|
||||||
protected abstract HttpHeader createMessage(String[] initialLine) throws Exception;
|
protected abstract HttpMessage createMessage(String[] initialLine) throws Exception;
|
||||||
protected abstract HttpHeader createInvalidMessage();
|
protected abstract HttpMessage createInvalidMessage();
|
||||||
|
|
||||||
private static int getChunkSize(String hex) {
|
private static int getChunkSize(String hex) {
|
||||||
hex = hex.trim();
|
hex = hex.trim();
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import static io.netty.buffer.Unpooled.*;
|
|
||||||
import static io.netty.handler.codec.http.HttpConstants.*;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.MessageToByteEncoder;
|
import io.netty.handler.codec.MessageToByteEncoder;
|
||||||
|
@ -24,8 +22,11 @@ import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static io.netty.buffer.Unpooled.*;
|
||||||
|
import static io.netty.handler.codec.http.HttpConstants.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes an {@link HttpHeader} or an {@link HttpContent} into
|
* Encodes an {@link HttpMessage} or an {@link HttpContent} into
|
||||||
* a {@link ByteBuf}.
|
* a {@link ByteBuf}.
|
||||||
*
|
*
|
||||||
* <h3>Extensibility</h3>
|
* <h3>Extensibility</h3>
|
||||||
|
@ -38,9 +39,14 @@ import java.util.Map;
|
||||||
* implement all abstract methods properly.
|
* implement all abstract methods properly.
|
||||||
* @apiviz.landmark
|
* @apiviz.landmark
|
||||||
*/
|
*/
|
||||||
public abstract class HttpObjectEncoder<H extends HttpHeader> extends MessageToByteEncoder<Object> {
|
public abstract class HttpObjectEncoder<H extends HttpMessage> extends MessageToByteEncoder<Object> {
|
||||||
|
|
||||||
private boolean chunked;
|
private static final int ST_INIT = 0;
|
||||||
|
private static final int ST_CONTENT_NON_CHUNK = 1;
|
||||||
|
private static final int ST_CONTENT_CHUNK = 2;
|
||||||
|
|
||||||
|
@SuppressWarnings("RedundantFieldInitialization")
|
||||||
|
private int state = ST_INIT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
|
@ -49,38 +55,44 @@ public abstract class HttpObjectEncoder<H extends HttpHeader> extends MessageToB
|
||||||
super(HttpObject.class);
|
super(HttpObject.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
|
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
|
||||||
if (msg instanceof HttpHeader) {
|
if (msg instanceof HttpMessage) {
|
||||||
HttpHeader m = (HttpHeader) msg;
|
if (state != ST_INIT) {
|
||||||
chunked = HttpHeaders.isTransferEncodingChunked(m);
|
throw new IllegalStateException("unexpected message type: " + msg.getClass().getSimpleName());
|
||||||
// Encode the message.
|
}
|
||||||
out.markWriterIndex();
|
|
||||||
|
|
||||||
|
HttpMessage m = (HttpMessage) msg;
|
||||||
|
|
||||||
|
// Encode the message.
|
||||||
encodeInitialLine(out, (H) m);
|
encodeInitialLine(out, (H) m);
|
||||||
encodeHeaders(out, m);
|
encodeHeaders(out, m);
|
||||||
out.writeByte(CR);
|
out.writeByte(CR);
|
||||||
out.writeByte(LF);
|
out.writeByte(LF);
|
||||||
|
|
||||||
|
state = HttpHeaders.isTransferEncodingChunked(m) ? ST_CONTENT_CHUNK : ST_CONTENT_NON_CHUNK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg instanceof HttpContent) {
|
if (msg instanceof HttpContent) {
|
||||||
HttpContent chunk = (HttpContent) msg;
|
if (state == ST_INIT) {
|
||||||
|
throw new IllegalStateException("unexpected message type: " + msg.getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpContent chunk = (HttpContent) msg;
|
||||||
|
ByteBuf content = chunk.data();
|
||||||
|
int contentLength = content.readableBytes();
|
||||||
|
|
||||||
|
if (state == ST_CONTENT_NON_CHUNK) {
|
||||||
|
if (contentLength > 0) {
|
||||||
|
out.writeBytes(content, content.readerIndex(), content.readableBytes());
|
||||||
|
}
|
||||||
|
|
||||||
if (!chunked) {
|
|
||||||
ByteBuf content = chunk.getContent();
|
|
||||||
out.writeBytes(content, content.readerIndex(), content.readableBytes());
|
|
||||||
} else {
|
|
||||||
if (chunk instanceof LastHttpContent) {
|
if (chunk instanceof LastHttpContent) {
|
||||||
out.writeByte((byte) '0');
|
state = ST_INIT;
|
||||||
out.writeByte(CR);
|
}
|
||||||
out.writeByte(LF);
|
} else if (state == ST_CONTENT_CHUNK) {
|
||||||
encodeTrailingHeaders(out, (LastHttpContent) chunk);
|
if (contentLength > 0) {
|
||||||
out.writeByte(CR);
|
|
||||||
out.writeByte(LF);
|
|
||||||
} else {
|
|
||||||
ByteBuf content = chunk.getContent();
|
|
||||||
int contentLength = content.readableBytes();
|
|
||||||
out.writeBytes(copiedBuffer(Integer.toHexString(contentLength), CharsetUtil.US_ASCII));
|
out.writeBytes(copiedBuffer(Integer.toHexString(contentLength), CharsetUtil.US_ASCII));
|
||||||
out.writeByte(CR);
|
out.writeByte(CR);
|
||||||
out.writeByte(LF);
|
out.writeByte(LF);
|
||||||
|
@ -88,18 +100,30 @@ public abstract class HttpObjectEncoder<H extends HttpHeader> extends MessageToB
|
||||||
out.writeByte(CR);
|
out.writeByte(CR);
|
||||||
out.writeByte(LF);
|
out.writeByte(LF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (chunk instanceof LastHttpContent) {
|
||||||
|
out.writeByte((byte) '0');
|
||||||
|
out.writeByte(CR);
|
||||||
|
out.writeByte(LF);
|
||||||
|
encodeTrailingHeaders(out, (LastHttpContent) chunk);
|
||||||
|
out.writeByte(CR);
|
||||||
|
out.writeByte(LF);
|
||||||
|
state = ST_INIT;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void encodeHeaders(ByteBuf buf, HttpHeader message) {
|
private static void encodeHeaders(ByteBuf buf, HttpMessage message) {
|
||||||
for (Map.Entry<String, String> h: message.getHeaders()) {
|
for (Map.Entry<String, String> h: message.headers()) {
|
||||||
encodeHeader(buf, h.getKey(), h.getValue());
|
encodeHeader(buf, h.getKey(), h.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void encodeTrailingHeaders(ByteBuf buf, LastHttpContent trailer) {
|
private static void encodeTrailingHeaders(ByteBuf buf, LastHttpContent trailer) {
|
||||||
for (Map.Entry<String, String> h: trailer.getHeaders()) {
|
for (Map.Entry<String, String> h: trailer.trailingHeaders()) {
|
||||||
encodeHeader(buf, h.getKey(), h.getValue());
|
encodeHeader(buf, h.getKey(), h.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2013 The Netty Project
|
* Copyright 2012 The Netty Project
|
||||||
*
|
*
|
||||||
* The Netty Project licenses this file to you under the Apache License,
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
@ -15,9 +15,35 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Combinate the {@link HttpRequestHeader} and {@link HttpMessage}, so the request is a <i>complete</i> HTTP
|
* An HTTP request.
|
||||||
* request.
|
*
|
||||||
|
* <h3>Accessing Query Parameters and Cookie</h3>
|
||||||
|
* <p>
|
||||||
|
* Unlike the Servlet API, a query string is constructed and decomposed by
|
||||||
|
* {@link QueryStringEncoder} and {@link QueryStringDecoder}. {@link Cookie}
|
||||||
|
* support is also provided separately via {@link CookieDecoder}, {@link ClientCookieEncoder},
|
||||||
|
* and {@link @ServerCookieEncoder}.
|
||||||
|
*
|
||||||
|
* @see HttpResponse
|
||||||
|
* @see ClientCookieEncoder
|
||||||
|
* @see ServerCookieEncoder
|
||||||
|
* @see CookieDecoder
|
||||||
*/
|
*/
|
||||||
public interface HttpRequest extends HttpRequestHeader, HttpMessage {
|
public interface HttpRequest extends HttpMessage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link HttpMethod} of this {@link HttpRequest}.
|
||||||
|
*
|
||||||
|
* @return The {@link HttpMethod} of this {@link HttpRequest}
|
||||||
|
*/
|
||||||
|
HttpMethod method();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the requested URI (or alternatively, path)
|
||||||
|
*
|
||||||
|
* @return The URI being requested
|
||||||
|
*/
|
||||||
|
String uri();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ import io.netty.handler.codec.TooLongFrameException;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes {@link ByteBuf}s into {@link HttpRequestHeader}s and {@link HttpContent}s.
|
* Decodes {@link ByteBuf}s into {@link HttpRequest}s and {@link HttpContent}s.
|
||||||
*
|
*
|
||||||
* <h3>Parameters that prevents excessive memory consumption</h3>
|
* <h3>Parameters that prevents excessive memory consumption</h3>
|
||||||
* <table border="1">
|
* <table border="1">
|
||||||
|
@ -71,14 +71,14 @@ public class HttpRequestDecoder extends HttpObjectDecoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpHeader createMessage(String[] initialLine) throws Exception {
|
protected HttpMessage createMessage(String[] initialLine) throws Exception {
|
||||||
return new DefaultHttpRequestHeader(
|
return new DefaultHttpRequest(
|
||||||
HttpVersion.valueOf(initialLine[2]), HttpMethod.valueOf(initialLine[0]), initialLine[1]);
|
HttpVersion.valueOf(initialLine[2]), HttpMethod.valueOf(initialLine[0]), initialLine[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpHeader createInvalidMessage() {
|
protected HttpMessage createInvalidMessage() {
|
||||||
return new DefaultHttpRequestHeader(HttpVersion.HTTP_1_0, HttpMethod.GET, "/bad-request");
|
return new DefaultHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.GET, "/bad-request");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -15,25 +15,31 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http.HttpConstants.*;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
|
import static io.netty.handler.codec.http.HttpConstants.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes an {@link HttpRequestHeader} or an {@link HttpContent} into
|
* Encodes an {@link HttpRequest} or an {@link HttpContent} into
|
||||||
* a {@link ByteBuf}.
|
* a {@link ByteBuf}.
|
||||||
*/
|
*/
|
||||||
public class HttpRequestEncoder extends HttpObjectEncoder<HttpRequestHeader> {
|
public class HttpRequestEncoder extends HttpObjectEncoder<HttpRequest> {
|
||||||
private static final char SLASH = '/';
|
private static final char SLASH = '/';
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void encodeInitialLine(ByteBuf buf, HttpRequestHeader request) throws Exception {
|
public boolean isEncodable(Object msg) throws Exception {
|
||||||
buf.writeBytes(request.getMethod().toString().getBytes(CharsetUtil.US_ASCII));
|
return super.isEncodable(msg) && !(msg instanceof HttpResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void encodeInitialLine(ByteBuf buf, HttpRequest request) throws Exception {
|
||||||
|
buf.writeBytes(request.method().toString().getBytes(CharsetUtil.US_ASCII));
|
||||||
buf.writeByte(SP);
|
buf.writeByte(SP);
|
||||||
|
|
||||||
// Add / as absolute path if no is present.
|
// Add / as absolute path if no is present.
|
||||||
// See http://tools.ietf.org/html/rfc2616#section-5.1.2
|
// See http://tools.ietf.org/html/rfc2616#section-5.1.2
|
||||||
String uri = request.getUri();
|
String uri = request.uri();
|
||||||
int start = uri.indexOf("://");
|
int start = uri.indexOf("://");
|
||||||
if (start != -1) {
|
if (start != -1) {
|
||||||
int startIndex = start + 3;
|
int startIndex = start + 3;
|
||||||
|
@ -44,7 +50,7 @@ public class HttpRequestEncoder extends HttpObjectEncoder<HttpRequestHeader> {
|
||||||
buf.writeBytes(uri.getBytes("UTF-8"));
|
buf.writeBytes(uri.getBytes("UTF-8"));
|
||||||
|
|
||||||
buf.writeByte(SP);
|
buf.writeByte(SP);
|
||||||
buf.writeBytes(request.getProtocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
|
buf.writeBytes(request.protocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
|
||||||
buf.writeByte(CR);
|
buf.writeByte(CR);
|
||||||
buf.writeByte(LF);
|
buf.writeByte(LF);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
@ -15,9 +15,26 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Combination of a {@link HttpResponseHeader} and {@link HttpMessage}.
|
* An HTTP response.
|
||||||
* So it represent a <i>complete</i> http response.
|
*
|
||||||
|
* <h3>Accessing Cookies</h3>
|
||||||
|
* <p>
|
||||||
|
* Unlike the Servlet API, {@link Cookie} support is provided separately via {@link CookieDecoder},
|
||||||
|
* {@link ClientCookieEncoder}, and {@link ServerCookieEncoder}.
|
||||||
|
*
|
||||||
|
* @see HttpRequest
|
||||||
|
* @see CookieDecoder
|
||||||
|
* @see ClientCookieEncoder
|
||||||
|
* @see ServerCookieEncoder
|
||||||
*/
|
*/
|
||||||
public interface HttpResponse extends HttpResponseHeader, HttpMessage {
|
public interface HttpResponse extends HttpMessage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the status of this {@link HttpResponse}.
|
||||||
|
*
|
||||||
|
* @return The {@link HttpResponseStatus} of this {@link HttpResponse}
|
||||||
|
*/
|
||||||
|
HttpResponseStatus status();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ import io.netty.handler.codec.TooLongFrameException;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes {@link ByteBuf}s into {@link HttpResponseHeader}s and
|
* Decodes {@link ByteBuf}s into {@link HttpResponse}s and
|
||||||
* {@link HttpContent}s.
|
* {@link HttpContent}s.
|
||||||
*
|
*
|
||||||
* <h3>Parameters that prevents excessive memory consumption</h3>
|
* <h3>Parameters that prevents excessive memory consumption</h3>
|
||||||
|
@ -59,7 +59,7 @@ import io.netty.handler.codec.TooLongFrameException;
|
||||||
* request does not have any content even if there is <tt>Content-Length</tt>
|
* request does not have any content even if there is <tt>Content-Length</tt>
|
||||||
* header. Because {@link HttpResponseDecoder} is not able to determine if the
|
* header. Because {@link HttpResponseDecoder} is not able to determine if the
|
||||||
* response currently being decoded is associated with a <tt>HEAD</tt> request,
|
* response currently being decoded is associated with a <tt>HEAD</tt> request,
|
||||||
* you must override {@link #isContentAlwaysEmpty(HttpHeader)} to return
|
* you must override {@link #isContentAlwaysEmpty(HttpMessage)} to return
|
||||||
* <tt>true</tt> for the response of the <tt>HEAD</tt> request.
|
* <tt>true</tt> for the response of the <tt>HEAD</tt> request.
|
||||||
* </p><p>
|
* </p><p>
|
||||||
* If you are writing an HTTP client that issues a <tt>HEAD</tt> request,
|
* If you are writing an HTTP client that issues a <tt>HEAD</tt> request,
|
||||||
|
@ -102,15 +102,15 @@ public class HttpResponseDecoder extends HttpObjectDecoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpHeader createMessage(String[] initialLine) {
|
protected HttpMessage createMessage(String[] initialLine) {
|
||||||
return new DefaultHttpResponseHeader(
|
return new DefaultHttpResponse(
|
||||||
HttpVersion.valueOf(initialLine[0]),
|
HttpVersion.valueOf(initialLine[0]),
|
||||||
new HttpResponseStatus(Integer.valueOf(initialLine[1]), initialLine[2]));
|
new HttpResponseStatus(Integer.valueOf(initialLine[1]), initialLine[2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpHeader createInvalidMessage() {
|
protected HttpMessage createInvalidMessage() {
|
||||||
return new DefaultHttpResponseHeader(HttpVersion.HTTP_1_0, UNKNOWN_STATUS);
|
return new DefaultHttpResponse(HttpVersion.HTTP_1_0, UNKNOWN_STATUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -15,23 +15,29 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http.HttpConstants.*;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
|
import static io.netty.handler.codec.http.HttpConstants.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes an {@link HttpResponseHeader} or an {@link HttpContent} into
|
* Encodes an {@link HttpResponse} or an {@link HttpContent} into
|
||||||
* a {@link ByteBuf}.
|
* a {@link ByteBuf}.
|
||||||
*/
|
*/
|
||||||
public class HttpResponseEncoder extends HttpObjectEncoder<HttpResponseHeader> {
|
public class HttpResponseEncoder extends HttpObjectEncoder<HttpResponse> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void encodeInitialLine(ByteBuf buf, HttpResponseHeader response) throws Exception {
|
public boolean isEncodable(Object msg) throws Exception {
|
||||||
buf.writeBytes(response.getProtocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
|
return super.isEncodable(msg) && !(msg instanceof HttpRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void encodeInitialLine(ByteBuf buf, HttpResponse response) throws Exception {
|
||||||
|
buf.writeBytes(response.protocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
|
||||||
buf.writeByte(SP);
|
buf.writeByte(SP);
|
||||||
buf.writeBytes(String.valueOf(response.getStatus().getCode()).getBytes(CharsetUtil.US_ASCII));
|
buf.writeBytes(String.valueOf(response.status().code()).getBytes(CharsetUtil.US_ASCII));
|
||||||
buf.writeByte(SP);
|
buf.writeByte(SP);
|
||||||
buf.writeBytes(String.valueOf(response.getStatus().getReasonPhrase()).getBytes(CharsetUtil.US_ASCII));
|
buf.writeBytes(String.valueOf(response.status().reasonPhrase()).getBytes(CharsetUtil.US_ASCII));
|
||||||
buf.writeByte(CR);
|
buf.writeByte(CR);
|
||||||
buf.writeByte(LF);
|
buf.writeByte(LF);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
* Returns the code of this status.
|
||||||
*/
|
*/
|
||||||
public int getCode() {
|
public int code() {
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the reason phrase of this status.
|
* Returns the reason phrase of this status.
|
||||||
*/
|
*/
|
||||||
public String getReasonPhrase() {
|
public String reasonPhrase() {
|
||||||
return reasonPhrase;
|
return reasonPhrase;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return getCode();
|
return code();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -469,12 +469,12 @@ public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getCode() == ((HttpResponseStatus) o).getCode();
|
return code() == ((HttpResponseStatus) o).code();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(HttpResponseStatus o) {
|
public int compareTo(HttpResponseStatus o) {
|
||||||
return getCode() - o.getCode();
|
return code() - o.code();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import java.util.List;
|
import io.netty.buffer.ByteBuf;
|
||||||
import java.util.Map;
|
import io.netty.buffer.Unpooled;
|
||||||
import java.util.Set;
|
import io.netty.handler.codec.DecoderResult;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The last {@link HttpContent} which has trailing headers.
|
* The last {@link HttpContent} which has trailing headers.
|
||||||
|
@ -25,68 +25,48 @@ import java.util.Set;
|
||||||
public interface LastHttpContent extends HttpContent {
|
public interface LastHttpContent extends HttpContent {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the trailing header value with the specified header name.
|
* The 'end of content' marker in chunked encoding.
|
||||||
* If there are more than one trailing header value for the specified
|
|
||||||
* header name, the first value is returned.
|
|
||||||
*
|
|
||||||
* @return the header value or {@code null} if there is no such header
|
|
||||||
*/
|
*/
|
||||||
String getHeader(String name);
|
LastHttpContent EMPTY_LAST_CONTENT = new LastHttpContent() {
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Returns the trailing header values with the specified header name.
|
public ByteBuf data() {
|
||||||
*
|
return Unpooled.EMPTY_BUFFER;
|
||||||
* @return the {@link List} of header values. An empty list if there is no
|
}
|
||||||
* such header.
|
|
||||||
*/
|
|
||||||
List<String> getHeaders(String name);
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Returns the all header names and values that this trailer contains.
|
public LastHttpContent copy() {
|
||||||
*
|
return EMPTY_LAST_CONTENT;
|
||||||
* @return the {@link List} of the header name-value pairs. An empty list
|
}
|
||||||
* if there is no header in this trailer.
|
|
||||||
*/
|
|
||||||
List<Map.Entry<String, String>> getHeaders();
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Returns {@code true} if and only if there is a trailing header with
|
public HttpHeaders trailingHeaders() {
|
||||||
* the specified header name.
|
return HttpHeaders.EMPTY_HEADERS;
|
||||||
*/
|
}
|
||||||
boolean containsHeader(String name);
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Returns the {@link Set} of all trailing header names that this trailer
|
public DecoderResult decoderResult() {
|
||||||
* contains.
|
return DecoderResult.SUCCESS;
|
||||||
*/
|
}
|
||||||
Set<String> getHeaderNames();
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Adds a new trailing header with the specified name and value.
|
public void updateDecoderResult(DecoderResult result) {
|
||||||
*/
|
throw new UnsupportedOperationException("read only");
|
||||||
void addHeader(String name, Object value);
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Sets a new trailing header with the specified name and value.
|
public boolean isFreed() {
|
||||||
* If there is an existing trailing header with the same name, the existing
|
return false;
|
||||||
* one is removed.
|
}
|
||||||
*/
|
|
||||||
void setHeader(String name, Object value);
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Sets a new trailing header with the specified name and values.
|
public void free() {
|
||||||
* If there is an existing trailing header with the same name, the existing
|
// NOOP
|
||||||
* one is removed.
|
}
|
||||||
*/
|
};
|
||||||
void setHeader(String name, Iterable<?> values);
|
|
||||||
|
|
||||||
/**
|
HttpHeaders trailingHeaders();
|
||||||
* Removes the trailing header with the specified name.
|
|
||||||
*/
|
|
||||||
void removeHeader(String name);
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Removes all trailing headers from this trailer.
|
LastHttpContent copy();
|
||||||
*/
|
|
||||||
void clearHeaders();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ import java.util.Map;
|
||||||
* @see QueryStringEncoder
|
* @see QueryStringEncoder
|
||||||
*
|
*
|
||||||
* @apiviz.stereotype utility
|
* @apiviz.stereotype utility
|
||||||
* @apiviz.has io.netty.handler.codec.http.HttpRequest oneway - - decodes
|
* @apiviz.has io.netty.handler.codec.http.FullHttpRequest oneway - - decodes
|
||||||
*/
|
*/
|
||||||
public class QueryStringDecoder {
|
public class QueryStringDecoder {
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ import java.util.List;
|
||||||
* @see QueryStringDecoder
|
* @see QueryStringDecoder
|
||||||
*
|
*
|
||||||
* @apiviz.stereotype utility
|
* @apiviz.stereotype utility
|
||||||
* @apiviz.has io.netty.handler.codec.http.HttpRequest oneway - - encodes
|
* @apiviz.has io.netty.handler.codec.http.FullHttpRequest oneway - - encodes
|
||||||
*/
|
*/
|
||||||
public class QueryStringEncoder {
|
public class QueryStringEncoder {
|
||||||
|
|
||||||
|
|
|
@ -15,19 +15,19 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http.CookieEncoderUtil.*;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static io.netty.handler.codec.http.CookieEncoderUtil.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes server-side {@link Cookie}s into HTTP header values. This encoder can encode
|
* Encodes server-side {@link Cookie}s into HTTP header values. This encoder can encode
|
||||||
* the HTTP cookie version 0, 1, and 2.
|
* the HTTP cookie version 0, 1, and 2.
|
||||||
* <pre>
|
* <pre>
|
||||||
* // Example
|
* // Example
|
||||||
* {@link HttpRequestHeader} req = ...;
|
* {@link HttpRequest} req = ...;
|
||||||
* res.setHeader("Set-Cookie", {@link ServerCookieEncoder}.encode("JSESSIONID", "1234"));
|
* res.setHeader("Set-Cookie", {@link ServerCookieEncoder}.encode("JSESSIONID", "1234"));
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.multipart;
|
package io.netty.handler.codec.http.multipart;
|
||||||
|
|
||||||
import io.netty.handler.codec.http.HttpRequestHeader;
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
@ -46,8 +46,8 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
|
||||||
/**
|
/**
|
||||||
* Keep all HttpDatas until cleanAllHttpDatas() is called.
|
* Keep all HttpDatas until cleanAllHttpDatas() is called.
|
||||||
*/
|
*/
|
||||||
private final ConcurrentHashMap<HttpRequestHeader, List<HttpData>> requestFileDeleteMap =
|
private final ConcurrentHashMap<HttpRequest, List<HttpData>> requestFileDeleteMap =
|
||||||
new ConcurrentHashMap<HttpRequestHeader, List<HttpData>>();
|
new ConcurrentHashMap<HttpRequest, List<HttpData>>();
|
||||||
/**
|
/**
|
||||||
* HttpData will be in memory if less than default size (16KB).
|
* HttpData will be in memory if less than default size (16KB).
|
||||||
* The type will be Mixed.
|
* The type will be Mixed.
|
||||||
|
@ -79,7 +79,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
|
||||||
/**
|
/**
|
||||||
* @return the associated list of Files for the request
|
* @return the associated list of Files for the request
|
||||||
*/
|
*/
|
||||||
private List<HttpData> getList(HttpRequestHeader request) {
|
private List<HttpData> getList(HttpRequest request) {
|
||||||
List<HttpData> list = requestFileDeleteMap.get(request);
|
List<HttpData> list = requestFileDeleteMap.get(request);
|
||||||
if (list == null) {
|
if (list == null) {
|
||||||
list = new ArrayList<HttpData>();
|
list = new ArrayList<HttpData>();
|
||||||
|
@ -89,7 +89,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Attribute createAttribute(HttpRequestHeader request, String name) {
|
public Attribute createAttribute(HttpRequest request, String name) {
|
||||||
if (useDisk) {
|
if (useDisk) {
|
||||||
Attribute attribute = new DiskAttribute(name);
|
Attribute attribute = new DiskAttribute(name);
|
||||||
List<HttpData> fileToDelete = getList(request);
|
List<HttpData> fileToDelete = getList(request);
|
||||||
|
@ -106,7 +106,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Attribute createAttribute(HttpRequestHeader request, String name, String value) {
|
public Attribute createAttribute(HttpRequest request, String name, String value) {
|
||||||
if (useDisk) {
|
if (useDisk) {
|
||||||
Attribute attribute;
|
Attribute attribute;
|
||||||
try {
|
try {
|
||||||
|
@ -133,7 +133,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileUpload createFileUpload(HttpRequestHeader request, String name, String filename,
|
public FileUpload createFileUpload(HttpRequest request, String name, String filename,
|
||||||
String contentType, String contentTransferEncoding, Charset charset,
|
String contentType, String contentTransferEncoding, Charset charset,
|
||||||
long size) {
|
long size) {
|
||||||
if (useDisk) {
|
if (useDisk) {
|
||||||
|
@ -155,7 +155,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeHttpDataFromClean(HttpRequestHeader request, InterfaceHttpData data) {
|
public void removeHttpDataFromClean(HttpRequest request, InterfaceHttpData data) {
|
||||||
if (data instanceof HttpData) {
|
if (data instanceof HttpData) {
|
||||||
List<HttpData> fileToDelete = getList(request);
|
List<HttpData> fileToDelete = getList(request);
|
||||||
fileToDelete.remove(data);
|
fileToDelete.remove(data);
|
||||||
|
@ -163,7 +163,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cleanRequestHttpDatas(HttpRequestHeader request) {
|
public void cleanRequestHttpDatas(HttpRequest request) {
|
||||||
List<HttpData> fileToDelete = requestFileDeleteMap.remove(request);
|
List<HttpData> fileToDelete = requestFileDeleteMap.remove(request);
|
||||||
if (fileToDelete != null) {
|
if (fileToDelete != null) {
|
||||||
for (HttpData data: fileToDelete) {
|
for (HttpData data: fileToDelete) {
|
||||||
|
@ -175,7 +175,7 @@ public class DefaultHttpDataFactory implements HttpDataFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cleanAllHttpDatas() {
|
public void cleanAllHttpDatas() {
|
||||||
for (HttpRequestHeader request : requestFileDeleteMap.keySet()) {
|
for (HttpRequest request : requestFileDeleteMap.keySet()) {
|
||||||
List<HttpData> fileToDelete = requestFileDeleteMap.get(request);
|
List<HttpData> fileToDelete = requestFileDeleteMap.get(request);
|
||||||
if (fileToDelete != null) {
|
if (fileToDelete != null) {
|
||||||
for (HttpData data: fileToDelete) {
|
for (HttpData data: fileToDelete) {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.multipart;
|
package io.netty.handler.codec.http.multipart;
|
||||||
|
|
||||||
import io.netty.handler.codec.http.HttpRequestHeader;
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
@ -28,20 +28,20 @@ public interface HttpDataFactory {
|
||||||
* @param request associated request
|
* @param request associated request
|
||||||
* @return a new Attribute with no value
|
* @return a new Attribute with no value
|
||||||
*/
|
*/
|
||||||
Attribute createAttribute(HttpRequestHeader request, String name);
|
Attribute createAttribute(HttpRequest request, String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param request associated request
|
* @param request associated request
|
||||||
* @return a new Attribute
|
* @return a new Attribute
|
||||||
*/
|
*/
|
||||||
Attribute createAttribute(HttpRequestHeader request, String name, String value);
|
Attribute createAttribute(HttpRequest request, String name, String value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param request associated request
|
* @param request associated request
|
||||||
* @param size the size of the Uploaded file
|
* @param size the size of the Uploaded file
|
||||||
* @return a new FileUpload
|
* @return a new FileUpload
|
||||||
*/
|
*/
|
||||||
FileUpload createFileUpload(HttpRequestHeader request, String name, String filename,
|
FileUpload createFileUpload(HttpRequest request, String name, String filename,
|
||||||
String contentType, String contentTransferEncoding, Charset charset,
|
String contentType, String contentTransferEncoding, Charset charset,
|
||||||
long size);
|
long size);
|
||||||
|
|
||||||
|
@ -50,14 +50,14 @@ public interface HttpDataFactory {
|
||||||
* is still a temporary one as setup at construction)
|
* is still a temporary one as setup at construction)
|
||||||
* @param request associated request
|
* @param request associated request
|
||||||
*/
|
*/
|
||||||
void removeHttpDataFromClean(HttpRequestHeader request, InterfaceHttpData data);
|
void removeHttpDataFromClean(HttpRequest request, InterfaceHttpData data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all InterfaceHttpData from virtual File storage from clean list for the request
|
* Remove all InterfaceHttpData from virtual File storage from clean list for the request
|
||||||
*
|
*
|
||||||
* @param request associated request
|
* @param request associated request
|
||||||
*/
|
*/
|
||||||
void cleanRequestHttpDatas(HttpRequestHeader request);
|
void cleanRequestHttpDatas(HttpRequest request);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all InterfaceHttpData from virtual File storage from clean list for all requests
|
* Remove all InterfaceHttpData from virtual File storage from clean list for all requests
|
||||||
|
|
|
@ -16,11 +16,11 @@
|
||||||
package io.netty.handler.codec.http.multipart;
|
package io.netty.handler.codec.http.multipart;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.handler.codec.http.HttpContent;
|
|
||||||
import io.netty.handler.codec.http.HttpConstants;
|
import io.netty.handler.codec.http.HttpConstants;
|
||||||
|
import io.netty.handler.codec.http.HttpContent;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
import io.netty.handler.codec.http.HttpRequestHeader;
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
import io.netty.handler.codec.http.LastHttpContent;
|
import io.netty.handler.codec.http.LastHttpContent;
|
||||||
import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadNoBackArrayException;
|
import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadNoBackArrayException;
|
||||||
import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadOptimize;
|
import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadOptimize;
|
||||||
|
@ -50,7 +50,7 @@ public class HttpPostRequestDecoder {
|
||||||
/**
|
/**
|
||||||
* Request to decode
|
* Request to decode
|
||||||
*/
|
*/
|
||||||
private final HttpRequestHeader request;
|
private final HttpRequest request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default charset to use
|
* Default charset to use
|
||||||
|
@ -136,7 +136,7 @@ public class HttpPostRequestDecoder {
|
||||||
* if the default charset was wrong when decoding or other
|
* if the default charset was wrong when decoding or other
|
||||||
* errors
|
* errors
|
||||||
*/
|
*/
|
||||||
public HttpPostRequestDecoder(HttpRequestHeader request) throws ErrorDataDecoderException,
|
public HttpPostRequestDecoder(HttpRequest request) throws ErrorDataDecoderException,
|
||||||
IncompatibleDataDecoderException {
|
IncompatibleDataDecoderException {
|
||||||
this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, HttpConstants.DEFAULT_CHARSET);
|
this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, HttpConstants.DEFAULT_CHARSET);
|
||||||
}
|
}
|
||||||
|
@ -155,7 +155,7 @@ public class HttpPostRequestDecoder {
|
||||||
* if the default charset was wrong when decoding or other
|
* if the default charset was wrong when decoding or other
|
||||||
* errors
|
* errors
|
||||||
*/
|
*/
|
||||||
public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequestHeader request) throws ErrorDataDecoderException,
|
public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request) throws ErrorDataDecoderException,
|
||||||
IncompatibleDataDecoderException {
|
IncompatibleDataDecoderException {
|
||||||
this(factory, request, HttpConstants.DEFAULT_CHARSET);
|
this(factory, request, HttpConstants.DEFAULT_CHARSET);
|
||||||
}
|
}
|
||||||
|
@ -176,7 +176,7 @@ public class HttpPostRequestDecoder {
|
||||||
* if the default charset was wrong when decoding or other
|
* if the default charset was wrong when decoding or other
|
||||||
* errors
|
* errors
|
||||||
*/
|
*/
|
||||||
public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequestHeader request, Charset charset)
|
public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request, Charset charset)
|
||||||
throws ErrorDataDecoderException, IncompatibleDataDecoderException {
|
throws ErrorDataDecoderException, IncompatibleDataDecoderException {
|
||||||
if (factory == null) {
|
if (factory == null) {
|
||||||
throw new NullPointerException("factory");
|
throw new NullPointerException("factory");
|
||||||
|
@ -188,15 +188,15 @@ public class HttpPostRequestDecoder {
|
||||||
throw new NullPointerException("charset");
|
throw new NullPointerException("charset");
|
||||||
}
|
}
|
||||||
this.request = request;
|
this.request = request;
|
||||||
HttpMethod method = request.getMethod();
|
HttpMethod method = request.method();
|
||||||
if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT) || method.equals(HttpMethod.PATCH)) {
|
if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT) || method.equals(HttpMethod.PATCH)) {
|
||||||
bodyToDecode = true;
|
bodyToDecode = true;
|
||||||
}
|
}
|
||||||
this.charset = charset;
|
this.charset = charset;
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
// Fill default values
|
// Fill default values
|
||||||
if (this.request.containsHeader(HttpHeaders.Names.CONTENT_TYPE)) {
|
if (this.request.headers().contains(HttpHeaders.Names.CONTENT_TYPE)) {
|
||||||
checkMultipart(this.request.getHeader(HttpHeaders.Names.CONTENT_TYPE));
|
checkMultipart(this.request.headers().get(HttpHeaders.Names.CONTENT_TYPE));
|
||||||
} else {
|
} else {
|
||||||
isMultipart = false;
|
isMultipart = false;
|
||||||
}
|
}
|
||||||
|
@ -341,7 +341,7 @@ public class HttpPostRequestDecoder {
|
||||||
* errors
|
* errors
|
||||||
*/
|
*/
|
||||||
public void offer(HttpContent content) throws ErrorDataDecoderException {
|
public void offer(HttpContent content) throws ErrorDataDecoderException {
|
||||||
ByteBuf chunked = content.getContent();
|
ByteBuf chunked = content.data();
|
||||||
if (undecodedChunk == null) {
|
if (undecodedChunk == null) {
|
||||||
undecodedChunk = chunked;
|
undecodedChunk = chunked;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -18,11 +18,11 @@ package io.netty.handler.codec.http.multipart;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.MessageBuf;
|
import io.netty.buffer.MessageBuf;
|
||||||
import io.netty.handler.codec.http.DefaultHttpContent;
|
import io.netty.handler.codec.http.DefaultHttpContent;
|
||||||
import io.netty.handler.codec.http.HttpContent;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpConstants;
|
import io.netty.handler.codec.http.HttpConstants;
|
||||||
|
import io.netty.handler.codec.http.HttpContent;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
|
||||||
import io.netty.handler.stream.ChunkedMessageInput;
|
import io.netty.handler.stream.ChunkedMessageInput;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -49,7 +49,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
||||||
/**
|
/**
|
||||||
* Request to encode
|
* Request to encode
|
||||||
*/
|
*/
|
||||||
private final HttpRequest request;
|
private final FullHttpRequest request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default charset to use
|
* Default charset to use
|
||||||
|
@ -100,7 +100,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
||||||
* @throws ErrorDataEncoderException
|
* @throws ErrorDataEncoderException
|
||||||
* if the request is not a POST
|
* if the request is not a POST
|
||||||
*/
|
*/
|
||||||
public HttpPostRequestEncoder(HttpRequest request, boolean multipart) throws ErrorDataEncoderException {
|
public HttpPostRequestEncoder(FullHttpRequest request, boolean multipart) throws ErrorDataEncoderException {
|
||||||
this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, multipart,
|
this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, multipart,
|
||||||
HttpConstants.DEFAULT_CHARSET);
|
HttpConstants.DEFAULT_CHARSET);
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
||||||
* @throws ErrorDataEncoderException
|
* @throws ErrorDataEncoderException
|
||||||
* if the request is not a POST
|
* if the request is not a POST
|
||||||
*/
|
*/
|
||||||
public HttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request, boolean multipart)
|
public HttpPostRequestEncoder(HttpDataFactory factory, FullHttpRequest request, boolean multipart)
|
||||||
throws ErrorDataEncoderException {
|
throws ErrorDataEncoderException {
|
||||||
this(factory, request, multipart, HttpConstants.DEFAULT_CHARSET);
|
this(factory, request, multipart, HttpConstants.DEFAULT_CHARSET);
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,8 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
||||||
* @throws ErrorDataEncoderException
|
* @throws ErrorDataEncoderException
|
||||||
* if the request is not a POST
|
* if the request is not a POST
|
||||||
*/
|
*/
|
||||||
public HttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request, boolean multipart, Charset charset)
|
public HttpPostRequestEncoder(
|
||||||
|
HttpDataFactory factory, FullHttpRequest request, boolean multipart, Charset charset)
|
||||||
throws ErrorDataEncoderException {
|
throws ErrorDataEncoderException {
|
||||||
if (factory == null) {
|
if (factory == null) {
|
||||||
throw new NullPointerException("factory");
|
throw new NullPointerException("factory");
|
||||||
|
@ -149,7 +150,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
||||||
if (charset == null) {
|
if (charset == null) {
|
||||||
throw new NullPointerException("charset");
|
throw new NullPointerException("charset");
|
||||||
}
|
}
|
||||||
if (request.getMethod() != HttpMethod.POST) {
|
if (request.method() != HttpMethod.POST) {
|
||||||
throw new ErrorDataEncoderException("Cannot create a Encoder if not a POST");
|
throw new ErrorDataEncoderException("Cannot create a Encoder if not a POST");
|
||||||
}
|
}
|
||||||
this.request = request;
|
this.request = request;
|
||||||
|
@ -598,7 +599,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
||||||
* @throws ErrorDataEncoderException
|
* @throws ErrorDataEncoderException
|
||||||
* if the encoding is in error or if the finalize were already done
|
* if the encoding is in error or if the finalize were already done
|
||||||
*/
|
*/
|
||||||
public HttpRequest finalizeRequest() throws ErrorDataEncoderException {
|
public FullHttpRequest finalizeRequest() throws ErrorDataEncoderException {
|
||||||
// Finalize the multipartHttpDatas
|
// Finalize the multipartHttpDatas
|
||||||
if (!headerFinalized) {
|
if (!headerFinalized) {
|
||||||
if (isMultipart) {
|
if (isMultipart) {
|
||||||
|
@ -617,10 +618,10 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
||||||
} else {
|
} else {
|
||||||
throw new ErrorDataEncoderException("Header already encoded");
|
throw new ErrorDataEncoderException("Header already encoded");
|
||||||
}
|
}
|
||||||
List<String> contentTypes = request.getHeaders(HttpHeaders.Names.CONTENT_TYPE);
|
List<String> contentTypes = request.headers().getAll(HttpHeaders.Names.CONTENT_TYPE);
|
||||||
List<String> transferEncoding = request.getHeaders(HttpHeaders.Names.TRANSFER_ENCODING);
|
List<String> transferEncoding = request.headers().getAll(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||||
if (contentTypes != null) {
|
if (contentTypes != null) {
|
||||||
request.removeHeader(HttpHeaders.Names.CONTENT_TYPE);
|
request.headers().remove(HttpHeaders.Names.CONTENT_TYPE);
|
||||||
for (String contentType : contentTypes) {
|
for (String contentType : contentTypes) {
|
||||||
// "multipart/form-data; boundary=--89421926422648"
|
// "multipart/form-data; boundary=--89421926422648"
|
||||||
if (contentType.toLowerCase().startsWith(HttpHeaders.Values.MULTIPART_FORM_DATA)) {
|
if (contentType.toLowerCase().startsWith(HttpHeaders.Values.MULTIPART_FORM_DATA)) {
|
||||||
|
@ -628,17 +629,17 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
||||||
} else if (contentType.toLowerCase().startsWith(HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED)) {
|
} else if (contentType.toLowerCase().startsWith(HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED)) {
|
||||||
// ignore
|
// ignore
|
||||||
} else {
|
} else {
|
||||||
request.addHeader(HttpHeaders.Names.CONTENT_TYPE, contentType);
|
request.headers().add(HttpHeaders.Names.CONTENT_TYPE, contentType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isMultipart) {
|
if (isMultipart) {
|
||||||
String value = HttpHeaders.Values.MULTIPART_FORM_DATA + "; " + HttpHeaders.Values.BOUNDARY + '='
|
String value = HttpHeaders.Values.MULTIPART_FORM_DATA + "; " + HttpHeaders.Values.BOUNDARY + '='
|
||||||
+ multipartDataBoundary;
|
+ multipartDataBoundary;
|
||||||
request.addHeader(HttpHeaders.Names.CONTENT_TYPE, value);
|
request.headers().add(HttpHeaders.Names.CONTENT_TYPE, value);
|
||||||
} else {
|
} else {
|
||||||
// Not multipart
|
// Not multipart
|
||||||
request.addHeader(HttpHeaders.Names.CONTENT_TYPE, HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED);
|
request.headers().add(HttpHeaders.Names.CONTENT_TYPE, HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED);
|
||||||
}
|
}
|
||||||
// Now consider size for chunk or not
|
// Now consider size for chunk or not
|
||||||
long realSize = globalBodySize;
|
long realSize = globalBodySize;
|
||||||
|
@ -648,25 +649,25 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
||||||
realSize -= 1; // last '&' removed
|
realSize -= 1; // last '&' removed
|
||||||
iterator = multipartHttpDatas.listIterator();
|
iterator = multipartHttpDatas.listIterator();
|
||||||
}
|
}
|
||||||
request.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(realSize));
|
request.headers().set(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(realSize));
|
||||||
if (realSize > HttpPostBodyUtil.chunkSize || isMultipart) {
|
if (realSize > HttpPostBodyUtil.chunkSize || isMultipart) {
|
||||||
isChunked = true;
|
isChunked = true;
|
||||||
if (transferEncoding != null) {
|
if (transferEncoding != null) {
|
||||||
request.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
|
request.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||||
for (String v : transferEncoding) {
|
for (String v : transferEncoding) {
|
||||||
if (v.equalsIgnoreCase(HttpHeaders.Values.CHUNKED)) {
|
if (v.equalsIgnoreCase(HttpHeaders.Values.CHUNKED)) {
|
||||||
// ignore
|
// ignore
|
||||||
} else {
|
} else {
|
||||||
request.addHeader(HttpHeaders.Names.TRANSFER_ENCODING, v);
|
request.headers().add(HttpHeaders.Names.TRANSFER_ENCODING, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HttpHeaders.setTransferEncodingChunked(request);
|
HttpHeaders.setTransferEncodingChunked(request);
|
||||||
request.setContent(EMPTY_BUFFER);
|
request.data().clear();
|
||||||
} else {
|
} else {
|
||||||
// get the only one body and set it to the request
|
// get the only one body and set it to the request
|
||||||
HttpContent chunk = nextChunk();
|
HttpContent chunk = nextChunk();
|
||||||
request.setContent(chunk.getContent());
|
request.data().clear().writeBytes(chunk.data());
|
||||||
}
|
}
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ package io.netty.handler.codec.http.websocketx;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.channel.ChannelPromise;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -148,5 +148,5 @@ public abstract class WebSocketClientHandshaker {
|
||||||
* @param response
|
* @param response
|
||||||
* HTTP response containing the closing handshake details
|
* HTTP response containing the closing handshake details
|
||||||
*/
|
*/
|
||||||
public abstract void finishHandshake(Channel channel, HttpResponse response);
|
public abstract void finishHandshake(Channel channel, FullHttpResponse response);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,19 +15,18 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.websocketx;
|
package io.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.channel.ChannelPromise;
|
||||||
import io.netty.handler.codec.http.DefaultHttpRequest;
|
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
import io.netty.handler.codec.http.HttpHeaders.Values;
|
import io.netty.handler.codec.http.HttpHeaders.Values;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
|
||||||
import io.netty.handler.codec.http.HttpRequestEncoder;
|
import io.netty.handler.codec.http.HttpRequestEncoder;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
|
||||||
import io.netty.handler.codec.http.HttpResponseDecoder;
|
import io.netty.handler.codec.http.HttpResponseDecoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http.HttpVersion;
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
|
@ -142,10 +141,10 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format request
|
// Format request
|
||||||
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
|
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
|
||||||
request.addHeader(Names.UPGRADE, Values.WEBSOCKET);
|
request.headers().add(Names.UPGRADE, Values.WEBSOCKET);
|
||||||
request.addHeader(Names.CONNECTION, Values.UPGRADE);
|
request.headers().add(Names.CONNECTION, Values.UPGRADE);
|
||||||
request.addHeader(Names.HOST, wsURL.getHost());
|
request.headers().add(Names.HOST, wsURL.getHost());
|
||||||
|
|
||||||
int wsPort = wsURL.getPort();
|
int wsPort = wsURL.getPort();
|
||||||
String originValue = "http://" + wsURL.getHost();
|
String originValue = "http://" + wsURL.getHost();
|
||||||
|
@ -155,24 +154,24 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
||||||
originValue = originValue + ':' + wsPort;
|
originValue = originValue + ':' + wsPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
request.addHeader(Names.ORIGIN, originValue);
|
request.headers().add(Names.ORIGIN, originValue);
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_KEY1, key1);
|
request.headers().add(Names.SEC_WEBSOCKET_KEY1, key1);
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_KEY2, key2);
|
request.headers().add(Names.SEC_WEBSOCKET_KEY2, key2);
|
||||||
String expectedSubprotocol = getExpectedSubprotocol();
|
String expectedSubprotocol = getExpectedSubprotocol();
|
||||||
if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) {
|
if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) {
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
|
request.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (customHeaders != null) {
|
if (customHeaders != null) {
|
||||||
for (Map.Entry<String, String> e : customHeaders.entrySet()) {
|
for (Map.Entry<String, String> e : customHeaders.entrySet()) {
|
||||||
request.addHeader(e.getKey(), e.getValue());
|
request.headers().add(e.getKey(), e.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set Content-Length to workaround some known defect.
|
// Set Content-Length to workaround some known defect.
|
||||||
// See also: http://www.ietf.org/mail-archive/web/hybi/current/msg02149.html
|
// See also: http://www.ietf.org/mail-archive/web/hybi/current/msg02149.html
|
||||||
request.setHeader(Names.CONTENT_LENGTH, key3.length);
|
request.headers().set(Names.CONTENT_LENGTH, key3.length);
|
||||||
request.setContent(Unpooled.copiedBuffer(key3));
|
request.data().writeBytes(key3);
|
||||||
|
|
||||||
ChannelFuture future = channel.write(request);
|
ChannelFuture future = channel.write(request);
|
||||||
future.addListener(new ChannelFutureListener() {
|
future.addListener(new ChannelFutureListener() {
|
||||||
|
@ -217,31 +216,31 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
||||||
* @throws WebSocketHandshakeException
|
* @throws WebSocketHandshakeException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void finishHandshake(Channel channel, HttpResponse response) {
|
public void finishHandshake(Channel channel, FullHttpResponse response) {
|
||||||
final HttpResponseStatus status = new HttpResponseStatus(101, "WebSocket Protocol Handshake");
|
final HttpResponseStatus status = new HttpResponseStatus(101, "WebSocket Protocol Handshake");
|
||||||
|
|
||||||
if (!response.getStatus().equals(status)) {
|
if (!response.status().equals(status)) {
|
||||||
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus());
|
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.status());
|
||||||
}
|
}
|
||||||
|
|
||||||
String upgrade = response.getHeader(Names.UPGRADE);
|
String upgrade = response.headers().get(Names.UPGRADE);
|
||||||
if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
|
if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
|
||||||
throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
|
throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
|
||||||
+ upgrade);
|
+ upgrade);
|
||||||
}
|
}
|
||||||
|
|
||||||
String connection = response.getHeader(Names.CONNECTION);
|
String connection = response.headers().get(Names.CONNECTION);
|
||||||
if (!Values.UPGRADE.equalsIgnoreCase(connection)) {
|
if (!Values.UPGRADE.equalsIgnoreCase(connection)) {
|
||||||
throw new WebSocketHandshakeException("Invalid handshake response connection: "
|
throw new WebSocketHandshakeException("Invalid handshake response connection: "
|
||||||
+ connection);
|
+ connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] challenge = response.getContent().array();
|
byte[] challenge = response.data().array();
|
||||||
if (!Arrays.equals(challenge, expectedChallengeResponseBytes)) {
|
if (!Arrays.equals(challenge, expectedChallengeResponseBytes)) {
|
||||||
throw new WebSocketHandshakeException("Invalid challenge");
|
throw new WebSocketHandshakeException("Invalid challenge");
|
||||||
}
|
}
|
||||||
|
|
||||||
String subprotocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
String subprotocol = response.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||||
setActualSubprotocol(subprotocol);
|
setActualSubprotocol(subprotocol);
|
||||||
|
|
||||||
setHandshakeComplete();
|
setHandshakeComplete();
|
||||||
|
|
|
@ -20,13 +20,13 @@ import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.channel.ChannelPromise;
|
||||||
import io.netty.handler.codec.http.DefaultHttpRequest;
|
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
import io.netty.handler.codec.http.HttpHeaders.Values;
|
import io.netty.handler.codec.http.HttpHeaders.Values;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
|
||||||
import io.netty.handler.codec.http.HttpRequestEncoder;
|
import io.netty.handler.codec.http.HttpRequestEncoder;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
|
||||||
import io.netty.handler.codec.http.HttpResponseDecoder;
|
import io.netty.handler.codec.http.HttpResponseDecoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http.HttpVersion;
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
|
@ -124,11 +124,11 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format request
|
// Format request
|
||||||
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
|
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
|
||||||
request.addHeader(Names.UPGRADE, Values.WEBSOCKET.toLowerCase());
|
request.headers().add(Names.UPGRADE, Values.WEBSOCKET.toLowerCase());
|
||||||
request.addHeader(Names.CONNECTION, Values.UPGRADE);
|
request.headers().add(Names.CONNECTION, Values.UPGRADE);
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_KEY, key);
|
request.headers().add(Names.SEC_WEBSOCKET_KEY, key);
|
||||||
request.addHeader(Names.HOST, wsURL.getHost());
|
request.headers().add(Names.HOST, wsURL.getHost());
|
||||||
|
|
||||||
int wsPort = wsURL.getPort();
|
int wsPort = wsURL.getPort();
|
||||||
String originValue = "http://" + wsURL.getHost();
|
String originValue = "http://" + wsURL.getHost();
|
||||||
|
@ -137,18 +137,18 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
|
||||||
// See http://tools.ietf.org/html/rfc6454#section-6.2
|
// See http://tools.ietf.org/html/rfc6454#section-6.2
|
||||||
originValue = originValue + ':' + wsPort;
|
originValue = originValue + ':' + wsPort;
|
||||||
}
|
}
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_ORIGIN, originValue);
|
request.headers().add(Names.SEC_WEBSOCKET_ORIGIN, originValue);
|
||||||
|
|
||||||
String expectedSubprotocol = getExpectedSubprotocol();
|
String expectedSubprotocol = getExpectedSubprotocol();
|
||||||
if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) {
|
if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) {
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
|
request.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_VERSION, "7");
|
request.headers().add(Names.SEC_WEBSOCKET_VERSION, "7");
|
||||||
|
|
||||||
if (customHeaders != null) {
|
if (customHeaders != null) {
|
||||||
for (Map.Entry<String, String> e : customHeaders.entrySet()) {
|
for (Map.Entry<String, String> e : customHeaders.entrySet()) {
|
||||||
request.addHeader(e.getKey(), e.getValue());
|
request.headers().add(e.getKey(), e.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,32 +192,32 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
|
||||||
* @throws WebSocketHandshakeException
|
* @throws WebSocketHandshakeException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void finishHandshake(Channel channel, HttpResponse response) {
|
public void finishHandshake(Channel channel, FullHttpResponse response) {
|
||||||
final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
|
final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
|
||||||
|
|
||||||
if (!response.getStatus().equals(status)) {
|
if (!response.status().equals(status)) {
|
||||||
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus());
|
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.status());
|
||||||
}
|
}
|
||||||
|
|
||||||
String upgrade = response.getHeader(Names.UPGRADE);
|
String upgrade = response.headers().get(Names.UPGRADE);
|
||||||
if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
|
if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
|
||||||
throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
|
throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
|
||||||
+ response.getHeader(Names.UPGRADE));
|
+ response.headers().get(Names.UPGRADE));
|
||||||
}
|
}
|
||||||
|
|
||||||
String connection = response.getHeader(Names.CONNECTION);
|
String connection = response.headers().get(Names.CONNECTION);
|
||||||
if (!Values.UPGRADE.equalsIgnoreCase(connection)) {
|
if (!Values.UPGRADE.equalsIgnoreCase(connection)) {
|
||||||
throw new WebSocketHandshakeException("Invalid handshake response connection: "
|
throw new WebSocketHandshakeException("Invalid handshake response connection: "
|
||||||
+ response.getHeader(Names.CONNECTION));
|
+ response.headers().get(Names.CONNECTION));
|
||||||
}
|
}
|
||||||
|
|
||||||
String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT);
|
String accept = response.headers().get(Names.SEC_WEBSOCKET_ACCEPT);
|
||||||
if (accept == null || !accept.equals(expectedChallengeResponseString)) {
|
if (accept == null || !accept.equals(expectedChallengeResponseString)) {
|
||||||
throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept,
|
throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept,
|
||||||
expectedChallengeResponseString));
|
expectedChallengeResponseString));
|
||||||
}
|
}
|
||||||
|
|
||||||
String subprotocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
String subprotocol = response.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||||
setActualSubprotocol(subprotocol);
|
setActualSubprotocol(subprotocol);
|
||||||
|
|
||||||
setHandshakeComplete();
|
setHandshakeComplete();
|
||||||
|
|
|
@ -20,13 +20,13 @@ import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.channel.ChannelPromise;
|
||||||
import io.netty.handler.codec.http.DefaultHttpRequest;
|
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
import io.netty.handler.codec.http.HttpHeaders.Values;
|
import io.netty.handler.codec.http.HttpHeaders.Values;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
|
||||||
import io.netty.handler.codec.http.HttpRequestEncoder;
|
import io.netty.handler.codec.http.HttpRequestEncoder;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
|
||||||
import io.netty.handler.codec.http.HttpResponseDecoder;
|
import io.netty.handler.codec.http.HttpResponseDecoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http.HttpVersion;
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
|
@ -124,11 +124,11 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format request
|
// Format request
|
||||||
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
|
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
|
||||||
request.addHeader(Names.UPGRADE, Values.WEBSOCKET.toLowerCase());
|
request.headers().add(Names.UPGRADE, Values.WEBSOCKET.toLowerCase());
|
||||||
request.addHeader(Names.CONNECTION, Values.UPGRADE);
|
request.headers().add(Names.CONNECTION, Values.UPGRADE);
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_KEY, key);
|
request.headers().add(Names.SEC_WEBSOCKET_KEY, key);
|
||||||
request.addHeader(Names.HOST, wsURL.getHost());
|
request.headers().add(Names.HOST, wsURL.getHost());
|
||||||
|
|
||||||
int wsPort = wsURL.getPort();
|
int wsPort = wsURL.getPort();
|
||||||
String originValue = "http://" + wsURL.getHost();
|
String originValue = "http://" + wsURL.getHost();
|
||||||
|
@ -137,18 +137,18 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
|
||||||
// See http://tools.ietf.org/html/rfc6454#section-6.2
|
// See http://tools.ietf.org/html/rfc6454#section-6.2
|
||||||
originValue = originValue + ':' + wsPort;
|
originValue = originValue + ':' + wsPort;
|
||||||
}
|
}
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_ORIGIN, originValue);
|
request.headers().add(Names.SEC_WEBSOCKET_ORIGIN, originValue);
|
||||||
|
|
||||||
String expectedSubprotocol = getExpectedSubprotocol();
|
String expectedSubprotocol = getExpectedSubprotocol();
|
||||||
if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) {
|
if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) {
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
|
request.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_VERSION, "8");
|
request.headers().add(Names.SEC_WEBSOCKET_VERSION, "8");
|
||||||
|
|
||||||
if (customHeaders != null) {
|
if (customHeaders != null) {
|
||||||
for (Map.Entry<String, String> e : customHeaders.entrySet()) {
|
for (Map.Entry<String, String> e : customHeaders.entrySet()) {
|
||||||
request.addHeader(e.getKey(), e.getValue());
|
request.headers().add(e.getKey(), e.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,32 +192,32 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
|
||||||
* @throws WebSocketHandshakeException
|
* @throws WebSocketHandshakeException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void finishHandshake(Channel channel, HttpResponse response) {
|
public void finishHandshake(Channel channel, FullHttpResponse response) {
|
||||||
final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
|
final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
|
||||||
|
|
||||||
if (!response.getStatus().equals(status)) {
|
if (!response.status().equals(status)) {
|
||||||
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus());
|
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.status());
|
||||||
}
|
}
|
||||||
|
|
||||||
String upgrade = response.getHeader(Names.UPGRADE);
|
String upgrade = response.headers().get(Names.UPGRADE);
|
||||||
if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
|
if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
|
||||||
throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
|
throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
|
||||||
+ response.getHeader(Names.UPGRADE));
|
+ response.headers().get(Names.UPGRADE));
|
||||||
}
|
}
|
||||||
|
|
||||||
String connection = response.getHeader(Names.CONNECTION);
|
String connection = response.headers().get(Names.CONNECTION);
|
||||||
if (!Values.UPGRADE.equalsIgnoreCase(connection)) {
|
if (!Values.UPGRADE.equalsIgnoreCase(connection)) {
|
||||||
throw new WebSocketHandshakeException("Invalid handshake response connection: "
|
throw new WebSocketHandshakeException("Invalid handshake response connection: "
|
||||||
+ response.getHeader(Names.CONNECTION));
|
+ response.headers().get(Names.CONNECTION));
|
||||||
}
|
}
|
||||||
|
|
||||||
String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT);
|
String accept = response.headers().get(Names.SEC_WEBSOCKET_ACCEPT);
|
||||||
if (accept == null || !accept.equals(expectedChallengeResponseString)) {
|
if (accept == null || !accept.equals(expectedChallengeResponseString)) {
|
||||||
throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept,
|
throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept,
|
||||||
expectedChallengeResponseString));
|
expectedChallengeResponseString));
|
||||||
}
|
}
|
||||||
|
|
||||||
String subprotocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
String subprotocol = response.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||||
setActualSubprotocol(subprotocol);
|
setActualSubprotocol(subprotocol);
|
||||||
|
|
||||||
setHandshakeComplete();
|
setHandshakeComplete();
|
||||||
|
|
|
@ -20,13 +20,13 @@ import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.channel.ChannelPromise;
|
||||||
import io.netty.handler.codec.http.DefaultHttpRequestHeader;
|
import io.netty.handler.codec.http.DefaultHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
import io.netty.handler.codec.http.HttpHeaders.Values;
|
import io.netty.handler.codec.http.HttpHeaders.Values;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
import io.netty.handler.codec.http.HttpRequestHeader;
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpRequestEncoder;
|
import io.netty.handler.codec.http.HttpRequestEncoder;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
|
||||||
import io.netty.handler.codec.http.HttpResponseDecoder;
|
import io.netty.handler.codec.http.HttpResponseDecoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http.HttpVersion;
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
|
@ -124,11 +124,11 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format request
|
// Format request
|
||||||
HttpRequestHeader request = new DefaultHttpRequestHeader(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
|
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
|
||||||
request.addHeader(Names.UPGRADE, Values.WEBSOCKET.toLowerCase());
|
request.headers().add(Names.UPGRADE, Values.WEBSOCKET.toLowerCase());
|
||||||
request.addHeader(Names.CONNECTION, Values.UPGRADE);
|
request.headers().add(Names.CONNECTION, Values.UPGRADE);
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_KEY, key);
|
request.headers().add(Names.SEC_WEBSOCKET_KEY, key);
|
||||||
request.addHeader(Names.HOST, wsURL.getHost());
|
request.headers().add(Names.HOST, wsURL.getHost());
|
||||||
|
|
||||||
int wsPort = wsURL.getPort();
|
int wsPort = wsURL.getPort();
|
||||||
String originValue = "http://" + wsURL.getHost();
|
String originValue = "http://" + wsURL.getHost();
|
||||||
|
@ -137,18 +137,18 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
|
||||||
// See http://tools.ietf.org/html/rfc6454#section-6.2
|
// See http://tools.ietf.org/html/rfc6454#section-6.2
|
||||||
originValue = originValue + ':' + wsPort;
|
originValue = originValue + ':' + wsPort;
|
||||||
}
|
}
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_ORIGIN, originValue);
|
request.headers().add(Names.SEC_WEBSOCKET_ORIGIN, originValue);
|
||||||
|
|
||||||
String expectedSubprotocol = getExpectedSubprotocol();
|
String expectedSubprotocol = getExpectedSubprotocol();
|
||||||
if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) {
|
if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) {
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
|
request.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_VERSION, "13");
|
request.headers().add(Names.SEC_WEBSOCKET_VERSION, "13");
|
||||||
|
|
||||||
if (customHeaders != null) {
|
if (customHeaders != null) {
|
||||||
for (Map.Entry<String, String> e: customHeaders.entrySet()) {
|
for (Map.Entry<String, String> e: customHeaders.entrySet()) {
|
||||||
request.addHeader(e.getKey(), e.getValue());
|
request.headers().add(e.getKey(), e.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,32 +191,32 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
|
||||||
* @throws WebSocketHandshakeException
|
* @throws WebSocketHandshakeException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void finishHandshake(Channel channel, HttpResponse response) {
|
public void finishHandshake(Channel channel, FullHttpResponse response) {
|
||||||
final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
|
final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
|
||||||
|
|
||||||
if (!response.getStatus().equals(status)) {
|
if (!response.status().equals(status)) {
|
||||||
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus());
|
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.status());
|
||||||
}
|
}
|
||||||
|
|
||||||
String upgrade = response.getHeader(Names.UPGRADE);
|
String upgrade = response.headers().get(Names.UPGRADE);
|
||||||
if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
|
if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
|
||||||
throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
|
throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
|
||||||
+ response.getHeader(Names.UPGRADE));
|
+ response.headers().get(Names.UPGRADE));
|
||||||
}
|
}
|
||||||
|
|
||||||
String connection = response.getHeader(Names.CONNECTION);
|
String connection = response.headers().get(Names.CONNECTION);
|
||||||
if (!Values.UPGRADE.equalsIgnoreCase(connection)) {
|
if (!Values.UPGRADE.equalsIgnoreCase(connection)) {
|
||||||
throw new WebSocketHandshakeException("Invalid handshake response connection: "
|
throw new WebSocketHandshakeException("Invalid handshake response connection: "
|
||||||
+ response.getHeader(Names.CONNECTION));
|
+ response.headers().get(Names.CONNECTION));
|
||||||
}
|
}
|
||||||
|
|
||||||
String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT);
|
String accept = response.headers().get(Names.SEC_WEBSOCKET_ACCEPT);
|
||||||
if (accept == null || !accept.equals(expectedChallengeResponseString)) {
|
if (accept == null || !accept.equals(expectedChallengeResponseString)) {
|
||||||
throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept,
|
throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept,
|
||||||
expectedChallengeResponseString));
|
expectedChallengeResponseString));
|
||||||
}
|
}
|
||||||
|
|
||||||
String subprotocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
String subprotocol = response.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||||
setActualSubprotocol(subprotocol);
|
setActualSubprotocol(subprotocol);
|
||||||
|
|
||||||
setHandshakeComplete();
|
setHandshakeComplete();
|
||||||
|
|
|
@ -18,7 +18,7 @@ package io.netty.handler.codec.http.websocketx;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.channel.ChannelPromise;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.util.internal.StringUtil;
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -112,7 +112,7 @@ public abstract class WebSocketServerHandshaker {
|
||||||
* @param req
|
* @param req
|
||||||
* HTTP Request
|
* HTTP Request
|
||||||
*/
|
*/
|
||||||
public ChannelFuture handshake(Channel channel, HttpRequest req) {
|
public ChannelFuture handshake(Channel channel, FullHttpRequest req) {
|
||||||
if (channel == null) {
|
if (channel == null) {
|
||||||
throw new NullPointerException("channel");
|
throw new NullPointerException("channel");
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ public abstract class WebSocketServerHandshaker {
|
||||||
* @param promise
|
* @param promise
|
||||||
* the {@link ChannelPromise} to be notified when the opening handshake is done
|
* the {@link ChannelPromise} to be notified when the opening handshake is done
|
||||||
*/
|
*/
|
||||||
public abstract ChannelFuture handshake(Channel channel, HttpRequest req, ChannelPromise promise);
|
public abstract ChannelFuture handshake(Channel channel, FullHttpRequest req, ChannelPromise promise);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs the closing handshake
|
* Performs the closing handshake
|
||||||
|
|
|
@ -22,13 +22,13 @@ import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.channel.ChannelPromise;
|
||||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
import io.netty.handler.codec.http.HttpHeaders.Values;
|
import io.netty.handler.codec.http.HttpHeaders.Values;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
|
||||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.logging.InternalLogger;
|
import io.netty.logging.InternalLogger;
|
||||||
|
@ -119,64 +119,63 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
|
||||||
* HTTP request
|
* HTTP request
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture handshake(Channel channel, HttpRequest req, ChannelPromise promise) {
|
public ChannelFuture handshake(Channel channel, FullHttpRequest req, ChannelPromise promise) {
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("Channel %s WS Version 00 server handshake", channel.id()));
|
logger.debug(String.format("Channel %s WS Version 00 server handshake", channel.id()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve the WebSocket handshake request.
|
// Serve the WebSocket handshake request.
|
||||||
if (!Values.UPGRADE.equalsIgnoreCase(req.getHeader(CONNECTION))
|
if (!Values.UPGRADE.equalsIgnoreCase(req.headers().get(CONNECTION))
|
||||||
|| !WEBSOCKET.equalsIgnoreCase(req.getHeader(Names.UPGRADE))) {
|
|| !WEBSOCKET.equalsIgnoreCase(req.headers().get(Names.UPGRADE))) {
|
||||||
throw new WebSocketHandshakeException("not a WebSocket handshake request: missing upgrade");
|
throw new WebSocketHandshakeException("not a WebSocket handshake request: missing upgrade");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hixie 75 does not contain these headers while Hixie 76 does
|
// Hixie 75 does not contain these headers while Hixie 76 does
|
||||||
boolean isHixie76 = req.containsHeader(SEC_WEBSOCKET_KEY1) && req.containsHeader(SEC_WEBSOCKET_KEY2);
|
boolean isHixie76 = req.headers().contains(SEC_WEBSOCKET_KEY1) && req.headers().contains(SEC_WEBSOCKET_KEY2);
|
||||||
|
|
||||||
// Create the WebSocket handshake response.
|
// Create the WebSocket handshake response.
|
||||||
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, new HttpResponseStatus(101,
|
FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, new HttpResponseStatus(101,
|
||||||
isHixie76 ? "WebSocket Protocol Handshake" : "Web Socket Protocol Handshake"));
|
isHixie76 ? "WebSocket Protocol Handshake" : "Web Socket Protocol Handshake"));
|
||||||
res.addHeader(Names.UPGRADE, WEBSOCKET);
|
res.headers().add(Names.UPGRADE, WEBSOCKET);
|
||||||
res.addHeader(CONNECTION, Values.UPGRADE);
|
res.headers().add(CONNECTION, Values.UPGRADE);
|
||||||
|
|
||||||
// Fill in the headers and contents depending on handshake method.
|
// Fill in the headers and contents depending on handshake method.
|
||||||
if (isHixie76) {
|
if (isHixie76) {
|
||||||
// New handshake method with a challenge:
|
// New handshake method with a challenge:
|
||||||
res.addHeader(SEC_WEBSOCKET_ORIGIN, req.getHeader(ORIGIN));
|
res.headers().add(SEC_WEBSOCKET_ORIGIN, req.headers().get(ORIGIN));
|
||||||
res.addHeader(SEC_WEBSOCKET_LOCATION, getWebSocketUrl());
|
res.headers().add(SEC_WEBSOCKET_LOCATION, getWebSocketUrl());
|
||||||
String subprotocols = req.getHeader(SEC_WEBSOCKET_PROTOCOL);
|
String subprotocols = req.headers().get(SEC_WEBSOCKET_PROTOCOL);
|
||||||
if (subprotocols != null) {
|
if (subprotocols != null) {
|
||||||
String selectedSubprotocol = selectSubprotocol(subprotocols);
|
String selectedSubprotocol = selectSubprotocol(subprotocols);
|
||||||
if (selectedSubprotocol == null) {
|
if (selectedSubprotocol == null) {
|
||||||
throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols);
|
throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols);
|
||||||
} else {
|
} else {
|
||||||
res.addHeader(SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
|
res.headers().add(SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
|
||||||
setSelectedSubprotocol(selectedSubprotocol);
|
setSelectedSubprotocol(selectedSubprotocol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the answer of the challenge.
|
// Calculate the answer of the challenge.
|
||||||
String key1 = req.getHeader(SEC_WEBSOCKET_KEY1);
|
String key1 = req.headers().get(SEC_WEBSOCKET_KEY1);
|
||||||
String key2 = req.getHeader(SEC_WEBSOCKET_KEY2);
|
String key2 = req.headers().get(SEC_WEBSOCKET_KEY2);
|
||||||
int a = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key1).replaceAll("")) /
|
int a = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key1).replaceAll("")) /
|
||||||
BEGINNING_SPACE.matcher(key1).replaceAll("").length());
|
BEGINNING_SPACE.matcher(key1).replaceAll("").length());
|
||||||
int b = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key2).replaceAll("")) /
|
int b = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key2).replaceAll("")) /
|
||||||
BEGINNING_SPACE.matcher(key2).replaceAll("").length());
|
BEGINNING_SPACE.matcher(key2).replaceAll("").length());
|
||||||
long c = req.getContent().readLong();
|
long c = req.data().readLong();
|
||||||
ByteBuf input = Unpooled.buffer(16);
|
ByteBuf input = Unpooled.buffer(16);
|
||||||
input.writeInt(a);
|
input.writeInt(a);
|
||||||
input.writeInt(b);
|
input.writeInt(b);
|
||||||
input.writeLong(c);
|
input.writeLong(c);
|
||||||
ByteBuf output = Unpooled.wrappedBuffer(WebSocketUtil.md5(input.array()));
|
res.data().writeBytes(WebSocketUtil.md5(input.array()));
|
||||||
res.setContent(output);
|
|
||||||
} else {
|
} else {
|
||||||
// Old Hixie 75 handshake method with no challenge:
|
// Old Hixie 75 handshake method with no challenge:
|
||||||
res.addHeader(WEBSOCKET_ORIGIN, req.getHeader(ORIGIN));
|
res.headers().add(WEBSOCKET_ORIGIN, req.headers().get(ORIGIN));
|
||||||
res.addHeader(WEBSOCKET_LOCATION, getWebSocketUrl());
|
res.headers().add(WEBSOCKET_LOCATION, getWebSocketUrl());
|
||||||
String protocol = req.getHeader(WEBSOCKET_PROTOCOL);
|
String protocol = req.headers().get(WEBSOCKET_PROTOCOL);
|
||||||
if (protocol != null) {
|
if (protocol != null) {
|
||||||
res.addHeader(WEBSOCKET_PROTOCOL, selectSubprotocol(protocol));
|
res.headers().add(WEBSOCKET_PROTOCOL, selectSubprotocol(protocol));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,12 +20,12 @@ import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.channel.ChannelPromise;
|
||||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
|
||||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.logging.InternalLogger;
|
import io.netty.logging.InternalLogger;
|
||||||
|
@ -109,15 +109,16 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker {
|
||||||
* HTTP request
|
* HTTP request
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture handshake(Channel channel, HttpRequest req, ChannelPromise promise) {
|
public ChannelFuture handshake(Channel channel, FullHttpRequest req, ChannelPromise promise) {
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("Channel %s WS Version 7 server handshake", channel.id()));
|
logger.debug(String.format("Channel %s WS Version 7 server handshake", channel.id()));
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS);
|
FullHttpResponse res =
|
||||||
|
new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS);
|
||||||
|
|
||||||
String key = req.getHeader(Names.SEC_WEBSOCKET_KEY);
|
String key = req.headers().get(Names.SEC_WEBSOCKET_KEY);
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
throw new WebSocketHandshakeException("not a WebSocket request: missing key");
|
throw new WebSocketHandshakeException("not a WebSocket request: missing key");
|
||||||
}
|
}
|
||||||
|
@ -129,17 +130,16 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker {
|
||||||
logger.debug(String.format("WS Version 7 Server Handshake key: %s. Response: %s.", key, accept));
|
logger.debug(String.format("WS Version 7 Server Handshake key: %s. Response: %s.", key, accept));
|
||||||
}
|
}
|
||||||
|
|
||||||
res.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS);
|
res.headers().add(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
||||||
res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
res.headers().add(Names.CONNECTION, Names.UPGRADE);
|
||||||
res.addHeader(Names.CONNECTION, Names.UPGRADE);
|
res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept);
|
||||||
res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept);
|
String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||||
String subprotocols = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
|
||||||
if (subprotocols != null) {
|
if (subprotocols != null) {
|
||||||
String selectedSubprotocol = selectSubprotocol(subprotocols);
|
String selectedSubprotocol = selectSubprotocol(subprotocols);
|
||||||
if (selectedSubprotocol == null) {
|
if (selectedSubprotocol == null) {
|
||||||
throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols);
|
throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols);
|
||||||
} else {
|
} else {
|
||||||
res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
|
res.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
|
||||||
setSelectedSubprotocol(selectedSubprotocol);
|
setSelectedSubprotocol(selectedSubprotocol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,12 @@ import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.channel.ChannelPromise;
|
||||||
import io.netty.handler.codec.http.DefaultHttpResponseHeader;
|
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseHeader;
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.logging.InternalLogger;
|
import io.netty.logging.InternalLogger;
|
||||||
|
@ -110,15 +110,15 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
|
||||||
* HTTP request
|
* HTTP request
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture handshake(Channel channel, HttpRequest req, ChannelPromise promise) {
|
public ChannelFuture handshake(Channel channel, FullHttpRequest req, ChannelPromise promise) {
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("Channel %s WS Version 8 server handshake", channel.id()));
|
logger.debug(String.format("Channel %s WS Version 8 server handshake", channel.id()));
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponseHeader res = new DefaultHttpResponseHeader(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS);
|
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS);
|
||||||
|
|
||||||
String key = req.getHeader(Names.SEC_WEBSOCKET_KEY);
|
String key = req.headers().get(Names.SEC_WEBSOCKET_KEY);
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
throw new WebSocketHandshakeException("not a WebSocket request: missing key");
|
throw new WebSocketHandshakeException("not a WebSocket request: missing key");
|
||||||
}
|
}
|
||||||
|
@ -130,17 +130,16 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
|
||||||
logger.debug(String.format("WS Version 8 Server Handshake key: %s. Response: %s.", key, accept));
|
logger.debug(String.format("WS Version 8 Server Handshake key: %s. Response: %s.", key, accept));
|
||||||
}
|
}
|
||||||
|
|
||||||
res.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS);
|
res.headers().add(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
||||||
res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
res.headers().add(Names.CONNECTION, Names.UPGRADE);
|
||||||
res.addHeader(Names.CONNECTION, Names.UPGRADE);
|
res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept);
|
||||||
res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept);
|
String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||||
String subprotocols = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
|
||||||
if (subprotocols != null) {
|
if (subprotocols != null) {
|
||||||
String selectedSubprotocol = selectSubprotocol(subprotocols);
|
String selectedSubprotocol = selectSubprotocol(subprotocols);
|
||||||
if (selectedSubprotocol == null) {
|
if (selectedSubprotocol == null) {
|
||||||
throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols);
|
throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols);
|
||||||
} else {
|
} else {
|
||||||
res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
|
res.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
|
||||||
setSelectedSubprotocol(selectedSubprotocol);
|
setSelectedSubprotocol(selectedSubprotocol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,12 @@ import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.channel.ChannelPromise;
|
||||||
import io.netty.handler.codec.http.DefaultHttpResponseHeader;
|
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseHeader;
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.logging.InternalLogger;
|
import io.netty.logging.InternalLogger;
|
||||||
|
@ -109,15 +109,15 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
|
||||||
* HTTP request
|
* HTTP request
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture handshake(Channel channel, HttpRequest req, ChannelPromise promise) {
|
public ChannelFuture handshake(Channel channel, FullHttpRequest req, ChannelPromise promise) {
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("Channel %s WS Version 13 server handshake", channel.id()));
|
logger.debug(String.format("Channel %s WS Version 13 server handshake", channel.id()));
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponseHeader res = new DefaultHttpResponseHeader(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS);
|
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS);
|
||||||
|
|
||||||
String key = req.getHeader(Names.SEC_WEBSOCKET_KEY);
|
String key = req.headers().get(Names.SEC_WEBSOCKET_KEY);
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
throw new WebSocketHandshakeException("not a WebSocket request: missing key");
|
throw new WebSocketHandshakeException("not a WebSocket request: missing key");
|
||||||
}
|
}
|
||||||
|
@ -129,18 +129,17 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
|
||||||
logger.debug(String.format("WS Version 13 Server Handshake key: %s. Response: %s.", key, accept));
|
logger.debug(String.format("WS Version 13 Server Handshake key: %s. Response: %s.", key, accept));
|
||||||
}
|
}
|
||||||
|
|
||||||
res.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS);
|
res.headers().add(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
||||||
res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
res.headers().add(Names.CONNECTION, Names.UPGRADE);
|
||||||
res.addHeader(Names.CONNECTION, Names.UPGRADE);
|
res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept);
|
||||||
res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept);
|
String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||||
String subprotocols = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
|
||||||
if (subprotocols != null) {
|
if (subprotocols != null) {
|
||||||
String selectedSubprotocol = selectSubprotocol(subprotocols);
|
String selectedSubprotocol = selectSubprotocol(subprotocols);
|
||||||
if (selectedSubprotocol == null) {
|
if (selectedSubprotocol == null) {
|
||||||
throw new WebSocketHandshakeException(
|
throw new WebSocketHandshakeException(
|
||||||
"Requested subprotocol(s) not supported: " + subprotocols);
|
"Requested subprotocol(s) not supported: " + subprotocols);
|
||||||
} else {
|
} else {
|
||||||
res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
|
res.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
|
||||||
setSelectedSubprotocol(selectedSubprotocol);
|
setSelectedSubprotocol(selectedSubprotocol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,10 +16,10 @@
|
||||||
package io.netty.handler.codec.http.websocketx;
|
package io.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.handler.codec.http.DefaultHttpResponseHeader;
|
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
import io.netty.handler.codec.http.HttpRequestHeader;
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpResponseHeader;
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http.HttpVersion;
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
|
|
||||||
|
@ -81,9 +81,9 @@ public class WebSocketServerHandshakerFactory {
|
||||||
* @return A new WebSocketServerHandshaker for the requested web socket version. Null if web
|
* @return A new WebSocketServerHandshaker for the requested web socket version. Null if web
|
||||||
* socket version is not supported.
|
* socket version is not supported.
|
||||||
*/
|
*/
|
||||||
public WebSocketServerHandshaker newHandshaker(HttpRequestHeader req) {
|
public WebSocketServerHandshaker newHandshaker(HttpRequest req) {
|
||||||
|
|
||||||
String version = req.getHeader(Names.SEC_WEBSOCKET_VERSION);
|
String version = req.headers().get(Names.SEC_WEBSOCKET_VERSION);
|
||||||
if (version != null) {
|
if (version != null) {
|
||||||
if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) {
|
if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) {
|
||||||
// Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification).
|
// Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification).
|
||||||
|
@ -113,11 +113,10 @@ public class WebSocketServerHandshakerFactory {
|
||||||
* Channel
|
* Channel
|
||||||
*/
|
*/
|
||||||
public static void sendUnsupportedWebSocketVersionResponse(Channel channel) {
|
public static void sendUnsupportedWebSocketVersionResponse(Channel channel) {
|
||||||
HttpResponseHeader res = new DefaultHttpResponseHeader(
|
HttpResponse res = new DefaultHttpResponse(
|
||||||
HttpVersion.HTTP_1_1,
|
HttpVersion.HTTP_1_1,
|
||||||
HttpResponseStatus.SWITCHING_PROTOCOLS);
|
HttpResponseStatus.UPGRADE_REQUIRED);
|
||||||
res.setStatus(HttpResponseStatus.UPGRADE_REQUIRED);
|
res.headers().set(Names.SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue());
|
||||||
res.setHeader(Names.SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue());
|
|
||||||
channel.write(res);
|
channel.write(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,8 @@ import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.util.AttributeKey;
|
import io.netty.util.AttributeKey;
|
||||||
|
|
||||||
|
@ -92,8 +92,8 @@ public class WebSocketServerProtocolHandler extends ChannelInboundMessageHandler
|
||||||
@Override
|
@Override
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||||
if (cause instanceof WebSocketHandshakeException) {
|
if (cause instanceof WebSocketHandshakeException) {
|
||||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.BAD_REQUEST);
|
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||||
response.setContent(Unpooled.wrappedBuffer(cause.getMessage().getBytes()));
|
HTTP_1_1, HttpResponseStatus.BAD_REQUEST, Unpooled.wrappedBuffer(cause.getMessage().getBytes()));
|
||||||
ctx.channel().write(response).addListener(ChannelFutureListener.CLOSE);
|
ctx.channel().write(response).addListener(ChannelFutureListener.CLOSE);
|
||||||
} else {
|
} else {
|
||||||
ctx.close();
|
ctx.close();
|
||||||
|
@ -113,7 +113,8 @@ public class WebSocketServerProtocolHandler extends ChannelInboundMessageHandler
|
||||||
@Override
|
@Override
|
||||||
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
if (!(msg instanceof WebSocketFrame)) {
|
if (!(msg instanceof WebSocketFrame)) {
|
||||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.FORBIDDEN);
|
FullHttpResponse response =
|
||||||
|
new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.FORBIDDEN);
|
||||||
ctx.channel().write(response);
|
ctx.channel().write(response);
|
||||||
} else {
|
} else {
|
||||||
ctx.nextInboundMessageBuffer().add(msg);
|
ctx.nextInboundMessageBuffer().add(msg);
|
||||||
|
|
|
@ -15,26 +15,28 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.websocketx;
|
package io.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http.HttpHeaders.isKeepAlive;
|
|
||||||
import static io.netty.handler.codec.http.HttpMethod.GET;
|
|
||||||
import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;
|
|
||||||
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpRequestHeader;
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpResponseHeader;
|
|
||||||
import io.netty.handler.ssl.SslHandler;
|
import io.netty.handler.ssl.SslHandler;
|
||||||
|
|
||||||
|
import static io.netty.handler.codec.http.HttpHeaders.*;
|
||||||
|
import static io.netty.handler.codec.http.HttpMethod.*;
|
||||||
|
import static io.netty.handler.codec.http.HttpResponseStatus.*;
|
||||||
|
import static io.netty.handler.codec.http.HttpVersion.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the HTTP handshake (the HTTP Upgrade request) for {@link WebSocketServerProtocolHandler}.
|
* Handles the HTTP handshake (the HTTP Upgrade request) for {@link WebSocketServerProtocolHandler}.
|
||||||
*/
|
*/
|
||||||
public class WebSocketServerProtocolHandshakeHandler extends ChannelInboundMessageHandlerAdapter<HttpRequest> {
|
public class WebSocketServerProtocolHandshakeHandler
|
||||||
|
extends ChannelInboundMessageHandlerAdapter<FullHttpRequest> {
|
||||||
|
|
||||||
private final String websocketPath;
|
private final String websocketPath;
|
||||||
private final String subprotocols;
|
private final String subprotocols;
|
||||||
|
@ -48,9 +50,9 @@ public class WebSocketServerProtocolHandshakeHandler extends ChannelInboundMessa
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void messageReceived(final ChannelHandlerContext ctx, HttpRequest req) throws Exception {
|
public void messageReceived(final ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
|
||||||
if (req.getMethod() != GET) {
|
if (req.method() != GET) {
|
||||||
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN));
|
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,20 +77,20 @@ public class WebSocketServerProtocolHandshakeHandler extends ChannelInboundMessa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequestHeader req, HttpResponseHeader res) {
|
private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) {
|
||||||
ChannelFuture f = ctx.channel().write(res);
|
ChannelFuture f = ctx.channel().write(res);
|
||||||
if (!isKeepAlive(req) || res.getStatus().getCode() != 200) {
|
if (!isKeepAlive(req) || res.status().code() != 200) {
|
||||||
f.addListener(ChannelFutureListener.CLOSE);
|
f.addListener(ChannelFutureListener.CLOSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getWebSocketLocation(ChannelPipeline cp, HttpRequestHeader req, String path) {
|
private static String getWebSocketLocation(ChannelPipeline cp, HttpRequest req, String path) {
|
||||||
String protocol = "ws";
|
String protocol = "ws";
|
||||||
if (cp.get(SslHandler.class) != null) {
|
if (cp.get(SslHandler.class) != null) {
|
||||||
// SSL in use so use Secure WebSockets
|
// SSL in use so use Secure WebSockets
|
||||||
protocol = "wss";
|
protocol = "wss";
|
||||||
}
|
}
|
||||||
return protocol + "://" + req.getHeader(HttpHeaders.Names.HOST) + path;
|
return protocol + "://" + req.headers().get(HttpHeaders.Names.HOST) + path;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,13 +19,13 @@ import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.embedded.EmbeddedMessageChannel;
|
import io.netty.channel.embedded.EmbeddedMessageChannel;
|
||||||
import io.netty.handler.codec.TooLongFrameException;
|
import io.netty.handler.codec.TooLongFrameException;
|
||||||
import io.netty.handler.codec.http.HttpHeader;
|
import io.netty.handler.codec.http.HttpMessage;
|
||||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||||
import io.netty.handler.codec.http.HttpObjectDecoder;
|
import io.netty.handler.codec.http.HttpObjectDecoder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes {@link ByteBuf}s into RTSP messages represented in
|
* Decodes {@link ByteBuf}s into RTSP messages represented in
|
||||||
* {@link io.netty.handler.codec.http.HttpHeader}s.
|
* {@link io.netty.handler.codec.http.HttpMessage}s.
|
||||||
* <p>
|
* <p>
|
||||||
* <h3>Parameters that prevents excessive memory consumption</h3>
|
* <h3>Parameters that prevents excessive memory consumption</h3>
|
||||||
* <table border="1">
|
* <table border="1">
|
||||||
|
@ -84,14 +84,14 @@ public abstract class RtspObjectDecoder extends HttpObjectDecoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isContentAlwaysEmpty(HttpHeader msg) {
|
protected boolean isContentAlwaysEmpty(HttpMessage msg) {
|
||||||
// Unlike HTTP, RTSP always assumes zero-length body if Content-Length
|
// Unlike HTTP, RTSP always assumes zero-length body if Content-Length
|
||||||
// header is absent.
|
// header is absent.
|
||||||
boolean empty = super.isContentAlwaysEmpty(msg);
|
boolean empty = super.isContentAlwaysEmpty(msg);
|
||||||
if (empty) {
|
if (empty) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!msg.containsHeader(RtspHeaders.Names.CONTENT_LENGTH)) {
|
if (!msg.headers().contains(RtspHeaders.Names.CONTENT_LENGTH)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return empty;
|
return empty;
|
||||||
|
|
|
@ -17,20 +17,19 @@ package io.netty.handler.codec.rtsp;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelHandler.Sharable;
|
import io.netty.channel.ChannelHandler.Sharable;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.handler.codec.http.FullHttpMessage;
|
||||||
import io.netty.handler.codec.UnsupportedMessageTypeException;
|
import io.netty.handler.codec.http.HttpMessage;
|
||||||
import io.netty.handler.codec.http.HttpHeader;
|
|
||||||
import io.netty.handler.codec.http.HttpObjectEncoder;
|
import io.netty.handler.codec.http.HttpObjectEncoder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes an RTSP message represented in {@link HttpHeader} into
|
* Encodes an RTSP message represented in {@link FullHttpMessage} into
|
||||||
* a {@link ByteBuf}.
|
* a {@link ByteBuf}.
|
||||||
|
|
||||||
*
|
*
|
||||||
* @apiviz.landmark
|
* @apiviz.landmark
|
||||||
*/
|
*/
|
||||||
@Sharable
|
@Sharable
|
||||||
public abstract class RtspObjectEncoder<H extends HttpHeader> extends HttpObjectEncoder<H> {
|
public abstract class RtspObjectEncoder<H extends HttpMessage> extends HttpObjectEncoder<H> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
|
@ -39,13 +38,7 @@ public abstract class RtspObjectEncoder<H extends HttpHeader> extends HttpObject
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void encode(ChannelHandlerContext ctx, Object msg,
|
public boolean isEncodable(Object msg) throws Exception {
|
||||||
ByteBuf out) throws Exception {
|
return msg instanceof FullHttpMessage;
|
||||||
// Ignore unrelated message types such as HttpChunk.
|
|
||||||
if (!(msg instanceof HttpHeader)) {
|
|
||||||
throw new UnsupportedMessageTypeException(msg, HttpHeader.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
super.encode(ctx, msg, out);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,12 @@ package io.netty.handler.codec.rtsp;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.handler.codec.TooLongFrameException;
|
import io.netty.handler.codec.TooLongFrameException;
|
||||||
import io.netty.handler.codec.http.DefaultHttpRequestHeader;
|
import io.netty.handler.codec.http.DefaultHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpHeader;
|
import io.netty.handler.codec.http.HttpMessage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes {@link ByteBuf}s into RTSP requests represented in
|
* Decodes {@link ByteBuf}s into RTSP requests represented in
|
||||||
* {@link io.netty.handler.codec.http.HttpRequestHeader}s.
|
* {@link io.netty.handler.codec.http.HttpRequest}s.
|
||||||
* <p>
|
* <p>
|
||||||
* <h3>Parameters that prevents excessive memory consumption</h3>
|
* <h3>Parameters that prevents excessive memory consumption</h3>
|
||||||
* <table border="1">
|
* <table border="1">
|
||||||
|
@ -65,14 +65,14 @@ public class RtspRequestDecoder extends RtspObjectDecoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpHeader createMessage(String[] initialLine) throws Exception {
|
protected HttpMessage createMessage(String[] initialLine) throws Exception {
|
||||||
return new DefaultHttpRequestHeader(RtspVersions.valueOf(initialLine[2]),
|
return new DefaultHttpRequest(RtspVersions.valueOf(initialLine[2]),
|
||||||
RtspMethods.valueOf(initialLine[0]), initialLine[1]);
|
RtspMethods.valueOf(initialLine[0]), initialLine[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpHeader createInvalidMessage() {
|
protected HttpMessage createInvalidMessage() {
|
||||||
return new DefaultHttpRequestHeader(RtspVersions.RTSP_1_0, RtspMethods.OPTIONS, "/bad-request");
|
return new DefaultHttpRequest(RtspVersions.RTSP_1_0, RtspMethods.OPTIONS, "/bad-request");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,24 +16,30 @@
|
||||||
package io.netty.handler.codec.rtsp;
|
package io.netty.handler.codec.rtsp;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.handler.codec.http.HttpRequestHeader;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes an RTSP request represented in {@link HttpRequestHeader} into
|
* Encodes an RTSP request represented in {@link FullHttpRequest} into
|
||||||
* a {@link ByteBuf}.
|
* a {@link ByteBuf}.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
public class RtspRequestEncoder extends RtspObjectEncoder<HttpRequestHeader> {
|
public class RtspRequestEncoder extends RtspObjectEncoder<HttpRequest> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void encodeInitialLine(ByteBuf buf, HttpRequestHeader request)
|
public boolean isEncodable(Object msg) throws Exception {
|
||||||
|
return msg instanceof FullHttpRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void encodeInitialLine(ByteBuf buf, HttpRequest request)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
buf.writeBytes(request.getMethod().toString().getBytes(CharsetUtil.US_ASCII));
|
buf.writeBytes(request.method().toString().getBytes(CharsetUtil.US_ASCII));
|
||||||
buf.writeByte((byte) ' ');
|
buf.writeByte((byte) ' ');
|
||||||
buf.writeBytes(request.getUri().getBytes(CharsetUtil.UTF_8));
|
buf.writeBytes(request.uri().getBytes(CharsetUtil.UTF_8));
|
||||||
buf.writeByte((byte) ' ');
|
buf.writeByte((byte) ' ');
|
||||||
buf.writeBytes(request.getProtocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
|
buf.writeBytes(request.protocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
|
||||||
buf.writeByte((byte) '\r');
|
buf.writeByte((byte) '\r');
|
||||||
buf.writeByte((byte) '\n');
|
buf.writeByte((byte) '\n');
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,13 @@ package io.netty.handler.codec.rtsp;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.handler.codec.TooLongFrameException;
|
import io.netty.handler.codec.TooLongFrameException;
|
||||||
import io.netty.handler.codec.http.DefaultHttpResponseHeader;
|
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpHeader;
|
import io.netty.handler.codec.http.HttpMessage;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes {@link ByteBuf}s into RTSP responses represented in
|
* Decodes {@link ByteBuf}s into RTSP responses represented in
|
||||||
* {@link io.netty.handler.codec.http.HttpResponseHeader}s.
|
* {@link io.netty.handler.codec.http.HttpResponse}s.
|
||||||
* <p>
|
* <p>
|
||||||
* <h3>Parameters that prevents excessive memory consumption</h3>
|
* <h3>Parameters that prevents excessive memory consumption</h3>
|
||||||
* <table border="1">
|
* <table border="1">
|
||||||
|
@ -69,15 +69,15 @@ public class RtspResponseDecoder extends RtspObjectDecoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpHeader createMessage(String[] initialLine) throws Exception {
|
protected HttpMessage createMessage(String[] initialLine) throws Exception {
|
||||||
return new DefaultHttpResponseHeader(
|
return new DefaultHttpResponse(
|
||||||
RtspVersions.valueOf(initialLine[0]),
|
RtspVersions.valueOf(initialLine[0]),
|
||||||
new HttpResponseStatus(Integer.valueOf(initialLine[1]), initialLine[2]));
|
new HttpResponseStatus(Integer.valueOf(initialLine[1]), initialLine[2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpHeader createInvalidMessage() {
|
protected HttpMessage createInvalidMessage() {
|
||||||
return new DefaultHttpResponseHeader(RtspVersions.RTSP_1_0, UNKNOWN_STATUS);
|
return new DefaultHttpResponse(RtspVersions.RTSP_1_0, UNKNOWN_STATUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,25 +16,30 @@
|
||||||
package io.netty.handler.codec.rtsp;
|
package io.netty.handler.codec.rtsp;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.handler.codec.http.HttpHeader;
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpResponseHeader;
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes an RTSP response represented in {@link HttpResponseHeader} into
|
* Encodes an RTSP response represented in {@link FullHttpResponse} into
|
||||||
* a {@link ByteBuf}.
|
* a {@link ByteBuf}.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
public class RtspResponseEncoder extends RtspObjectEncoder<HttpResponseHeader> {
|
public class RtspResponseEncoder extends RtspObjectEncoder<HttpResponse> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void encodeInitialLine(ByteBuf buf, HttpResponseHeader response)
|
public boolean isEncodable(Object msg) throws Exception {
|
||||||
|
return msg instanceof FullHttpResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void encodeInitialLine(ByteBuf buf, HttpResponse response)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
buf.writeBytes(response.getProtocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
|
buf.writeBytes(response.protocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
|
||||||
buf.writeByte((byte) ' ');
|
buf.writeByte((byte) ' ');
|
||||||
buf.writeBytes(String.valueOf(response.getStatus().getCode()).getBytes(CharsetUtil.US_ASCII));
|
buf.writeBytes(String.valueOf(response.status().code()).getBytes(CharsetUtil.US_ASCII));
|
||||||
buf.writeByte((byte) ' ');
|
buf.writeByte((byte) ' ');
|
||||||
buf.writeBytes(String.valueOf(response.getStatus().getReasonPhrase()).getBytes(CharsetUtil.US_ASCII));
|
buf.writeBytes(String.valueOf(response.status().reasonPhrase()).getBytes(CharsetUtil.US_ASCII));
|
||||||
buf.writeByte((byte) '\r');
|
buf.writeByte((byte) '\r');
|
||||||
buf.writeByte((byte) '\n');
|
buf.writeByte((byte) '\n');
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,9 +196,9 @@ public class SpdyHeaders {
|
||||||
*/
|
*/
|
||||||
public static void setMethod(int spdyVersion, SpdyHeaderBlock block, HttpMethod method) {
|
public static void setMethod(int spdyVersion, SpdyHeaderBlock block, HttpMethod method) {
|
||||||
if (spdyVersion < 3) {
|
if (spdyVersion < 3) {
|
||||||
block.setHeader(Spdy2HttpNames.METHOD, method.getName());
|
block.setHeader(Spdy2HttpNames.METHOD, method.name());
|
||||||
} else {
|
} else {
|
||||||
block.setHeader(HttpNames.METHOD, method.getName());
|
block.setHeader(HttpNames.METHOD, method.name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,7 +264,7 @@ public class SpdyHeaders {
|
||||||
int code = Integer.parseInt(status.substring(0, space));
|
int code = Integer.parseInt(status.substring(0, space));
|
||||||
String reasonPhrase = status.substring(space + 1);
|
String reasonPhrase = status.substring(space + 1);
|
||||||
HttpResponseStatus responseStatus = HttpResponseStatus.valueOf(code);
|
HttpResponseStatus responseStatus = HttpResponseStatus.valueOf(code);
|
||||||
if (responseStatus.getReasonPhrase().equals(reasonPhrase)) {
|
if (responseStatus.reasonPhrase().equals(reasonPhrase)) {
|
||||||
return responseStatus;
|
return responseStatus;
|
||||||
} else {
|
} else {
|
||||||
return new HttpResponseStatus(code, reasonPhrase);
|
return new HttpResponseStatus(code, reasonPhrase);
|
||||||
|
|
|
@ -16,18 +16,17 @@
|
||||||
package io.netty.handler.codec.spdy;
|
package io.netty.handler.codec.spdy;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||||
import io.netty.handler.codec.TooLongFrameException;
|
import io.netty.handler.codec.TooLongFrameException;
|
||||||
import io.netty.handler.codec.http.DefaultHttpRequest;
|
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpHeader;
|
import io.netty.handler.codec.http.FullHttpMessage;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpMessage;
|
import io.netty.handler.codec.http.HttpMessage;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http.HttpVersion;
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
|
|
||||||
|
@ -36,13 +35,13 @@ import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes {@link SpdySynStreamFrame}s, {@link SpdySynReplyFrame}s,
|
* Decodes {@link SpdySynStreamFrame}s, {@link SpdySynReplyFrame}s,
|
||||||
* and {@link SpdyDataFrame}s into {@link HttpRequest}s and {@link HttpResponse}s.
|
* and {@link SpdyDataFrame}s into {@link FullHttpRequest}s and {@link FullHttpResponse}s.
|
||||||
*/
|
*/
|
||||||
public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||||
|
|
||||||
private final int spdyVersion;
|
private final int spdyVersion;
|
||||||
private final int maxContentLength;
|
private final int maxContentLength;
|
||||||
private final Map<Integer, HttpMessage> messageMap = new HashMap<Integer, HttpMessage>();
|
private final Map<Integer, FullHttpMessage> messageMap = new HashMap<Integer, FullHttpMessage>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
|
@ -98,20 +97,21 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
HttpResponse httpResponse = createHttpResponse(spdyVersion, spdySynStreamFrame);
|
FullHttpResponse httpResponseWithEntity =
|
||||||
|
createHttpResponse(spdyVersion, spdySynStreamFrame);
|
||||||
|
|
||||||
// Set the Stream-ID, Associated-To-Stream-ID, Priority, and URL as headers
|
// Set the Stream-ID, Associated-To-Stream-ID, Priority, and URL as headers
|
||||||
SpdyHttpHeaders.setStreamId(httpResponse, streamID);
|
SpdyHttpHeaders.setStreamId(httpResponseWithEntity, streamID);
|
||||||
SpdyHttpHeaders.setAssociatedToStreamId(httpResponse, associatedToStreamId);
|
SpdyHttpHeaders.setAssociatedToStreamId(httpResponseWithEntity, associatedToStreamId);
|
||||||
SpdyHttpHeaders.setPriority(httpResponse, spdySynStreamFrame.getPriority());
|
SpdyHttpHeaders.setPriority(httpResponseWithEntity, spdySynStreamFrame.getPriority());
|
||||||
SpdyHttpHeaders.setUrl(httpResponse, URL);
|
SpdyHttpHeaders.setUrl(httpResponseWithEntity, URL);
|
||||||
|
|
||||||
if (spdySynStreamFrame.isLast()) {
|
if (spdySynStreamFrame.isLast()) {
|
||||||
HttpHeaders.setContentLength(httpResponse, 0);
|
HttpHeaders.setContentLength(httpResponseWithEntity, 0);
|
||||||
return httpResponse;
|
return httpResponseWithEntity;
|
||||||
} else {
|
} else {
|
||||||
// Response body will follow in a series of Data Frames
|
// Response body will follow in a series of Data Frames
|
||||||
messageMap.put(Integer.valueOf(streamID), httpResponse);
|
messageMap.put(Integer.valueOf(streamID), httpResponseWithEntity);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
SpdyRstStreamFrame spdyRstStreamFrame =
|
SpdyRstStreamFrame spdyRstStreamFrame =
|
||||||
|
@ -121,16 +121,16 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||||
} else {
|
} else {
|
||||||
// SYN_STREAM frames initiated by the client are HTTP requests
|
// SYN_STREAM frames initiated by the client are HTTP requests
|
||||||
try {
|
try {
|
||||||
HttpRequest httpRequest = createHttpRequest(spdyVersion, spdySynStreamFrame);
|
FullHttpRequest httpRequestWithEntity = createHttpRequest(spdyVersion, spdySynStreamFrame);
|
||||||
|
|
||||||
// Set the Stream-ID as a header
|
// Set the Stream-ID as a header
|
||||||
SpdyHttpHeaders.setStreamId(httpRequest, streamID);
|
SpdyHttpHeaders.setStreamId(httpRequestWithEntity, streamID);
|
||||||
|
|
||||||
if (spdySynStreamFrame.isLast()) {
|
if (spdySynStreamFrame.isLast()) {
|
||||||
return httpRequest;
|
return httpRequestWithEntity;
|
||||||
} else {
|
} else {
|
||||||
// Request body will follow in a series of Data Frames
|
// Request body will follow in a series of Data Frames
|
||||||
messageMap.put(Integer.valueOf(streamID), httpRequest);
|
messageMap.put(Integer.valueOf(streamID), httpRequestWithEntity);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// If a client sends a SYN_STREAM without all of the method, url (host and path),
|
// If a client sends a SYN_STREAM without all of the method, url (host and path),
|
||||||
|
@ -150,17 +150,17 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||||
int streamID = spdySynReplyFrame.getStreamId();
|
int streamID = spdySynReplyFrame.getStreamId();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
HttpResponse httpResponse = createHttpResponse(spdyVersion, spdySynReplyFrame);
|
FullHttpResponse httpResponseWithEntity = createHttpResponse(spdyVersion, spdySynReplyFrame);
|
||||||
|
|
||||||
// Set the Stream-ID as a header
|
// Set the Stream-ID as a header
|
||||||
SpdyHttpHeaders.setStreamId(httpResponse, streamID);
|
SpdyHttpHeaders.setStreamId(httpResponseWithEntity, streamID);
|
||||||
|
|
||||||
if (spdySynReplyFrame.isLast()) {
|
if (spdySynReplyFrame.isLast()) {
|
||||||
HttpHeaders.setContentLength(httpResponse, 0);
|
HttpHeaders.setContentLength(httpResponseWithEntity, 0);
|
||||||
return httpResponse;
|
return httpResponseWithEntity;
|
||||||
} else {
|
} else {
|
||||||
// Response body will follow in a series of Data Frames
|
// Response body will follow in a series of Data Frames
|
||||||
messageMap.put(Integer.valueOf(streamID), httpResponse);
|
messageMap.put(Integer.valueOf(streamID), httpResponseWithEntity);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// If a client receives a SYN_REPLY without valid status and version headers
|
// If a client receives a SYN_REPLY without valid status and version headers
|
||||||
|
@ -174,7 +174,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||||
|
|
||||||
SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
|
SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
|
||||||
Integer streamID = Integer.valueOf(spdyHeadersFrame.getStreamId());
|
Integer streamID = Integer.valueOf(spdyHeadersFrame.getStreamId());
|
||||||
HttpHeader httpMessage = messageMap.get(streamID);
|
HttpMessage httpMessage = messageMap.get(streamID);
|
||||||
|
|
||||||
// If message is not in map discard HEADERS frame.
|
// If message is not in map discard HEADERS frame.
|
||||||
// SpdySessionHandler should prevent this from happening.
|
// SpdySessionHandler should prevent this from happening.
|
||||||
|
@ -183,22 +183,22 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Map.Entry<String, String> e: spdyHeadersFrame.getHeaders()) {
|
for (Map.Entry<String, String> e: spdyHeadersFrame.getHeaders()) {
|
||||||
httpMessage.addHeader(e.getKey(), e.getValue());
|
httpMessage.headers().add(e.getKey(), e.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (msg instanceof SpdyDataFrame) {
|
} else if (msg instanceof SpdyDataFrame) {
|
||||||
|
|
||||||
SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
|
SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
|
||||||
Integer streamID = Integer.valueOf(spdyDataFrame.getStreamId());
|
Integer streamID = Integer.valueOf(spdyDataFrame.getStreamId());
|
||||||
HttpMessage httpMessage = messageMap.get(streamID);
|
FullHttpMessage fullHttpMessage = messageMap.get(streamID);
|
||||||
|
|
||||||
// If message is not in map discard Data Frame.
|
// If message is not in map discard Data Frame.
|
||||||
// SpdySessionHandler should prevent this from happening.
|
// SpdySessionHandler should prevent this from happening.
|
||||||
if (httpMessage == null) {
|
if (fullHttpMessage == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteBuf content = httpMessage.getContent();
|
ByteBuf content = fullHttpMessage.data();
|
||||||
if (content.readableBytes() > maxContentLength - spdyDataFrame.getData().readableBytes()) {
|
if (content.readableBytes() > maxContentLength - spdyDataFrame.getData().readableBytes()) {
|
||||||
messageMap.remove(streamID);
|
messageMap.remove(streamID);
|
||||||
throw new TooLongFrameException(
|
throw new TooLongFrameException(
|
||||||
|
@ -207,18 +207,12 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||||
|
|
||||||
ByteBuf spdyDataFrameData = spdyDataFrame.getData();
|
ByteBuf spdyDataFrameData = spdyDataFrame.getData();
|
||||||
int spdyDataFrameDataLen = spdyDataFrameData.readableBytes();
|
int spdyDataFrameDataLen = spdyDataFrameData.readableBytes();
|
||||||
if (content == Unpooled.EMPTY_BUFFER) {
|
content.writeBytes(spdyDataFrameData, spdyDataFrameData.readerIndex(), spdyDataFrameDataLen);
|
||||||
content = Unpooled.buffer(spdyDataFrameDataLen);
|
|
||||||
content.writeBytes(spdyDataFrameData, spdyDataFrameData.readerIndex(), spdyDataFrameDataLen);
|
|
||||||
httpMessage.setContent(content);
|
|
||||||
} else {
|
|
||||||
content.writeBytes(spdyDataFrameData, spdyDataFrameData.readerIndex(), spdyDataFrameDataLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spdyDataFrame.isLast()) {
|
if (spdyDataFrame.isLast()) {
|
||||||
HttpHeaders.setContentLength(httpMessage, content.readableBytes());
|
HttpHeaders.setContentLength(fullHttpMessage, content.readableBytes());
|
||||||
messageMap.remove(streamID);
|
messageMap.remove(streamID);
|
||||||
return httpMessage;
|
return fullHttpMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (msg instanceof SpdyRstStreamFrame) {
|
} else if (msg instanceof SpdyRstStreamFrame) {
|
||||||
|
@ -231,7 +225,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpRequest createHttpRequest(int spdyVersion, SpdyHeaderBlock requestFrame)
|
private static FullHttpRequest createHttpRequest(int spdyVersion, SpdyHeaderBlock requestFrame)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
// Create the first line of the request from the name/value pairs
|
// Create the first line of the request from the name/value pairs
|
||||||
HttpMethod method = SpdyHeaders.getMethod(spdyVersion, requestFrame);
|
HttpMethod method = SpdyHeaders.getMethod(spdyVersion, requestFrame);
|
||||||
|
@ -241,7 +235,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||||
SpdyHeaders.removeUrl(spdyVersion, requestFrame);
|
SpdyHeaders.removeUrl(spdyVersion, requestFrame);
|
||||||
SpdyHeaders.removeVersion(spdyVersion, requestFrame);
|
SpdyHeaders.removeVersion(spdyVersion, requestFrame);
|
||||||
|
|
||||||
HttpRequest httpRequest = new DefaultHttpRequest(httpVersion, method, url);
|
FullHttpRequest httpRequestWithEntity = new DefaultFullHttpRequest(httpVersion, method, url);
|
||||||
|
|
||||||
// Remove the scheme header
|
// Remove the scheme header
|
||||||
SpdyHeaders.removeScheme(spdyVersion, requestFrame);
|
SpdyHeaders.removeScheme(spdyVersion, requestFrame);
|
||||||
|
@ -250,23 +244,23 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||||
// Replace the SPDY host header with the HTTP host header
|
// Replace the SPDY host header with the HTTP host header
|
||||||
String host = SpdyHeaders.getHost(requestFrame);
|
String host = SpdyHeaders.getHost(requestFrame);
|
||||||
SpdyHeaders.removeHost(requestFrame);
|
SpdyHeaders.removeHost(requestFrame);
|
||||||
HttpHeaders.setHost(httpRequest, host);
|
HttpHeaders.setHost(httpRequestWithEntity, host);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Map.Entry<String, String> e: requestFrame.getHeaders()) {
|
for (Map.Entry<String, String> e: requestFrame.getHeaders()) {
|
||||||
httpRequest.addHeader(e.getKey(), e.getValue());
|
httpRequestWithEntity.headers().add(e.getKey(), e.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Connection and Keep-Alive headers are no longer valid
|
// The Connection and Keep-Alive headers are no longer valid
|
||||||
HttpHeaders.setKeepAlive(httpRequest, true);
|
HttpHeaders.setKeepAlive(httpRequestWithEntity, true);
|
||||||
|
|
||||||
// Transfer-Encoding header is not valid
|
// Transfer-Encoding header is not valid
|
||||||
httpRequest.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
|
httpRequestWithEntity.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||||
|
|
||||||
return httpRequest;
|
return httpRequestWithEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpResponse createHttpResponse(int spdyVersion, SpdyHeaderBlock responseFrame)
|
private static FullHttpResponse createHttpResponse(int spdyVersion, SpdyHeaderBlock responseFrame)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
// Create the first line of the response from the name/value pairs
|
// Create the first line of the response from the name/value pairs
|
||||||
HttpResponseStatus status = SpdyHeaders.getStatus(spdyVersion, responseFrame);
|
HttpResponseStatus status = SpdyHeaders.getStatus(spdyVersion, responseFrame);
|
||||||
|
@ -274,18 +268,18 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
||||||
SpdyHeaders.removeStatus(spdyVersion, responseFrame);
|
SpdyHeaders.removeStatus(spdyVersion, responseFrame);
|
||||||
SpdyHeaders.removeVersion(spdyVersion, responseFrame);
|
SpdyHeaders.removeVersion(spdyVersion, responseFrame);
|
||||||
|
|
||||||
HttpResponse httpResponse = new DefaultHttpResponse(version, status);
|
FullHttpResponse httpResponseWithEntity = new DefaultFullHttpResponse(version, status);
|
||||||
for (Map.Entry<String, String> e: responseFrame.getHeaders()) {
|
for (Map.Entry<String, String> e: responseFrame.getHeaders()) {
|
||||||
httpResponse.addHeader(e.getKey(), e.getValue());
|
httpResponseWithEntity.headers().add(e.getKey(), e.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Connection and Keep-Alive headers are no longer valid
|
// The Connection and Keep-Alive headers are no longer valid
|
||||||
HttpHeaders.setKeepAlive(httpResponse, true);
|
HttpHeaders.setKeepAlive(httpResponseWithEntity, true);
|
||||||
|
|
||||||
// Transfer-Encoding header is not valid
|
// Transfer-Encoding header is not valid
|
||||||
httpResponse.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
|
httpResponseWithEntity.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||||
httpResponse.removeHeader(HttpHeaders.Names.TRAILER);
|
httpResponseWithEntity.headers().remove(HttpHeaders.Names.TRAILER);
|
||||||
|
|
||||||
return httpResponse;
|
return httpResponseWithEntity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,26 +18,26 @@ package io.netty.handler.codec.spdy;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||||
import io.netty.handler.codec.UnsupportedMessageTypeException;
|
import io.netty.handler.codec.UnsupportedMessageTypeException;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpContent;
|
import io.netty.handler.codec.http.HttpContent;
|
||||||
import io.netty.handler.codec.http.HttpHeader;
|
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpMessage;
|
||||||
import io.netty.handler.codec.http.HttpRequestHeader;
|
|
||||||
import io.netty.handler.codec.http.HttpResponseHeader;
|
|
||||||
import io.netty.handler.codec.http.LastHttpContent;
|
|
||||||
import io.netty.handler.codec.http.HttpObject;
|
import io.netty.handler.codec.http.HttpObject;
|
||||||
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
|
import io.netty.handler.codec.http.LastHttpContent;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes {@link HttpRequestHeader}s, {@link HttpResponseHeader}s, and {@link HttpContent}s
|
* Encodes {@link HttpRequest}s, {@link HttpResponse}s, and {@link HttpContent}s
|
||||||
* into {@link SpdySynStreamFrame}s and {@link SpdySynReplyFrame}s.
|
* into {@link SpdySynStreamFrame}s and {@link SpdySynReplyFrame}s.
|
||||||
*
|
*
|
||||||
* <h3>Request Annotations</h3>
|
* <h3>Request Annotations</h3>
|
||||||
*
|
*
|
||||||
* SPDY specific headers must be added to {@link HttpRequestHeader}s:
|
* SPDY specific headers must be added to {@link HttpRequest}s:
|
||||||
* <table border=1>
|
* <table border=1>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <th>Header Name</th><th>Header Value</th>
|
* <th>Header Name</th><th>Header Value</th>
|
||||||
|
@ -58,7 +58,7 @@ import java.util.Map;
|
||||||
*
|
*
|
||||||
* <h3>Response Annotations</h3>
|
* <h3>Response Annotations</h3>
|
||||||
*
|
*
|
||||||
* SPDY specific headers must be added to {@link HttpResponseHeader}s:
|
* SPDY specific headers must be added to {@link HttpResponse}s:
|
||||||
* <table border=1>
|
* <table border=1>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <th>Header Name</th><th>Header Value</th>
|
* <th>Header Name</th><th>Header Value</th>
|
||||||
|
@ -71,7 +71,7 @@ import java.util.Map;
|
||||||
*
|
*
|
||||||
* <h3>Pushed Resource Annotations</h3>
|
* <h3>Pushed Resource Annotations</h3>
|
||||||
*
|
*
|
||||||
* SPDY specific headers must be added to pushed {@link HttpResponseHeader}s:
|
* SPDY specific headers must be added to pushed {@link HttpResponse}s:
|
||||||
* <table border=1>
|
* <table border=1>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <th>Header Name</th><th>Header Value</th>
|
* <th>Header Name</th><th>Header Value</th>
|
||||||
|
@ -112,8 +112,8 @@ import java.util.Map;
|
||||||
* <h3>Chunked Content</h3>
|
* <h3>Chunked Content</h3>
|
||||||
*
|
*
|
||||||
* This encoder associates all {@link HttpContent}s that it receives
|
* This encoder associates all {@link HttpContent}s that it receives
|
||||||
* with the most recently received 'chunked' {@link HttpRequestHeader}
|
* with the most recently received 'chunked' {@link HttpRequest}
|
||||||
* or {@link HttpResponseHeader}.
|
* or {@link HttpResponse}.
|
||||||
*
|
*
|
||||||
* <h3>Pushed Resources</h3>
|
* <h3>Pushed Resources</h3>
|
||||||
*
|
*
|
||||||
|
@ -144,16 +144,16 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
|
||||||
public Object encode(ChannelHandlerContext ctx, Object msg) throws Exception {
|
public Object encode(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
|
|
||||||
List<Object> out = new ArrayList<Object>();
|
List<Object> out = new ArrayList<Object>();
|
||||||
if (msg instanceof HttpRequestHeader) {
|
if (msg instanceof HttpRequest) {
|
||||||
|
|
||||||
HttpRequestHeader httpRequest = (HttpRequestHeader) msg;
|
HttpRequest httpRequest = (HttpRequest) msg;
|
||||||
SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpRequest);
|
SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpRequest);
|
||||||
out.add(spdySynStreamFrame);
|
out.add(spdySynStreamFrame);
|
||||||
}
|
}
|
||||||
if (msg instanceof HttpResponseHeader) {
|
if (msg instanceof HttpResponse) {
|
||||||
|
|
||||||
HttpResponseHeader httpResponse = (HttpResponseHeader) msg;
|
HttpResponse httpResponse = (HttpResponse) msg;
|
||||||
if (httpResponse.containsHeader(SpdyHttpHeaders.Names.ASSOCIATED_TO_STREAM_ID)) {
|
if (httpResponse.headers().contains(SpdyHttpHeaders.Names.ASSOCIATED_TO_STREAM_ID)) {
|
||||||
SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpResponse);
|
SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpResponse);
|
||||||
out.add(spdySynStreamFrame);
|
out.add(spdySynStreamFrame);
|
||||||
} else {
|
} else {
|
||||||
|
@ -165,12 +165,12 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
|
||||||
|
|
||||||
HttpContent chunk = (HttpContent) msg;
|
HttpContent chunk = (HttpContent) msg;
|
||||||
SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(currentStreamId);
|
SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(currentStreamId);
|
||||||
spdyDataFrame.setData(chunk.getContent());
|
spdyDataFrame.setData(chunk.data());
|
||||||
spdyDataFrame.setLast(chunk instanceof LastHttpContent);
|
spdyDataFrame.setLast(chunk instanceof LastHttpContent);
|
||||||
|
|
||||||
if (chunk instanceof LastHttpContent) {
|
if (chunk instanceof LastHttpContent) {
|
||||||
LastHttpContent trailer = (LastHttpContent) chunk;
|
LastHttpContent trailer = (LastHttpContent) chunk;
|
||||||
List<Map.Entry<String, String>> trailers = trailer.getHeaders();
|
List<Map.Entry<String, String>> trailers = trailer.trailingHeaders().entries();
|
||||||
if (trailers.isEmpty()) {
|
if (trailers.isEmpty()) {
|
||||||
out.add(spdyDataFrame);
|
out.add(spdyDataFrame);
|
||||||
} else {
|
} else {
|
||||||
|
@ -194,7 +194,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
|
||||||
return out.toArray();
|
return out.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SpdySynStreamFrame createSynStreamFrame(HttpHeader httpMessage)
|
private SpdySynStreamFrame createSynStreamFrame(HttpMessage httpMessage)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
// Get the Stream-ID, Associated-To-Stream-ID, Priority, URL, and scheme from the headers
|
// Get the Stream-ID, Associated-To-Stream-ID, Priority, URL, and scheme from the headers
|
||||||
int streamID = SpdyHttpHeaders.getStreamId(httpMessage);
|
int streamID = SpdyHttpHeaders.getStreamId(httpMessage);
|
||||||
|
@ -210,33 +210,33 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
|
||||||
|
|
||||||
// The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding
|
// The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding
|
||||||
// headers are not valid and MUST not be sent.
|
// headers are not valid and MUST not be sent.
|
||||||
httpMessage.removeHeader(HttpHeaders.Names.CONNECTION);
|
httpMessage.headers().remove(HttpHeaders.Names.CONNECTION);
|
||||||
httpMessage.removeHeader("Keep-Alive");
|
httpMessage.headers().remove("Keep-Alive");
|
||||||
httpMessage.removeHeader("Proxy-Connection");
|
httpMessage.headers().remove("Proxy-Connection");
|
||||||
httpMessage.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
|
httpMessage.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||||
|
|
||||||
SpdySynStreamFrame spdySynStreamFrame =
|
SpdySynStreamFrame spdySynStreamFrame =
|
||||||
new DefaultSpdySynStreamFrame(streamID, associatedToStreamId, priority);
|
new DefaultSpdySynStreamFrame(streamID, associatedToStreamId, priority);
|
||||||
|
|
||||||
// Unfold the first line of the message into name/value pairs
|
// Unfold the first line of the message into name/value pairs
|
||||||
if (httpMessage instanceof HttpRequest) {
|
if (httpMessage instanceof FullHttpRequest) {
|
||||||
HttpRequestHeader httpRequest = (HttpRequestHeader) httpMessage;
|
HttpRequest httpRequest = (HttpRequest) httpMessage;
|
||||||
SpdyHeaders.setMethod(spdyVersion, spdySynStreamFrame, httpRequest.getMethod());
|
SpdyHeaders.setMethod(spdyVersion, spdySynStreamFrame, httpRequest.method());
|
||||||
SpdyHeaders.setUrl(spdyVersion, spdySynStreamFrame, httpRequest.getUri());
|
SpdyHeaders.setUrl(spdyVersion, spdySynStreamFrame, httpRequest.uri());
|
||||||
SpdyHeaders.setVersion(spdyVersion, spdySynStreamFrame, httpMessage.getProtocolVersion());
|
SpdyHeaders.setVersion(spdyVersion, spdySynStreamFrame, httpMessage.protocolVersion());
|
||||||
}
|
}
|
||||||
if (httpMessage instanceof HttpResponseHeader) {
|
if (httpMessage instanceof HttpResponse) {
|
||||||
HttpResponseHeader httpResponse = (HttpResponseHeader) httpMessage;
|
HttpResponse httpResponse = (HttpResponse) httpMessage;
|
||||||
SpdyHeaders.setStatus(spdyVersion, spdySynStreamFrame, httpResponse.getStatus());
|
SpdyHeaders.setStatus(spdyVersion, spdySynStreamFrame, httpResponse.status());
|
||||||
SpdyHeaders.setUrl(spdyVersion, spdySynStreamFrame, URL);
|
SpdyHeaders.setUrl(spdyVersion, spdySynStreamFrame, URL);
|
||||||
SpdyHeaders.setVersion(spdyVersion, spdySynStreamFrame, httpMessage.getProtocolVersion());
|
SpdyHeaders.setVersion(spdyVersion, spdySynStreamFrame, httpMessage.protocolVersion());
|
||||||
spdySynStreamFrame.setUnidirectional(true);
|
spdySynStreamFrame.setUnidirectional(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace the HTTP host header with the SPDY host header
|
// Replace the HTTP host header with the SPDY host header
|
||||||
if (spdyVersion >= 3) {
|
if (spdyVersion >= 3) {
|
||||||
String host = HttpHeaders.getHost(httpMessage);
|
String host = HttpHeaders.getHost(httpMessage);
|
||||||
httpMessage.removeHeader(HttpHeaders.Names.HOST);
|
httpMessage.headers().remove(HttpHeaders.Names.HOST);
|
||||||
SpdyHeaders.setHost(spdySynStreamFrame, host);
|
SpdyHeaders.setHost(spdySynStreamFrame, host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +247,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
|
||||||
SpdyHeaders.setScheme(spdyVersion, spdySynStreamFrame, scheme);
|
SpdyHeaders.setScheme(spdyVersion, spdySynStreamFrame, scheme);
|
||||||
|
|
||||||
// Transfer the remaining HTTP headers
|
// Transfer the remaining HTTP headers
|
||||||
for (Map.Entry<String, String> entry: httpMessage.getHeaders()) {
|
for (Map.Entry<String, String> entry: httpMessage.headers()) {
|
||||||
spdySynStreamFrame.addHeader(entry.getKey(), entry.getValue());
|
spdySynStreamFrame.addHeader(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
currentStreamId = spdySynStreamFrame.getStreamId();
|
currentStreamId = spdySynStreamFrame.getStreamId();
|
||||||
|
@ -255,7 +255,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
|
||||||
return spdySynStreamFrame;
|
return spdySynStreamFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SpdySynReplyFrame createSynReplyFrame(HttpResponseHeader httpResponse)
|
private SpdySynReplyFrame createSynReplyFrame(HttpResponse httpResponse)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
boolean chunked = HttpHeaders.isTransferEncodingChunked(httpResponse);
|
boolean chunked = HttpHeaders.isTransferEncodingChunked(httpResponse);
|
||||||
|
|
||||||
|
@ -265,19 +265,19 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
|
||||||
|
|
||||||
// The Connection, Keep-Alive, Proxy-Connection, and Transfer-ENcoding
|
// The Connection, Keep-Alive, Proxy-Connection, and Transfer-ENcoding
|
||||||
// headers are not valid and MUST not be sent.
|
// headers are not valid and MUST not be sent.
|
||||||
httpResponse.removeHeader(HttpHeaders.Names.CONNECTION);
|
httpResponse.headers().remove(HttpHeaders.Names.CONNECTION);
|
||||||
httpResponse.removeHeader("Keep-Alive");
|
httpResponse.headers().remove("Keep-Alive");
|
||||||
httpResponse.removeHeader("Proxy-Connection");
|
httpResponse.headers().remove("Proxy-Connection");
|
||||||
httpResponse.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
|
httpResponse.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||||
|
|
||||||
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);
|
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);
|
||||||
|
|
||||||
// Unfold the first line of the response into name/value pairs
|
// Unfold the first line of the response into name/value pairs
|
||||||
SpdyHeaders.setStatus(spdyVersion, spdySynReplyFrame, httpResponse.getStatus());
|
SpdyHeaders.setStatus(spdyVersion, spdySynReplyFrame, httpResponse.status());
|
||||||
SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, httpResponse.getProtocolVersion());
|
SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, httpResponse.protocolVersion());
|
||||||
|
|
||||||
// Transfer the remaining HTTP headers
|
// Transfer the remaining HTTP headers
|
||||||
for (Map.Entry<String, String> entry: httpResponse.getHeaders()) {
|
for (Map.Entry<String, String> entry: httpResponse.headers()) {
|
||||||
spdySynReplyFrame.addHeader(entry.getKey(), entry.getValue());
|
spdySynReplyFrame.addHeader(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.spdy;
|
package io.netty.handler.codec.spdy;
|
||||||
|
|
||||||
import io.netty.handler.codec.http.HttpHeader;
|
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
|
import io.netty.handler.codec.http.HttpMessage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the constants for the header names and the utility methods
|
* Provides the constants for the header names and the utility methods
|
||||||
|
@ -60,29 +60,29 @@ public final class SpdyHttpHeaders {
|
||||||
/**
|
/**
|
||||||
* Removes the {@code "X-SPDY-Stream-ID"} header.
|
* Removes the {@code "X-SPDY-Stream-ID"} header.
|
||||||
*/
|
*/
|
||||||
public static void removeStreamId(HttpHeader message) {
|
public static void removeStreamId(HttpMessage message) {
|
||||||
message.removeHeader(Names.STREAM_ID);
|
message.headers().remove(Names.STREAM_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value of the {@code "X-SPDY-Stream-ID"} header.
|
* Returns the value of the {@code "X-SPDY-Stream-ID"} header.
|
||||||
*/
|
*/
|
||||||
public static int getStreamId(HttpHeader message) {
|
public static int getStreamId(HttpMessage message) {
|
||||||
return HttpHeaders.getIntHeader(message, Names.STREAM_ID);
|
return HttpHeaders.getIntHeader(message, Names.STREAM_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@code "X-SPDY-Stream-ID"} header.
|
* Sets the {@code "X-SPDY-Stream-ID"} header.
|
||||||
*/
|
*/
|
||||||
public static void setStreamId(HttpHeader message, int streamId) {
|
public static void setStreamId(HttpMessage message, int streamId) {
|
||||||
HttpHeaders.setIntHeader(message, Names.STREAM_ID, streamId);
|
HttpHeaders.setIntHeader(message, Names.STREAM_ID, streamId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the {@code "X-SPDY-Associated-To-Stream-ID"} header.
|
* Removes the {@code "X-SPDY-Associated-To-Stream-ID"} header.
|
||||||
*/
|
*/
|
||||||
public static void removeAssociatedToStreamId(HttpHeader message) {
|
public static void removeAssociatedToStreamId(HttpMessage message) {
|
||||||
message.removeHeader(Names.ASSOCIATED_TO_STREAM_ID);
|
message.headers().remove(Names.ASSOCIATED_TO_STREAM_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,22 +91,22 @@ public final class SpdyHttpHeaders {
|
||||||
* @return the header value or {@code 0} if there is no such header or
|
* @return the header value or {@code 0} if there is no such header or
|
||||||
* if the header value is not a number
|
* if the header value is not a number
|
||||||
*/
|
*/
|
||||||
public static int getAssociatedToStreamId(HttpHeader message) {
|
public static int getAssociatedToStreamId(HttpMessage message) {
|
||||||
return HttpHeaders.getIntHeader(message, Names.ASSOCIATED_TO_STREAM_ID, 0);
|
return HttpHeaders.getIntHeader(message, Names.ASSOCIATED_TO_STREAM_ID, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@code "X-SPDY-Associated-To-Stream-ID"} header.
|
* Sets the {@code "X-SPDY-Associated-To-Stream-ID"} header.
|
||||||
*/
|
*/
|
||||||
public static void setAssociatedToStreamId(HttpHeader message, int associatedToStreamId) {
|
public static void setAssociatedToStreamId(HttpMessage message, int associatedToStreamId) {
|
||||||
HttpHeaders.setIntHeader(message, Names.ASSOCIATED_TO_STREAM_ID, associatedToStreamId);
|
HttpHeaders.setIntHeader(message, Names.ASSOCIATED_TO_STREAM_ID, associatedToStreamId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the {@code "X-SPDY-Priority"} header.
|
* Removes the {@code "X-SPDY-Priority"} header.
|
||||||
*/
|
*/
|
||||||
public static void removePriority(HttpHeader message) {
|
public static void removePriority(HttpMessage message) {
|
||||||
message.removeHeader(Names.PRIORITY);
|
message.headers().remove(Names.PRIORITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -115,56 +115,56 @@ public final class SpdyHttpHeaders {
|
||||||
* @return the header value or {@code 0} if there is no such header or
|
* @return the header value or {@code 0} if there is no such header or
|
||||||
* if the header value is not a number
|
* if the header value is not a number
|
||||||
*/
|
*/
|
||||||
public static byte getPriority(HttpHeader message) {
|
public static byte getPriority(HttpMessage message) {
|
||||||
return (byte) HttpHeaders.getIntHeader(message, Names.PRIORITY, 0);
|
return (byte) HttpHeaders.getIntHeader(message, Names.PRIORITY, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@code "X-SPDY-Priority"} header.
|
* Sets the {@code "X-SPDY-Priority"} header.
|
||||||
*/
|
*/
|
||||||
public static void setPriority(HttpHeader message, byte priority) {
|
public static void setPriority(HttpMessage message, byte priority) {
|
||||||
HttpHeaders.setIntHeader(message, Names.PRIORITY, priority);
|
HttpHeaders.setIntHeader(message, Names.PRIORITY, priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the {@code "X-SPDY-URL"} header.
|
* Removes the {@code "X-SPDY-URL"} header.
|
||||||
*/
|
*/
|
||||||
public static void removeUrl(HttpHeader message) {
|
public static void removeUrl(HttpMessage message) {
|
||||||
message.removeHeader(Names.URL);
|
message.headers().remove(Names.URL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value of the {@code "X-SPDY-URL"} header.
|
* Returns the value of the {@code "X-SPDY-URL"} header.
|
||||||
*/
|
*/
|
||||||
public static String getUrl(HttpHeader message) {
|
public static String getUrl(HttpMessage message) {
|
||||||
return message.getHeader(Names.URL);
|
return message.headers().get(Names.URL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@code "X-SPDY-URL"} header.
|
* Sets the {@code "X-SPDY-URL"} header.
|
||||||
*/
|
*/
|
||||||
public static void setUrl(HttpHeader message, String url) {
|
public static void setUrl(HttpMessage message, String url) {
|
||||||
message.setHeader(Names.URL, url);
|
message.headers().set(Names.URL, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the {@code "X-SPDY-Scheme"} header.
|
* Removes the {@code "X-SPDY-Scheme"} header.
|
||||||
*/
|
*/
|
||||||
public static void removeScheme(HttpHeader message) {
|
public static void removeScheme(HttpMessage message) {
|
||||||
message.removeHeader(Names.SCHEME);
|
message.headers().remove(Names.SCHEME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value of the {@code "X-SPDY-Scheme"} header.
|
* Returns the value of the {@code "X-SPDY-Scheme"} header.
|
||||||
*/
|
*/
|
||||||
public static String getScheme(HttpHeader message) {
|
public static String getScheme(HttpMessage message) {
|
||||||
return message.getHeader(Names.SCHEME);
|
return message.headers().get(Names.SCHEME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@code "X-SPDY-Scheme"} header.
|
* Sets the {@code "X-SPDY-Scheme"} header.
|
||||||
*/
|
*/
|
||||||
public static void setScheme(HttpHeader message, String scheme) {
|
public static void setScheme(HttpMessage message, String scheme) {
|
||||||
message.setHeader(Names.SCHEME, scheme);
|
message.headers().set(Names.SCHEME, scheme);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,31 +15,31 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.spdy;
|
package io.netty.handler.codec.spdy;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.MessageToMessageCodec;
|
||||||
|
import io.netty.handler.codec.http.HttpMessage;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.handler.codec.MessageToMessageCodec;
|
|
||||||
import io.netty.handler.codec.http.HttpHeader;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link MessageToMessageCodec} that takes care of adding the right {@link SpdyHttpHeaders.Names#STREAM_ID} to the
|
* {@link MessageToMessageCodec} that takes care of adding the right {@link SpdyHttpHeaders.Names#STREAM_ID} to the
|
||||||
* {@link HttpHeader} if one is not present. This makes it possible to just re-use plan handlers current used
|
* {@link HttpMessage} if one is not present. This makes it possible to just re-use plan handlers current used
|
||||||
* for HTTP.
|
* for HTTP.
|
||||||
*/
|
*/
|
||||||
public class SpdyHttpResponseStreamIdHandler extends
|
public class SpdyHttpResponseStreamIdHandler extends
|
||||||
MessageToMessageCodec<Object, HttpHeader> {
|
MessageToMessageCodec<Object, HttpMessage> {
|
||||||
private static final Integer NO_ID = -1;
|
private static final Integer NO_ID = -1;
|
||||||
private final Queue<Integer> ids = new LinkedList<Integer>();
|
private final Queue<Integer> ids = new LinkedList<Integer>();
|
||||||
|
|
||||||
public SpdyHttpResponseStreamIdHandler() {
|
public SpdyHttpResponseStreamIdHandler() {
|
||||||
super(new Class<?>[] { HttpHeader.class, SpdyRstStreamFrame.class }, new Class<?>[] { HttpHeader.class });
|
super(new Class<?>[] { HttpMessage.class, SpdyRstStreamFrame.class }, new Class<?>[] { HttpMessage.class });
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object encode(ChannelHandlerContext ctx, HttpHeader msg) throws Exception {
|
protected Object encode(ChannelHandlerContext ctx, HttpMessage msg) throws Exception {
|
||||||
Integer id = ids.poll();
|
Integer id = ids.poll();
|
||||||
if (id != null && id.intValue() != NO_ID && !msg.containsHeader(SpdyHttpHeaders.Names.STREAM_ID)) {
|
if (id != null && id.intValue() != NO_ID && !msg.headers().contains(SpdyHttpHeaders.Names.STREAM_ID)) {
|
||||||
SpdyHttpHeaders.setStreamId(msg, id);
|
SpdyHttpHeaders.setStreamId(msg, id);
|
||||||
}
|
}
|
||||||
return msg;
|
return msg;
|
||||||
|
@ -47,12 +47,12 @@ public class SpdyHttpResponseStreamIdHandler extends
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object decode(ChannelHandlerContext ctx, Object msg) throws Exception {
|
protected Object decode(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
if (msg instanceof HttpHeader) {
|
if (msg instanceof HttpMessage) {
|
||||||
boolean contains = ((HttpHeader) msg).containsHeader(SpdyHttpHeaders.Names.STREAM_ID);
|
boolean contains = ((HttpMessage) msg).headers().contains(SpdyHttpHeaders.Names.STREAM_ID);
|
||||||
if (!contains) {
|
if (!contains) {
|
||||||
ids.add(NO_ID);
|
ids.add(NO_ID);
|
||||||
} else {
|
} else {
|
||||||
ids.add(SpdyHttpHeaders.getStreamId((HttpHeader) msg));
|
ids.add(SpdyHttpHeaders.getStreamId((HttpMessage) msg));
|
||||||
}
|
}
|
||||||
} else if (msg instanceof SpdyRstStreamFrame) {
|
} else if (msg instanceof SpdyRstStreamFrame) {
|
||||||
ids.remove(((SpdyRstStreamFrame) msg).getStreamId());
|
ids.remove(((SpdyRstStreamFrame) msg).getStreamId());
|
||||||
|
|
|
@ -22,25 +22,25 @@ public class DefaultHttpRequestTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHeaderRemoval() {
|
public void testHeaderRemoval() {
|
||||||
HttpHeader m = new DefaultHttpRequestHeader(
|
HttpMessage m = new DefaultHttpRequest(
|
||||||
HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
|
HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
|
||||||
|
|
||||||
// Insert sample keys.
|
// Insert sample keys.
|
||||||
for (int i = 0; i < 1000; i ++) {
|
for (int i = 0; i < 1000; i ++) {
|
||||||
m.setHeader(String.valueOf(i), "");
|
m.headers().set(String.valueOf(i), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove in reversed order.
|
// Remove in reversed order.
|
||||||
for (int i = 999; i >= 0; i --) {
|
for (int i = 999; i >= 0; i --) {
|
||||||
m.removeHeader(String.valueOf(i));
|
m.headers().remove(String.valueOf(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if random access returns nothing.
|
// Check if random access returns nothing.
|
||||||
for (int i = 0; i < 1000; i ++) {
|
for (int i = 0; i < 1000; i ++) {
|
||||||
Assert.assertNull(m.getHeader(String.valueOf(i)));
|
Assert.assertNull(m.headers().get(String.valueOf(i)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if sequential access returns nothing.
|
// Check if sequential access returns nothing.
|
||||||
Assert.assertTrue(m.getHeaders().isEmpty());
|
Assert.assertTrue(m.headers().isEmpty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.CompositeByteBuf;
|
import io.netty.buffer.CompositeByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
|
@ -23,20 +22,21 @@ import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.embedded.EmbeddedMessageChannel;
|
import io.netty.channel.embedded.EmbeddedMessageChannel;
|
||||||
import io.netty.handler.codec.TooLongFrameException;
|
import io.netty.handler.codec.TooLongFrameException;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.easymock.EasyMock;
|
import org.easymock.EasyMock;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class HttpObjectAggregatorTest {
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class FullHttpMessageDecoderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAggregate() {
|
public void testAggregate() {
|
||||||
HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024);
|
HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024);
|
||||||
EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr);
|
EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr);
|
||||||
|
|
||||||
HttpRequestHeader message = new DefaultHttpRequestHeader(HttpVersion.HTTP_1_1,
|
HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
|
||||||
HttpMethod.GET, "http://localhost");
|
HttpMethod.GET, "http://localhost");
|
||||||
HttpHeaders.setHeader(message, "X-Test", true);
|
HttpHeaders.setHeader(message, "X-Test", true);
|
||||||
HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII));
|
HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII));
|
||||||
|
@ -49,18 +49,18 @@ public class HttpObjectAggregatorTest {
|
||||||
// this should trigger a messageReceived event so return true
|
// this should trigger a messageReceived event so return true
|
||||||
assertTrue(embedder.writeInbound(chunk3));
|
assertTrue(embedder.writeInbound(chunk3));
|
||||||
assertTrue(embedder.finish());
|
assertTrue(embedder.finish());
|
||||||
DefaultHttpRequest aggratedMessage = (DefaultHttpRequest) embedder.readInbound();
|
DefaultFullHttpRequest aggratedMessage = (DefaultFullHttpRequest) embedder.readInbound();
|
||||||
assertNotNull(aggratedMessage);
|
assertNotNull(aggratedMessage);
|
||||||
|
|
||||||
assertEquals(chunk1.getContent().readableBytes() + chunk2.getContent().readableBytes(), HttpHeaders.getContentLength(aggratedMessage));
|
assertEquals(chunk1.data().readableBytes() + chunk2.data().readableBytes(), HttpHeaders.getContentLength(aggratedMessage));
|
||||||
assertEquals(aggratedMessage.getHeader("X-Test"), Boolean.TRUE.toString());
|
assertEquals(aggratedMessage.headers().get("X-Test"), Boolean.TRUE.toString());
|
||||||
checkContentBuffer(aggratedMessage);
|
checkContentBuffer(aggratedMessage);
|
||||||
assertNull(embedder.readInbound());
|
assertNull(embedder.readInbound());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkContentBuffer(DefaultHttpRequest aggregatedMessage) {
|
private static void checkContentBuffer(DefaultFullHttpRequest aggregatedMessage) {
|
||||||
CompositeByteBuf buffer = (CompositeByteBuf) aggregatedMessage.getContent();
|
CompositeByteBuf buffer = (CompositeByteBuf) aggregatedMessage.data();
|
||||||
assertEquals(2, buffer.numComponents());
|
assertEquals(2, buffer.numComponents());
|
||||||
List<ByteBuf> buffers = buffer.decompose(0, buffer.capacity());
|
List<ByteBuf> buffers = buffer.decompose(0, buffer.capacity());
|
||||||
assertEquals(2, buffers.size());
|
assertEquals(2, buffers.size());
|
||||||
|
@ -74,14 +74,14 @@ public class HttpObjectAggregatorTest {
|
||||||
public void testAggregateWithTrailer() {
|
public void testAggregateWithTrailer() {
|
||||||
HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024);
|
HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024);
|
||||||
EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr);
|
EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr);
|
||||||
HttpRequestHeader message = new DefaultHttpRequestHeader(HttpVersion.HTTP_1_1,
|
HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
|
||||||
HttpMethod.GET, "http://localhost");
|
HttpMethod.GET, "http://localhost");
|
||||||
HttpHeaders.setHeader(message, "X-Test", true);
|
HttpHeaders.setHeader(message, "X-Test", true);
|
||||||
HttpHeaders.setTransferEncodingChunked(message);
|
HttpHeaders.setTransferEncodingChunked(message);
|
||||||
HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII));
|
HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII));
|
||||||
HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII));
|
HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII));
|
||||||
LastHttpContent trailer = new DefaultLastHttpContent();
|
LastHttpContent trailer = new DefaultLastHttpContent();
|
||||||
trailer.setHeader("X-Trailer", true);
|
trailer.trailingHeaders().set("X-Trailer", true);
|
||||||
|
|
||||||
assertFalse(embedder.writeInbound(message));
|
assertFalse(embedder.writeInbound(message));
|
||||||
assertFalse(embedder.writeInbound(chunk1));
|
assertFalse(embedder.writeInbound(chunk1));
|
||||||
|
@ -90,12 +90,12 @@ public class HttpObjectAggregatorTest {
|
||||||
// this should trigger a messageReceived event so return true
|
// this should trigger a messageReceived event so return true
|
||||||
assertTrue(embedder.writeInbound(trailer));
|
assertTrue(embedder.writeInbound(trailer));
|
||||||
assertTrue(embedder.finish());
|
assertTrue(embedder.finish());
|
||||||
DefaultHttpRequest aggratedMessage = (DefaultHttpRequest) embedder.readInbound();
|
DefaultFullHttpRequest aggratedMessage = (DefaultFullHttpRequest) embedder.readInbound();
|
||||||
assertNotNull(aggratedMessage);
|
assertNotNull(aggratedMessage);
|
||||||
|
|
||||||
assertEquals(chunk1.getContent().readableBytes() + chunk2.getContent().readableBytes(), HttpHeaders.getContentLength(aggratedMessage));
|
assertEquals(chunk1.data().readableBytes() + chunk2.data().readableBytes(), HttpHeaders.getContentLength(aggratedMessage));
|
||||||
assertEquals(aggratedMessage.getHeader("X-Test"), Boolean.TRUE.toString());
|
assertEquals(aggratedMessage.headers().get("X-Test"), Boolean.TRUE.toString());
|
||||||
assertEquals(aggratedMessage.getHeader("X-Trailer"), Boolean.TRUE.toString());
|
assertEquals(aggratedMessage.headers().get("X-Trailer"), Boolean.TRUE.toString());
|
||||||
checkContentBuffer(aggratedMessage);
|
checkContentBuffer(aggratedMessage);
|
||||||
|
|
||||||
assertNull(embedder.readInbound());
|
assertNull(embedder.readInbound());
|
||||||
|
@ -107,7 +107,7 @@ public class HttpObjectAggregatorTest {
|
||||||
public void testTooLongFrameException() {
|
public void testTooLongFrameException() {
|
||||||
HttpObjectAggregator aggr = new HttpObjectAggregator(4);
|
HttpObjectAggregator aggr = new HttpObjectAggregator(4);
|
||||||
EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr);
|
EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr);
|
||||||
HttpRequestHeader message = new DefaultHttpRequestHeader(HttpVersion.HTTP_1_1,
|
HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
|
||||||
HttpMethod.GET, "http://localhost");
|
HttpMethod.GET, "http://localhost");
|
||||||
HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII));
|
HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII));
|
||||||
HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII));
|
HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII));
|
|
@ -15,15 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.embedded.EmbeddedByteChannel;
|
import io.netty.channel.embedded.EmbeddedByteChannel;
|
||||||
import io.netty.handler.codec.CodecException;
|
import io.netty.handler.codec.CodecException;
|
||||||
import io.netty.handler.codec.PrematureChannelClosureException;
|
import io.netty.handler.codec.PrematureChannelClosureException;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class HttpClientCodecTest {
|
public class HttpClientCodecTest {
|
||||||
|
|
||||||
private static final String RESPONSE = "HTTP/1.0 200 OK\r\n" + "Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 28\r\n" + "\r\n"
|
private static final String RESPONSE = "HTTP/1.0 200 OK\r\n" + "Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 28\r\n" + "\r\n"
|
||||||
|
@ -38,7 +38,7 @@ public class HttpClientCodecTest {
|
||||||
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true);
|
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true);
|
||||||
EmbeddedByteChannel ch = new EmbeddedByteChannel(codec);
|
EmbeddedByteChannel ch = new EmbeddedByteChannel(codec);
|
||||||
|
|
||||||
ch.writeOutbound(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/"));
|
ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/"));
|
||||||
ch.writeInbound(Unpooled.copiedBuffer(RESPONSE, CharsetUtil.ISO_8859_1));
|
ch.writeInbound(Unpooled.copiedBuffer(RESPONSE, CharsetUtil.ISO_8859_1));
|
||||||
ch.finish();
|
ch.finish();
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ public class HttpClientCodecTest {
|
||||||
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true);
|
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true);
|
||||||
EmbeddedByteChannel ch = new EmbeddedByteChannel(codec);
|
EmbeddedByteChannel ch = new EmbeddedByteChannel(codec);
|
||||||
|
|
||||||
ch.writeOutbound(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/"));
|
ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/"));
|
||||||
ch.writeInbound(Unpooled.copiedBuffer(CHUNKED_RESPONSE, CharsetUtil.ISO_8859_1));
|
ch.writeInbound(Unpooled.copiedBuffer(CHUNKED_RESPONSE, CharsetUtil.ISO_8859_1));
|
||||||
ch.finish();
|
ch.finish();
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ public class HttpClientCodecTest {
|
||||||
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true);
|
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true);
|
||||||
EmbeddedByteChannel ch = new EmbeddedByteChannel(codec);
|
EmbeddedByteChannel ch = new EmbeddedByteChannel(codec);
|
||||||
|
|
||||||
assertTrue(ch.writeOutbound(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/")));
|
assertTrue(ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/")));
|
||||||
assertNotNull(ch.readOutbound());
|
assertNotNull(ch.readOutbound());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -75,7 +75,7 @@ public class HttpClientCodecTest {
|
||||||
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true);
|
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true);
|
||||||
EmbeddedByteChannel ch = new EmbeddedByteChannel(codec);
|
EmbeddedByteChannel ch = new EmbeddedByteChannel(codec);
|
||||||
|
|
||||||
ch.writeOutbound(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/"));
|
ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/"));
|
||||||
ch.writeInbound(Unpooled.copiedBuffer(INCOMPLETE_CHUNKED_RESPONSE, CharsetUtil.ISO_8859_1));
|
ch.writeInbound(Unpooled.copiedBuffer(INCOMPLETE_CHUNKED_RESPONSE, CharsetUtil.ISO_8859_1));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -33,8 +33,8 @@ public class HttpInvalidMessageTest {
|
||||||
public void testRequestWithBadInitialLine() throws Exception {
|
public void testRequestWithBadInitialLine() throws Exception {
|
||||||
EmbeddedByteChannel ch = new EmbeddedByteChannel(new HttpRequestDecoder());
|
EmbeddedByteChannel ch = new EmbeddedByteChannel(new HttpRequestDecoder());
|
||||||
ch.writeInbound(Unpooled.copiedBuffer("GET / HTTP/1.0 with extra\r\n", CharsetUtil.UTF_8));
|
ch.writeInbound(Unpooled.copiedBuffer("GET / HTTP/1.0 with extra\r\n", CharsetUtil.UTF_8));
|
||||||
HttpRequestHeader req = (HttpRequestHeader) ch.readInbound();
|
HttpRequest req = (HttpRequest) ch.readInbound();
|
||||||
DecoderResult dr = req.getDecoderResult();
|
DecoderResult dr = req.decoderResult();
|
||||||
Assert.assertFalse(dr.isSuccess());
|
Assert.assertFalse(dr.isSuccess());
|
||||||
Assert.assertFalse(dr.isPartialFailure());
|
Assert.assertFalse(dr.isPartialFailure());
|
||||||
ensureInboundTrafficDiscarded(ch);
|
ensureInboundTrafficDiscarded(ch);
|
||||||
|
@ -47,12 +47,12 @@ public class HttpInvalidMessageTest {
|
||||||
ch.writeInbound(Unpooled.copiedBuffer("Good_Name: Good Value\r\n", CharsetUtil.UTF_8));
|
ch.writeInbound(Unpooled.copiedBuffer("Good_Name: Good Value\r\n", CharsetUtil.UTF_8));
|
||||||
ch.writeInbound(Unpooled.copiedBuffer("Bad=Name: Bad Value\r\n", CharsetUtil.UTF_8));
|
ch.writeInbound(Unpooled.copiedBuffer("Bad=Name: Bad Value\r\n", CharsetUtil.UTF_8));
|
||||||
ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.UTF_8));
|
ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.UTF_8));
|
||||||
HttpRequestHeader req = (HttpRequestHeader) ch.readInbound();
|
HttpRequest req = (HttpRequest) ch.readInbound();
|
||||||
DecoderResult dr = req.getDecoderResult();
|
DecoderResult dr = req.decoderResult();
|
||||||
Assert.assertFalse(dr.isSuccess());
|
Assert.assertFalse(dr.isSuccess());
|
||||||
Assert.assertTrue(dr.isPartialFailure());
|
Assert.assertTrue(dr.isPartialFailure());
|
||||||
Assert.assertEquals("Good Value", req.getHeader("Good_Name"));
|
Assert.assertEquals("Good Value", req.headers().get("Good_Name"));
|
||||||
Assert.assertEquals("/maybe-something", req.getUri());
|
Assert.assertEquals("/maybe-something", req.uri());
|
||||||
ensureInboundTrafficDiscarded(ch);
|
ensureInboundTrafficDiscarded(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,8 +60,8 @@ public class HttpInvalidMessageTest {
|
||||||
public void testResponseWithBadInitialLine() throws Exception {
|
public void testResponseWithBadInitialLine() throws Exception {
|
||||||
EmbeddedByteChannel ch = new EmbeddedByteChannel(new HttpResponseDecoder());
|
EmbeddedByteChannel ch = new EmbeddedByteChannel(new HttpResponseDecoder());
|
||||||
ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.0 BAD_CODE Bad Server\r\n", CharsetUtil.UTF_8));
|
ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.0 BAD_CODE Bad Server\r\n", CharsetUtil.UTF_8));
|
||||||
HttpResponseHeader res = (HttpResponseHeader) ch.readInbound();
|
HttpResponse res = (HttpResponse) ch.readInbound();
|
||||||
DecoderResult dr = res.getDecoderResult();
|
DecoderResult dr = res.decoderResult();
|
||||||
Assert.assertFalse(dr.isSuccess());
|
Assert.assertFalse(dr.isSuccess());
|
||||||
Assert.assertFalse(dr.isPartialFailure());
|
Assert.assertFalse(dr.isPartialFailure());
|
||||||
ensureInboundTrafficDiscarded(ch);
|
ensureInboundTrafficDiscarded(ch);
|
||||||
|
@ -74,12 +74,12 @@ public class HttpInvalidMessageTest {
|
||||||
ch.writeInbound(Unpooled.copiedBuffer("Good_Name: Good Value\r\n", CharsetUtil.UTF_8));
|
ch.writeInbound(Unpooled.copiedBuffer("Good_Name: Good Value\r\n", CharsetUtil.UTF_8));
|
||||||
ch.writeInbound(Unpooled.copiedBuffer("Bad=Name: Bad Value\r\n", CharsetUtil.UTF_8));
|
ch.writeInbound(Unpooled.copiedBuffer("Bad=Name: Bad Value\r\n", CharsetUtil.UTF_8));
|
||||||
ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.UTF_8));
|
ch.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.UTF_8));
|
||||||
HttpResponseHeader res = (HttpResponseHeader) ch.readInbound();
|
HttpResponse res = (HttpResponse) ch.readInbound();
|
||||||
DecoderResult dr = res.getDecoderResult();
|
DecoderResult dr = res.decoderResult();
|
||||||
Assert.assertFalse(dr.isSuccess());
|
Assert.assertFalse(dr.isSuccess());
|
||||||
Assert.assertTrue(dr.isPartialFailure());
|
Assert.assertTrue(dr.isPartialFailure());
|
||||||
Assert.assertEquals("Maybe OK", res.getStatus().getReasonPhrase());
|
Assert.assertEquals("Maybe OK", res.status().reasonPhrase());
|
||||||
Assert.assertEquals("Good Value", res.getHeader("Good_Name"));
|
Assert.assertEquals("Good Value", res.headers().get("Good_Name"));
|
||||||
ensureInboundTrafficDiscarded(ch);
|
ensureInboundTrafficDiscarded(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,11 +90,11 @@ public class HttpInvalidMessageTest {
|
||||||
ch.writeInbound(Unpooled.copiedBuffer("Transfer-Encoding: chunked\r\n\r\n", CharsetUtil.UTF_8));
|
ch.writeInbound(Unpooled.copiedBuffer("Transfer-Encoding: chunked\r\n\r\n", CharsetUtil.UTF_8));
|
||||||
ch.writeInbound(Unpooled.copiedBuffer("BAD_LENGTH\r\n", CharsetUtil.UTF_8));
|
ch.writeInbound(Unpooled.copiedBuffer("BAD_LENGTH\r\n", CharsetUtil.UTF_8));
|
||||||
|
|
||||||
HttpRequestHeader req = (HttpRequestHeader) ch.readInbound();
|
HttpRequest req = (HttpRequest) ch.readInbound();
|
||||||
Assert.assertTrue(req.getDecoderResult().isSuccess());
|
Assert.assertTrue(req.decoderResult().isSuccess());
|
||||||
|
|
||||||
HttpContent chunk = (HttpContent) ch.readInbound();
|
HttpContent chunk = (HttpContent) ch.readInbound();
|
||||||
DecoderResult dr = chunk.getDecoderResult();
|
DecoderResult dr = chunk.decoderResult();
|
||||||
Assert.assertFalse(dr.isSuccess());
|
Assert.assertFalse(dr.isSuccess());
|
||||||
Assert.assertFalse(dr.isPartialFailure());
|
Assert.assertFalse(dr.isPartialFailure());
|
||||||
ensureInboundTrafficDiscarded(ch);
|
ensureInboundTrafficDiscarded(ch);
|
||||||
|
|
|
@ -15,14 +15,14 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
public class HttpRequestEncoderTest {
|
public class HttpRequestEncoderTest {
|
||||||
|
@ -31,7 +31,7 @@ public class HttpRequestEncoderTest {
|
||||||
public void testUriWithoutPath() throws Exception {
|
public void testUriWithoutPath() throws Exception {
|
||||||
HttpRequestEncoder encoder = new HttpRequestEncoder();
|
HttpRequestEncoder encoder = new HttpRequestEncoder();
|
||||||
ByteBuf buffer = Unpooled.buffer(64);
|
ByteBuf buffer = Unpooled.buffer(64);
|
||||||
encoder.encodeInitialLine(buffer, new DefaultHttpRequestHeader(HttpVersion.HTTP_1_1,
|
encoder.encodeInitialLine(buffer, new DefaultHttpRequest(HttpVersion.HTTP_1_1,
|
||||||
HttpMethod.GET, "http://localhost"));
|
HttpMethod.GET, "http://localhost"));
|
||||||
String req = buffer.toString(Charset.forName("US-ASCII"));
|
String req = buffer.toString(Charset.forName("US-ASCII"));
|
||||||
assertEquals("GET http://localhost/ HTTP/1.1\r\n", req);
|
assertEquals("GET http://localhost/ HTTP/1.1\r\n", req);
|
||||||
|
@ -42,7 +42,7 @@ public class HttpRequestEncoderTest {
|
||||||
public void testUriWithPath() throws Exception {
|
public void testUriWithPath() throws Exception {
|
||||||
HttpRequestEncoder encoder = new HttpRequestEncoder();
|
HttpRequestEncoder encoder = new HttpRequestEncoder();
|
||||||
ByteBuf buffer = Unpooled.buffer(64);
|
ByteBuf buffer = Unpooled.buffer(64);
|
||||||
encoder.encodeInitialLine(buffer, new DefaultHttpRequestHeader(HttpVersion.HTTP_1_1,
|
encoder.encodeInitialLine(buffer, new DefaultHttpRequest(HttpVersion.HTTP_1_1,
|
||||||
HttpMethod.GET, "http://localhost/"));
|
HttpMethod.GET, "http://localhost/"));
|
||||||
String req = buffer.toString(Charset.forName("US-ASCII"));
|
String req = buffer.toString(Charset.forName("US-ASCII"));
|
||||||
assertEquals("GET http://localhost/ HTTP/1.1\r\n", req);
|
assertEquals("GET http://localhost/ HTTP/1.1\r\n", req);
|
||||||
|
|
|
@ -19,7 +19,6 @@ import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.embedded.EmbeddedByteChannel;
|
import io.netty.channel.embedded.EmbeddedByteChannel;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -44,8 +43,8 @@ public class HttpServerCodecTest {
|
||||||
decoderEmbedder.writeInbound(prepareDataChunk(offeredContentLength));
|
decoderEmbedder.writeInbound(prepareDataChunk(offeredContentLength));
|
||||||
decoderEmbedder.finish();
|
decoderEmbedder.finish();
|
||||||
|
|
||||||
HttpHeader httpMessage = (HttpHeader) decoderEmbedder.readInbound();
|
HttpMessage httpMessage = (HttpMessage) decoderEmbedder.readInbound();
|
||||||
//Assert.assertSame(HttpTransferEncoding.STREAMED, httpMessage.getTransferEncoding());
|
Assert.assertNotNull(httpMessage);
|
||||||
|
|
||||||
boolean empty = true;
|
boolean empty = true;
|
||||||
int totalBytesPolled = 0;
|
int totalBytesPolled = 0;
|
||||||
|
@ -55,7 +54,7 @@ public class HttpServerCodecTest {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
empty = false;
|
empty = false;
|
||||||
totalBytesPolled += httpChunk.getContent().readableBytes();
|
totalBytesPolled += httpChunk.data().readableBytes();
|
||||||
Assert.assertFalse(httpChunk instanceof LastHttpContent);
|
Assert.assertFalse(httpChunk instanceof LastHttpContent);
|
||||||
}
|
}
|
||||||
Assert.assertFalse(empty);
|
Assert.assertFalse(empty);
|
||||||
|
|
|
@ -15,15 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.websocketx;
|
package io.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http.HttpHeaders.Values.WEBSOCKET;
|
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||||
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
import io.netty.handler.codec.http.DefaultHttpRequest;
|
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpRequestHeader;
|
|
||||||
import io.netty.handler.codec.http.HttpVersion;
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
|
||||||
|
import static io.netty.handler.codec.http.HttpHeaders.Values.*;
|
||||||
|
import static io.netty.handler.codec.http.HttpVersion.*;
|
||||||
|
|
||||||
public class WebSocketRequestBuilder {
|
public class WebSocketRequestBuilder {
|
||||||
|
|
||||||
|
@ -96,30 +96,30 @@ public class WebSocketRequestBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpRequest build() {
|
public FullHttpRequest build() {
|
||||||
HttpRequest req = new DefaultHttpRequest(httpVersion, method, uri);
|
FullHttpRequest req = new DefaultFullHttpRequest(httpVersion, method, uri);
|
||||||
if (host != null) {
|
if (host != null) {
|
||||||
req.setHeader(Names.HOST, host);
|
req.headers().set(Names.HOST, host);
|
||||||
}
|
}
|
||||||
if (upgrade != null) {
|
if (upgrade != null) {
|
||||||
req.setHeader(Names.UPGRADE, upgrade);
|
req.headers().set(Names.UPGRADE, upgrade);
|
||||||
}
|
}
|
||||||
if (connection != null) {
|
if (connection != null) {
|
||||||
req.setHeader(Names.CONNECTION, connection);
|
req.headers().set(Names.CONNECTION, connection);
|
||||||
}
|
}
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
req.setHeader(Names.SEC_WEBSOCKET_KEY, key);
|
req.headers().set(Names.SEC_WEBSOCKET_KEY, key);
|
||||||
}
|
}
|
||||||
if (origin != null) {
|
if (origin != null) {
|
||||||
req.setHeader(Names.SEC_WEBSOCKET_ORIGIN, origin);
|
req.headers().set(Names.SEC_WEBSOCKET_ORIGIN, origin);
|
||||||
}
|
}
|
||||||
if (version != null) {
|
if (version != null) {
|
||||||
req.setHeader(Names.SEC_WEBSOCKET_VERSION, version.toHttpHeaderValue());
|
req.headers().set(Names.SEC_WEBSOCKET_VERSION, version.toHttpHeaderValue());
|
||||||
}
|
}
|
||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpRequestHeader sucessful() {
|
public static HttpRequest sucessful() {
|
||||||
return new WebSocketRequestBuilder().httpVersion(HTTP_1_1)
|
return new WebSocketRequestBuilder().httpVersion(HTTP_1_1)
|
||||||
.method(HttpMethod.GET)
|
.method(HttpMethod.GET)
|
||||||
.uri("/test")
|
.uri("/test")
|
||||||
|
|
|
@ -15,26 +15,26 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.websocketx;
|
package io.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http.HttpHeaders.Values.*;
|
|
||||||
import static io.netty.handler.codec.http.HttpVersion.*;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.embedded.EmbeddedByteChannel;
|
import io.netty.channel.embedded.EmbeddedByteChannel;
|
||||||
import io.netty.handler.codec.http.DefaultHttpRequest;
|
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||||
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpResponseDecoder;
|
import io.netty.handler.codec.http.HttpResponseDecoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseHeader;
|
|
||||||
import io.netty.handler.codec.http.LastHttpContent;
|
import io.netty.handler.codec.http.LastHttpContent;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static io.netty.handler.codec.http.HttpHeaders.Values.*;
|
||||||
|
import static io.netty.handler.codec.http.HttpVersion.*;
|
||||||
|
|
||||||
public class WebSocketServerHandshaker00Test {
|
public class WebSocketServerHandshaker00Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -42,17 +42,16 @@ public class WebSocketServerHandshaker00Test {
|
||||||
EmbeddedByteChannel ch = new EmbeddedByteChannel(
|
EmbeddedByteChannel ch = new EmbeddedByteChannel(
|
||||||
new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder());
|
new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder());
|
||||||
|
|
||||||
HttpRequest req = new DefaultHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat");
|
FullHttpRequest req = new DefaultFullHttpRequest(
|
||||||
req.setHeader(Names.HOST, "server.example.com");
|
HTTP_1_1, HttpMethod.GET, "/chat", Unpooled.copiedBuffer("^n:ds[4U", CharsetUtil.US_ASCII));
|
||||||
req.setHeader(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
|
||||||
req.setHeader(Names.CONNECTION, "Upgrade");
|
|
||||||
req.setHeader(Names.ORIGIN, "http://example.com");
|
|
||||||
req.setHeader(Names.SEC_WEBSOCKET_KEY1, "4 @1 46546xW%0l 1 5");
|
|
||||||
req.setHeader(Names.SEC_WEBSOCKET_KEY2, "12998 5 Y3 1 .P00");
|
|
||||||
req.setHeader(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
|
|
||||||
|
|
||||||
ByteBuf buffer = Unpooled.copiedBuffer("^n:ds[4U", CharsetUtil.US_ASCII);
|
req.headers().set(Names.HOST, "server.example.com");
|
||||||
req.setContent(buffer);
|
req.headers().set(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
||||||
|
req.headers().set(Names.CONNECTION, "Upgrade");
|
||||||
|
req.headers().set(Names.ORIGIN, "http://example.com");
|
||||||
|
req.headers().set(Names.SEC_WEBSOCKET_KEY1, "4 @1 46546xW%0l 1 5");
|
||||||
|
req.headers().set(Names.SEC_WEBSOCKET_KEY2, "12998 5 Y3 1 .P00");
|
||||||
|
req.headers().set(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
|
||||||
|
|
||||||
new WebSocketServerHandshaker00(
|
new WebSocketServerHandshaker00(
|
||||||
"ws://example.com/chat", "chat", Integer.MAX_VALUE).handshake(ch, req);
|
"ws://example.com/chat", "chat", Integer.MAX_VALUE).handshake(ch, req);
|
||||||
|
@ -61,12 +60,12 @@ public class WebSocketServerHandshaker00Test {
|
||||||
|
|
||||||
EmbeddedByteChannel ch2 = new EmbeddedByteChannel(new HttpResponseDecoder());
|
EmbeddedByteChannel ch2 = new EmbeddedByteChannel(new HttpResponseDecoder());
|
||||||
ch2.writeInbound(resBuf);
|
ch2.writeInbound(resBuf);
|
||||||
HttpResponseHeader res = (HttpResponseHeader) ch2.readInbound();
|
HttpResponse res = (HttpResponse) ch2.readInbound();
|
||||||
|
|
||||||
Assert.assertEquals("ws://example.com/chat", res.getHeader(Names.SEC_WEBSOCKET_LOCATION));
|
Assert.assertEquals("ws://example.com/chat", res.headers().get(Names.SEC_WEBSOCKET_LOCATION));
|
||||||
Assert.assertEquals("chat", res.getHeader(Names.SEC_WEBSOCKET_PROTOCOL));
|
Assert.assertEquals("chat", res.headers().get(Names.SEC_WEBSOCKET_PROTOCOL));
|
||||||
LastHttpContent content = (LastHttpContent) ch2.readInbound();
|
LastHttpContent content = (LastHttpContent) ch2.readInbound();
|
||||||
|
|
||||||
Assert.assertEquals("8jKS'y:G*Co,Wxa-", content.getContent().toString(CharsetUtil.US_ASCII));
|
Assert.assertEquals("8jKS'y:G*Co,Wxa-", content.data().toString(CharsetUtil.US_ASCII));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,23 +15,23 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.websocketx;
|
package io.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http.HttpHeaders.Values.*;
|
|
||||||
import static io.netty.handler.codec.http.HttpVersion.*;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.embedded.EmbeddedByteChannel;
|
import io.netty.channel.embedded.EmbeddedByteChannel;
|
||||||
import io.netty.handler.codec.http.DefaultHttpRequest;
|
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseHeader;
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpResponseDecoder;
|
import io.netty.handler.codec.http.HttpResponseDecoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static io.netty.handler.codec.http.HttpHeaders.Values.*;
|
||||||
|
import static io.netty.handler.codec.http.HttpVersion.*;
|
||||||
|
|
||||||
public class WebSocketServerHandshaker08Test {
|
public class WebSocketServerHandshaker08Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -39,14 +39,14 @@ public class WebSocketServerHandshaker08Test {
|
||||||
EmbeddedByteChannel ch = new EmbeddedByteChannel(
|
EmbeddedByteChannel ch = new EmbeddedByteChannel(
|
||||||
new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder());
|
new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder());
|
||||||
|
|
||||||
HttpRequest req = new DefaultHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat");
|
FullHttpRequest req = new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat");
|
||||||
req.setHeader(Names.HOST, "server.example.com");
|
req.headers().set(Names.HOST, "server.example.com");
|
||||||
req.setHeader(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
req.headers().set(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
||||||
req.setHeader(Names.CONNECTION, "Upgrade");
|
req.headers().set(Names.CONNECTION, "Upgrade");
|
||||||
req.setHeader(Names.SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ==");
|
req.headers().set(Names.SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ==");
|
||||||
req.setHeader(Names.SEC_WEBSOCKET_ORIGIN, "http://example.com");
|
req.headers().set(Names.SEC_WEBSOCKET_ORIGIN, "http://example.com");
|
||||||
req.setHeader(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
|
req.headers().set(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
|
||||||
req.setHeader(Names.SEC_WEBSOCKET_VERSION, "8");
|
req.headers().set(Names.SEC_WEBSOCKET_VERSION, "8");
|
||||||
|
|
||||||
new WebSocketServerHandshaker08(
|
new WebSocketServerHandshaker08(
|
||||||
"ws://example.com/chat", "chat", false, Integer.MAX_VALUE).handshake(ch, req);
|
"ws://example.com/chat", "chat", false, Integer.MAX_VALUE).handshake(ch, req);
|
||||||
|
@ -55,10 +55,10 @@ public class WebSocketServerHandshaker08Test {
|
||||||
|
|
||||||
EmbeddedByteChannel ch2 = new EmbeddedByteChannel(new HttpResponseDecoder());
|
EmbeddedByteChannel ch2 = new EmbeddedByteChannel(new HttpResponseDecoder());
|
||||||
ch2.writeInbound(resBuf);
|
ch2.writeInbound(resBuf);
|
||||||
HttpResponseHeader res = (HttpResponseHeader) ch2.readInbound();
|
HttpResponse res = (HttpResponse) ch2.readInbound();
|
||||||
|
|
||||||
Assert.assertEquals(
|
Assert.assertEquals(
|
||||||
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.getHeader(Names.SEC_WEBSOCKET_ACCEPT));
|
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.headers().get(Names.SEC_WEBSOCKET_ACCEPT));
|
||||||
Assert.assertEquals("chat", res.getHeader(Names.SEC_WEBSOCKET_PROTOCOL));
|
Assert.assertEquals("chat", res.headers().get(Names.SEC_WEBSOCKET_PROTOCOL));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,23 +15,23 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.websocketx;
|
package io.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http.HttpHeaders.Values.*;
|
|
||||||
import static io.netty.handler.codec.http.HttpVersion.*;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.embedded.EmbeddedByteChannel;
|
import io.netty.channel.embedded.EmbeddedByteChannel;
|
||||||
import io.netty.handler.codec.http.DefaultHttpRequest;
|
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseHeader;
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpResponseDecoder;
|
import io.netty.handler.codec.http.HttpResponseDecoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static io.netty.handler.codec.http.HttpHeaders.Values.*;
|
||||||
|
import static io.netty.handler.codec.http.HttpVersion.*;
|
||||||
|
|
||||||
public class WebSocketServerHandshaker13Test {
|
public class WebSocketServerHandshaker13Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -39,14 +39,14 @@ public class WebSocketServerHandshaker13Test {
|
||||||
EmbeddedByteChannel ch = new EmbeddedByteChannel(
|
EmbeddedByteChannel ch = new EmbeddedByteChannel(
|
||||||
new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder());
|
new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder());
|
||||||
|
|
||||||
HttpRequest req = new DefaultHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat");
|
FullHttpRequest req = new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat");
|
||||||
req.setHeader(Names.HOST, "server.example.com");
|
req.headers().set(Names.HOST, "server.example.com");
|
||||||
req.setHeader(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
req.headers().set(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
||||||
req.setHeader(Names.CONNECTION, "Upgrade");
|
req.headers().set(Names.CONNECTION, "Upgrade");
|
||||||
req.setHeader(Names.SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ==");
|
req.headers().set(Names.SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ==");
|
||||||
req.setHeader(Names.SEC_WEBSOCKET_ORIGIN, "http://example.com");
|
req.headers().set(Names.SEC_WEBSOCKET_ORIGIN, "http://example.com");
|
||||||
req.setHeader(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
|
req.headers().set(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
|
||||||
req.setHeader(Names.SEC_WEBSOCKET_VERSION, "13");
|
req.headers().set(Names.SEC_WEBSOCKET_VERSION, "13");
|
||||||
|
|
||||||
new WebSocketServerHandshaker13(
|
new WebSocketServerHandshaker13(
|
||||||
"ws://example.com/chat", "chat", false, Integer.MAX_VALUE).handshake(ch, req);
|
"ws://example.com/chat", "chat", false, Integer.MAX_VALUE).handshake(ch, req);
|
||||||
|
@ -55,10 +55,10 @@ public class WebSocketServerHandshaker13Test {
|
||||||
|
|
||||||
EmbeddedByteChannel ch2 = new EmbeddedByteChannel(new HttpResponseDecoder());
|
EmbeddedByteChannel ch2 = new EmbeddedByteChannel(new HttpResponseDecoder());
|
||||||
ch2.writeInbound(resBuf);
|
ch2.writeInbound(resBuf);
|
||||||
HttpResponseHeader res = (HttpResponseHeader) ch2.readInbound();
|
HttpResponse res = (HttpResponse) ch2.readInbound();
|
||||||
|
|
||||||
Assert.assertEquals(
|
Assert.assertEquals(
|
||||||
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.getHeader(Names.SEC_WEBSOCKET_ACCEPT));
|
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.headers().get(Names.SEC_WEBSOCKET_ACCEPT));
|
||||||
Assert.assertEquals("chat", res.getHeader(Names.SEC_WEBSOCKET_PROTOCOL));
|
Assert.assertEquals("chat", res.headers().get(Names.SEC_WEBSOCKET_PROTOCOL));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,13 +22,13 @@ import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||||
import io.netty.channel.ChannelOutboundMessageHandlerAdapter;
|
import io.netty.channel.ChannelOutboundMessageHandlerAdapter;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.channel.ChannelPromise;
|
||||||
import io.netty.channel.embedded.EmbeddedMessageChannel;
|
import io.netty.channel.embedded.EmbeddedMessageChannel;
|
||||||
import io.netty.handler.codec.http.DefaultHttpRequestHeader;
|
import io.netty.handler.codec.http.DefaultHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpRequestHeader;
|
|
||||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpResponseHeader;
|
|
||||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ public class WebSocketServerProtocolHandlerTest {
|
||||||
|
|
||||||
writeUpgradeRequest(ch);
|
writeUpgradeRequest(ch);
|
||||||
|
|
||||||
assertEquals(SWITCHING_PROTOCOLS, ((HttpResponseHeader) ch.outboundMessageBuffer().poll()).getStatus());
|
assertEquals(SWITCHING_PROTOCOLS, ((HttpResponse) ch.outboundMessageBuffer().poll()).status());
|
||||||
assertNotNull(WebSocketServerProtocolHandler.getHandshaker(handshakerCtx));
|
assertNotNull(WebSocketServerProtocolHandler.getHandshaker(handshakerCtx));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,16 +55,16 @@ public class WebSocketServerProtocolHandlerTest {
|
||||||
EmbeddedMessageChannel ch = createChannel(new MockOutboundHandler());
|
EmbeddedMessageChannel ch = createChannel(new MockOutboundHandler());
|
||||||
|
|
||||||
writeUpgradeRequest(ch);
|
writeUpgradeRequest(ch);
|
||||||
assertEquals(SWITCHING_PROTOCOLS, ((HttpResponseHeader) ch.outboundMessageBuffer().poll()).getStatus());
|
assertEquals(SWITCHING_PROTOCOLS, ((HttpResponse) ch.outboundMessageBuffer().poll()).status());
|
||||||
|
|
||||||
ch.writeInbound(new DefaultHttpRequestHeader(HTTP_1_1, HttpMethod.GET, "/test"));
|
ch.writeInbound(new DefaultHttpRequest(HTTP_1_1, HttpMethod.GET, "/test"));
|
||||||
assertEquals(FORBIDDEN, ((HttpResponseHeader) ch.outboundMessageBuffer().poll()).getStatus());
|
assertEquals(FORBIDDEN, ((HttpResponse) ch.outboundMessageBuffer().poll()).status());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHttpUpgradeRequestInvalidUpgradeHeader() {
|
public void testHttpUpgradeRequestInvalidUpgradeHeader() {
|
||||||
EmbeddedMessageChannel ch = createChannel();
|
EmbeddedMessageChannel ch = createChannel();
|
||||||
HttpRequest httpRequest = new WebSocketRequestBuilder().httpVersion(HTTP_1_1)
|
FullHttpRequest httpRequestWithEntity = new WebSocketRequestBuilder().httpVersion(HTTP_1_1)
|
||||||
.method(HttpMethod.GET)
|
.method(HttpMethod.GET)
|
||||||
.uri("/test")
|
.uri("/test")
|
||||||
.connection("Upgrade")
|
.connection("Upgrade")
|
||||||
|
@ -72,10 +72,10 @@ public class WebSocketServerProtocolHandlerTest {
|
||||||
.upgrade("BogusSocket")
|
.upgrade("BogusSocket")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
ch.writeInbound(httpRequest);
|
ch.writeInbound(httpRequestWithEntity);
|
||||||
|
|
||||||
HttpResponse response = getHttpResponse(ch);
|
FullHttpResponse response = getHttpResponse(ch);
|
||||||
assertEquals(BAD_REQUEST, response.getStatus());
|
assertEquals(BAD_REQUEST, response.status());
|
||||||
assertEquals("not a WebSocket handshake request: missing upgrade", getResponseMessage(response));
|
assertEquals("not a WebSocket handshake request: missing upgrade", getResponseMessage(response));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ public class WebSocketServerProtocolHandlerTest {
|
||||||
@Test
|
@Test
|
||||||
public void testHttpUpgradeRequestMissingWSKeyHeader() {
|
public void testHttpUpgradeRequestMissingWSKeyHeader() {
|
||||||
EmbeddedMessageChannel ch = createChannel();
|
EmbeddedMessageChannel ch = createChannel();
|
||||||
HttpRequestHeader httpRequest = new WebSocketRequestBuilder().httpVersion(HTTP_1_1)
|
HttpRequest httpRequest = new WebSocketRequestBuilder().httpVersion(HTTP_1_1)
|
||||||
.method(HttpMethod.GET)
|
.method(HttpMethod.GET)
|
||||||
.uri("/test")
|
.uri("/test")
|
||||||
.key(null)
|
.key(null)
|
||||||
|
@ -94,8 +94,8 @@ public class WebSocketServerProtocolHandlerTest {
|
||||||
|
|
||||||
ch.writeInbound(httpRequest);
|
ch.writeInbound(httpRequest);
|
||||||
|
|
||||||
HttpResponse response = getHttpResponse(ch);
|
FullHttpResponse response = getHttpResponse(ch);
|
||||||
assertEquals(BAD_REQUEST, response.getStatus());
|
assertEquals(BAD_REQUEST, response.status());
|
||||||
assertEquals("not a WebSocket request: missing key", getResponseMessage(response));
|
assertEquals("not a WebSocket request: missing key", getResponseMessage(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,13 +129,13 @@ public class WebSocketServerProtocolHandlerTest {
|
||||||
ch.writeInbound(WebSocketRequestBuilder.sucessful());
|
ch.writeInbound(WebSocketRequestBuilder.sucessful());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getResponseMessage(HttpResponse response) {
|
private static String getResponseMessage(FullHttpResponse response) {
|
||||||
return new String(response.getContent().array());
|
return new String(response.data().array());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpResponse getHttpResponse(EmbeddedMessageChannel ch) {
|
private static FullHttpResponse getHttpResponse(EmbeddedMessageChannel ch) {
|
||||||
MessageBuf<Object> outbound = ch.pipeline().context(MockOutboundHandler.class).outboundMessageBuffer();
|
MessageBuf<Object> outbound = ch.pipeline().context(MockOutboundHandler.class).outboundMessageBuffer();
|
||||||
return (HttpResponse) outbound.poll();
|
return (FullHttpResponse) outbound.poll();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class MockOutboundHandler extends ChannelOutboundMessageHandlerAdapter<Object> {
|
private static class MockOutboundHandler extends ChannelOutboundMessageHandlerAdapter<Object> {
|
||||||
|
|
|
@ -20,10 +20,10 @@ import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.stream.ChunkedFile;
|
import io.netty.handler.stream.ChunkedFile;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
|
@ -94,7 +94,7 @@ import static io.netty.handler.codec.http.HttpVersion.*;
|
||||||
*
|
*
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAdapter<HttpRequest> {
|
public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAdapter<FullHttpRequest> {
|
||||||
|
|
||||||
public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
|
public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
|
||||||
public static final String HTTP_DATE_GMT_TIMEZONE = "GMT";
|
public static final String HTTP_DATE_GMT_TIMEZONE = "GMT";
|
||||||
|
@ -102,19 +102,19 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void messageReceived(
|
public void messageReceived(
|
||||||
ChannelHandlerContext ctx, HttpRequest request) throws Exception {
|
ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
|
||||||
|
|
||||||
if (!request.getDecoderResult().isSuccess()) {
|
if (!request.decoderResult().isSuccess()) {
|
||||||
sendError(ctx, BAD_REQUEST);
|
sendError(ctx, BAD_REQUEST);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.getMethod() != GET) {
|
if (request.method() != GET) {
|
||||||
sendError(ctx, METHOD_NOT_ALLOWED);
|
sendError(ctx, METHOD_NOT_ALLOWED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String uri = request.getUri();
|
final String uri = request.uri();
|
||||||
final String path = sanitizeUri(uri);
|
final String path = sanitizeUri(uri);
|
||||||
if (path == null) {
|
if (path == null) {
|
||||||
sendError(ctx, FORBIDDEN);
|
sendError(ctx, FORBIDDEN);
|
||||||
|
@ -142,7 +142,7 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache Validation
|
// Cache Validation
|
||||||
String ifModifiedSince = request.getHeader(IF_MODIFIED_SINCE);
|
String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE);
|
||||||
if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) {
|
if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) {
|
||||||
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
|
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
|
||||||
Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);
|
Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);
|
||||||
|
@ -166,12 +166,12 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
|
||||||
}
|
}
|
||||||
long fileLength = raf.length();
|
long fileLength = raf.length();
|
||||||
|
|
||||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
|
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
|
||||||
setContentLength(response, fileLength);
|
setContentLength(response, fileLength);
|
||||||
setContentTypeHeader(response, file);
|
setContentTypeHeader(response, file);
|
||||||
setDateAndCacheHeaders(response, file);
|
setDateAndCacheHeaders(response, file);
|
||||||
if (isKeepAlive(request)) {
|
if (isKeepAlive(request)) {
|
||||||
response.setHeader(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
|
response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the initial line and the header.
|
// Write the initial line and the header.
|
||||||
|
@ -232,8 +232,8 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
|
||||||
private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[A-Za-z0-9][-_A-Za-z0-9\\.]*");
|
private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[A-Za-z0-9][-_A-Za-z0-9\\.]*");
|
||||||
|
|
||||||
private static void sendListing(ChannelHandlerContext ctx, File dir) {
|
private static void sendListing(ChannelHandlerContext ctx, File dir) {
|
||||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
|
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
|
||||||
response.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8");
|
response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
|
||||||
|
|
||||||
StringBuilder buf = new StringBuilder();
|
StringBuilder buf = new StringBuilder();
|
||||||
String dirPath = dir.getPath();
|
String dirPath = dir.getPath();
|
||||||
|
@ -270,26 +270,24 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
|
||||||
|
|
||||||
buf.append("</ul></body></html>\r\n");
|
buf.append("</ul></body></html>\r\n");
|
||||||
|
|
||||||
response.setContent(Unpooled.copiedBuffer(buf, CharsetUtil.UTF_8));
|
response.data().writeBytes(Unpooled.copiedBuffer(buf, CharsetUtil.UTF_8));
|
||||||
|
|
||||||
// Close the connection as soon as the error message is sent.
|
// Close the connection as soon as the error message is sent.
|
||||||
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
|
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void sendRedirect(ChannelHandlerContext ctx, String newUri) {
|
private static void sendRedirect(ChannelHandlerContext ctx, String newUri) {
|
||||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, FOUND);
|
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND);
|
||||||
response.setHeader(LOCATION, newUri);
|
response.headers().set(LOCATION, newUri);
|
||||||
|
|
||||||
// Close the connection as soon as the error message is sent.
|
// Close the connection as soon as the error message is sent.
|
||||||
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
|
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
|
private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
|
||||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, status);
|
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||||
response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
|
HTTP_1_1, status, Unpooled.copiedBuffer("Failure: " + status.toString() + "\r\n", CharsetUtil.UTF_8));
|
||||||
response.setContent(Unpooled.copiedBuffer(
|
response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||||
"Failure: " + status.toString() + "\r\n",
|
|
||||||
CharsetUtil.UTF_8));
|
|
||||||
|
|
||||||
// Close the connection as soon as the error message is sent.
|
// Close the connection as soon as the error message is sent.
|
||||||
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
|
ctx.write(response).addListener(ChannelFutureListener.CLOSE);
|
||||||
|
@ -302,7 +300,7 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
|
||||||
* Context
|
* Context
|
||||||
*/
|
*/
|
||||||
private static void sendNotModified(ChannelHandlerContext ctx) {
|
private static void sendNotModified(ChannelHandlerContext ctx) {
|
||||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, NOT_MODIFIED);
|
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_MODIFIED);
|
||||||
setDateHeader(response);
|
setDateHeader(response);
|
||||||
|
|
||||||
// Close the connection as soon as the error message is sent.
|
// Close the connection as soon as the error message is sent.
|
||||||
|
@ -315,12 +313,12 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
|
||||||
* @param response
|
* @param response
|
||||||
* HTTP response
|
* HTTP response
|
||||||
*/
|
*/
|
||||||
private static void setDateHeader(HttpResponse response) {
|
private static void setDateHeader(FullHttpResponse response) {
|
||||||
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
|
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
|
||||||
dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));
|
dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));
|
||||||
|
|
||||||
Calendar time = new GregorianCalendar();
|
Calendar time = new GregorianCalendar();
|
||||||
response.setHeader(DATE, dateFormatter.format(time.getTime()));
|
response.headers().set(DATE, dateFormatter.format(time.getTime()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -331,19 +329,19 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
|
||||||
* @param fileToCache
|
* @param fileToCache
|
||||||
* file to extract content type
|
* file to extract content type
|
||||||
*/
|
*/
|
||||||
private static void setDateAndCacheHeaders(HttpResponse response, File fileToCache) {
|
private static void setDateAndCacheHeaders(FullHttpResponse response, File fileToCache) {
|
||||||
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
|
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
|
||||||
dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));
|
dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));
|
||||||
|
|
||||||
// Date header
|
// Date header
|
||||||
Calendar time = new GregorianCalendar();
|
Calendar time = new GregorianCalendar();
|
||||||
response.setHeader(DATE, dateFormatter.format(time.getTime()));
|
response.headers().set(DATE, dateFormatter.format(time.getTime()));
|
||||||
|
|
||||||
// Add cache headers
|
// Add cache headers
|
||||||
time.add(Calendar.SECOND, HTTP_CACHE_SECONDS);
|
time.add(Calendar.SECOND, HTTP_CACHE_SECONDS);
|
||||||
response.setHeader(EXPIRES, dateFormatter.format(time.getTime()));
|
response.headers().set(EXPIRES, dateFormatter.format(time.getTime()));
|
||||||
response.setHeader(CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);
|
response.headers().set(CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);
|
||||||
response.setHeader(
|
response.headers().set(
|
||||||
LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified())));
|
LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,9 +353,9 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
|
||||||
* @param file
|
* @param file
|
||||||
* file to extract content type
|
* file to extract content type
|
||||||
*/
|
*/
|
||||||
private static void setContentTypeHeader(HttpResponse response, File file) {
|
private static void setContentTypeHeader(FullHttpResponse response, File file) {
|
||||||
MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
|
MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
|
||||||
response.setHeader(CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath()));
|
response.headers().set(CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,10 @@ import io.netty.channel.socket.nio.NioEventLoopGroup;
|
||||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||||
import io.netty.handler.codec.http.ClientCookieEncoder;
|
import io.netty.handler.codec.http.ClientCookieEncoder;
|
||||||
import io.netty.handler.codec.http.DefaultCookie;
|
import io.netty.handler.codec.http.DefaultCookie;
|
||||||
import io.netty.handler.codec.http.DefaultHttpRequestHeader;
|
import io.netty.handler.codec.http.DefaultHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
import io.netty.handler.codec.http.HttpRequestHeader;
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpVersion;
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
@ -73,14 +73,14 @@ public class HttpSnoopClient {
|
||||||
Channel ch = b.connect().sync().channel();
|
Channel ch = b.connect().sync().channel();
|
||||||
|
|
||||||
// Prepare the HTTP request.
|
// Prepare the HTTP request.
|
||||||
HttpRequestHeader request = new DefaultHttpRequestHeader(
|
HttpRequest request = new DefaultHttpRequest(
|
||||||
HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getRawPath());
|
HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getRawPath());
|
||||||
request.setHeader(HttpHeaders.Names.HOST, host);
|
request.headers().set(HttpHeaders.Names.HOST, host);
|
||||||
request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
|
request.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
|
||||||
request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
|
request.headers().set(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
|
||||||
|
|
||||||
// Set some example cookies.
|
// Set some example cookies.
|
||||||
request.setHeader(
|
request.headers().set(
|
||||||
HttpHeaders.Names.COOKIE,
|
HttpHeaders.Names.COOKIE,
|
||||||
ClientCookieEncoder.encode(
|
ClientCookieEncoder.encode(
|
||||||
new DefaultCookie("my-cookie", "foo"),
|
new DefaultCookie("my-cookie", "foo"),
|
||||||
|
|
|
@ -19,7 +19,7 @@ import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||||
import io.netty.handler.codec.http.HttpContent;
|
import io.netty.handler.codec.http.HttpContent;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpResponseHeader;
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
import io.netty.handler.codec.http.LastHttpContent;
|
import io.netty.handler.codec.http.LastHttpContent;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
|
@ -27,16 +27,16 @@ public class HttpSnoopClientHandler extends ChannelInboundMessageHandlerAdapter<
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
if (msg instanceof HttpResponseHeader) {
|
if (msg instanceof HttpResponse) {
|
||||||
HttpResponseHeader response = (HttpResponseHeader) msg;
|
HttpResponse response = (HttpResponse) msg;
|
||||||
|
|
||||||
System.out.println("STATUS: " + response.getStatus());
|
System.out.println("STATUS: " + response.status());
|
||||||
System.out.println("VERSION: " + response.getProtocolVersion());
|
System.out.println("VERSION: " + response.protocolVersion());
|
||||||
System.out.println();
|
System.out.println();
|
||||||
|
|
||||||
if (!response.getHeaderNames().isEmpty()) {
|
if (!response.headers().isEmpty()) {
|
||||||
for (String name: response.getHeaderNames()) {
|
for (String name: response.headers().names()) {
|
||||||
for (String value: response.getHeaders(name)) {
|
for (String value: response.headers().getAll(name)) {
|
||||||
System.out.println("HEADER: " + name + " = " + value);
|
System.out.println("HEADER: " + name + " = " + value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ public class HttpSnoopClientHandler extends ChannelInboundMessageHandlerAdapter<
|
||||||
if (msg instanceof HttpContent) {
|
if (msg instanceof HttpContent) {
|
||||||
HttpContent content = (HttpContent) msg;
|
HttpContent content = (HttpContent) msg;
|
||||||
|
|
||||||
System.out.print(content.getContent().toString(CharsetUtil.UTF_8));
|
System.out.print(content.data().toString(CharsetUtil.UTF_8));
|
||||||
System.out.flush();
|
System.out.flush();
|
||||||
|
|
||||||
if (content instanceof LastHttpContent) {
|
if (content instanceof LastHttpContent) {
|
||||||
|
|
|
@ -24,15 +24,15 @@ import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||||
import io.netty.handler.codec.DecoderResult;
|
import io.netty.handler.codec.DecoderResult;
|
||||||
import io.netty.handler.codec.http.Cookie;
|
import io.netty.handler.codec.http.Cookie;
|
||||||
import io.netty.handler.codec.http.CookieDecoder;
|
import io.netty.handler.codec.http.CookieDecoder;
|
||||||
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||||
import io.netty.handler.codec.http.DefaultHttpResponseHeader;
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpContent;
|
import io.netty.handler.codec.http.HttpContent;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpRequestHeader;
|
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
|
||||||
import io.netty.handler.codec.http.HttpResponseHeader;
|
|
||||||
import io.netty.handler.codec.http.LastHttpContent;
|
|
||||||
import io.netty.handler.codec.http.HttpObject;
|
import io.netty.handler.codec.http.HttpObject;
|
||||||
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
|
import io.netty.handler.codec.http.LastHttpContent;
|
||||||
import io.netty.handler.codec.http.QueryStringDecoder;
|
import io.netty.handler.codec.http.QueryStringDecoder;
|
||||||
import io.netty.handler.codec.http.ServerCookieEncoder;
|
import io.netty.handler.codec.http.ServerCookieEncoder;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
|
@ -49,15 +49,14 @@ import static io.netty.handler.codec.http.HttpVersion.*;
|
||||||
|
|
||||||
public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<Object> {
|
public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<Object> {
|
||||||
|
|
||||||
private HttpRequestHeader request;
|
private HttpRequest request;
|
||||||
private boolean readingChunks;
|
|
||||||
/** Buffer that stores the response content */
|
/** Buffer that stores the response content */
|
||||||
private final StringBuilder buf = new StringBuilder();
|
private final StringBuilder buf = new StringBuilder();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
if (msg instanceof HttpRequestHeader) {
|
if (msg instanceof HttpRequest) {
|
||||||
HttpRequestHeader request = this.request = (HttpRequestHeader) msg;
|
HttpRequest request = this.request = (HttpRequest) msg;
|
||||||
|
|
||||||
if (is100ContinueExpected(request)) {
|
if (is100ContinueExpected(request)) {
|
||||||
send100Continue(ctx);
|
send100Continue(ctx);
|
||||||
|
@ -67,13 +66,13 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
|
||||||
buf.append("WELCOME TO THE WILD WILD WEB SERVER\r\n");
|
buf.append("WELCOME TO THE WILD WILD WEB SERVER\r\n");
|
||||||
buf.append("===================================\r\n");
|
buf.append("===================================\r\n");
|
||||||
|
|
||||||
buf.append("VERSION: ").append(request.getProtocolVersion()).append("\r\n");
|
buf.append("VERSION: ").append(request.protocolVersion()).append("\r\n");
|
||||||
buf.append("HOSTNAME: ").append(getHost(request, "unknown")).append("\r\n");
|
buf.append("HOSTNAME: ").append(getHost(request, "unknown")).append("\r\n");
|
||||||
buf.append("REQUEST_URI: ").append(request.getUri()).append("\r\n\r\n");
|
buf.append("REQUEST_URI: ").append(request.uri()).append("\r\n\r\n");
|
||||||
|
|
||||||
List<Map.Entry<String, String>> headers = request.getHeaders();
|
List<Map.Entry<String, String>> headers = request.headers().entries();
|
||||||
if (!headers.isEmpty()) {
|
if (!headers.isEmpty()) {
|
||||||
for (Map.Entry<String, String> h: request.getHeaders()) {
|
for (Map.Entry<String, String> h: request.headers().entries()) {
|
||||||
String key = h.getKey();
|
String key = h.getKey();
|
||||||
String value = h.getValue();
|
String value = h.getValue();
|
||||||
buf.append("HEADER: ").append(key).append(" = ").append(value).append("\r\n");
|
buf.append("HEADER: ").append(key).append(" = ").append(value).append("\r\n");
|
||||||
|
@ -81,7 +80,7 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
|
||||||
buf.append("\r\n");
|
buf.append("\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.getUri());
|
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri());
|
||||||
Map<String, List<String>> params = queryStringDecoder.getParameters();
|
Map<String, List<String>> params = queryStringDecoder.getParameters();
|
||||||
if (!params.isEmpty()) {
|
if (!params.isEmpty()) {
|
||||||
for (Entry<String, List<String>> p: params.entrySet()) {
|
for (Entry<String, List<String>> p: params.entrySet()) {
|
||||||
|
@ -94,40 +93,28 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
|
||||||
buf.append("\r\n");
|
buf.append("\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isTransferEncodingChunked(request)) {
|
appendDecoderResult(buf, request);
|
||||||
readingChunks = true;
|
|
||||||
} else {
|
|
||||||
appendDecoderResult(buf, request);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg instanceof HttpContent) {
|
if (msg instanceof HttpContent) {
|
||||||
HttpContent httpContent = (HttpContent) msg;
|
HttpContent httpContent = (HttpContent) msg;
|
||||||
|
|
||||||
ByteBuf content = httpContent.getContent();
|
ByteBuf content = httpContent.data();
|
||||||
if (content.readable()) {
|
if (content.readable()) {
|
||||||
buf.append("CONTENT: ");
|
buf.append("CONTENT: ");
|
||||||
buf.append(content.toString(CharsetUtil.UTF_8));
|
buf.append(content.toString(CharsetUtil.UTF_8));
|
||||||
buf.append("\r\n");
|
buf.append("\r\n");
|
||||||
|
appendDecoderResult(buf, request);
|
||||||
}
|
}
|
||||||
appendDecoderResult(buf, request);
|
|
||||||
writeResponse(ctx, request);
|
|
||||||
|
|
||||||
if (msg instanceof LastHttpContent) {
|
if (msg instanceof LastHttpContent) {
|
||||||
if (readingChunks) {
|
|
||||||
buf.append("CHUNK: ");
|
|
||||||
} else {
|
|
||||||
buf.append("CONTENT: ");
|
|
||||||
}
|
|
||||||
buf.append(content.toString(CharsetUtil.UTF_8)).append("\r\n");
|
|
||||||
|
|
||||||
readingChunks = false;
|
|
||||||
buf.append("END OF CONTENT\r\n");
|
buf.append("END OF CONTENT\r\n");
|
||||||
|
|
||||||
LastHttpContent trailer = (LastHttpContent) msg;
|
LastHttpContent trailer = (LastHttpContent) msg;
|
||||||
if (!trailer.getHeaderNames().isEmpty()) {
|
if (!trailer.trailingHeaders().isEmpty()) {
|
||||||
buf.append("\r\n");
|
buf.append("\r\n");
|
||||||
for (String name: trailer.getHeaderNames()) {
|
for (String name: trailer.trailingHeaders().names()) {
|
||||||
for (String value: trailer.getHeaders(name)) {
|
for (String value: trailer.trailingHeaders().getAll(name)) {
|
||||||
buf.append("TRAILING HEADER: ");
|
buf.append("TRAILING HEADER: ");
|
||||||
buf.append(name).append(" = ").append(value).append("\r\n");
|
buf.append(name).append(" = ").append(value).append("\r\n");
|
||||||
}
|
}
|
||||||
|
@ -135,20 +122,13 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
|
||||||
buf.append("\r\n");
|
buf.append("\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
appendDecoderResult(buf, trailer);
|
|
||||||
writeResponse(ctx, trailer);
|
writeResponse(ctx, trailer);
|
||||||
} else {
|
|
||||||
if (readingChunks) {
|
|
||||||
buf.append("CHUNK: ");
|
|
||||||
}
|
|
||||||
buf.append(content.toString(CharsetUtil.UTF_8)).append("\r\n");
|
|
||||||
appendDecoderResult(buf, httpContent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void appendDecoderResult(StringBuilder buf, HttpObject o) {
|
private static void appendDecoderResult(StringBuilder buf, HttpObject o) {
|
||||||
DecoderResult result = o.getDecoderResult();
|
DecoderResult result = o.decoderResult();
|
||||||
if (result.isSuccess()) {
|
if (result.isSuccess()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -167,34 +147,34 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
|
||||||
boolean keepAlive = isKeepAlive(request);
|
boolean keepAlive = isKeepAlive(request);
|
||||||
|
|
||||||
// Build the response object.
|
// Build the response object.
|
||||||
HttpResponse response = new DefaultHttpResponse(
|
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||||
HTTP_1_1, currentObj.getDecoderResult().isSuccess()? OK : BAD_REQUEST);
|
HTTP_1_1, currentObj.decoderResult().isSuccess()? OK : BAD_REQUEST,
|
||||||
|
Unpooled.copiedBuffer(buf.toString(), CharsetUtil.UTF_8));
|
||||||
|
|
||||||
response.setContent(Unpooled.copiedBuffer(buf.toString(), CharsetUtil.UTF_8));
|
response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||||
response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
|
|
||||||
|
|
||||||
if (keepAlive) {
|
if (keepAlive) {
|
||||||
// Add 'Content-Length' header only for a keep-alive connection.
|
// Add 'Content-Length' header only for a keep-alive connection.
|
||||||
response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes());
|
response.headers().set(CONTENT_LENGTH, response.data().readableBytes());
|
||||||
// Add keep alive header as per:
|
// Add keep alive header as per:
|
||||||
// - http://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01.html#Connection
|
// - http://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01.html#Connection
|
||||||
response.setHeader(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
|
response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode the cookie.
|
// Encode the cookie.
|
||||||
String cookieString = request.getHeader(COOKIE);
|
String cookieString = request.headers().get(COOKIE);
|
||||||
if (cookieString != null) {
|
if (cookieString != null) {
|
||||||
Set<Cookie> cookies = CookieDecoder.decode(cookieString);
|
Set<Cookie> cookies = CookieDecoder.decode(cookieString);
|
||||||
if (!cookies.isEmpty()) {
|
if (!cookies.isEmpty()) {
|
||||||
// Reset the cookies if necessary.
|
// Reset the cookies if necessary.
|
||||||
for (Cookie cookie: cookies) {
|
for (Cookie cookie: cookies) {
|
||||||
response.addHeader(SET_COOKIE, ServerCookieEncoder.encode(cookie));
|
response.headers().add(SET_COOKIE, ServerCookieEncoder.encode(cookie));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Browser sent no cookie. Add some.
|
// Browser sent no cookie. Add some.
|
||||||
response.addHeader(SET_COOKIE, ServerCookieEncoder.encode("key1", "value1"));
|
response.headers().add(SET_COOKIE, ServerCookieEncoder.encode("key1", "value1"));
|
||||||
response.addHeader(SET_COOKIE, ServerCookieEncoder.encode("key2", "value2"));
|
response.headers().add(SET_COOKIE, ServerCookieEncoder.encode("key2", "value2"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the response.
|
// Write the response.
|
||||||
|
@ -207,7 +187,7 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void send100Continue(ChannelHandlerContext ctx) {
|
private static void send100Continue(ChannelHandlerContext ctx) {
|
||||||
HttpResponseHeader response = new DefaultHttpResponseHeader(HTTP_1_1, CONTINUE);
|
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, CONTINUE);
|
||||||
ctx.write(response);
|
ctx.write(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ package io.netty.example.http.snoop;
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.socket.SocketChannel;
|
import io.netty.channel.socket.SocketChannel;
|
||||||
import io.netty.handler.codec.http.HttpContentCompressor;
|
|
||||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||||
|
|
||||||
|
@ -38,7 +37,7 @@ public class HttpSnoopServerInitializer extends ChannelInitializer<SocketChannel
|
||||||
//pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
|
//pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
|
||||||
p.addLast("encoder", new HttpResponseEncoder());
|
p.addLast("encoder", new HttpResponseEncoder());
|
||||||
// Remove the following line if you don't want automatic content compression.
|
// Remove the following line if you don't want automatic content compression.
|
||||||
p.addLast("deflater", new HttpContentCompressor());
|
//p.addLast("deflater", new HttpContentCompressor());
|
||||||
p.addLast("handler", new HttpSnoopServerHandler());
|
p.addLast("handler", new HttpSnoopServerHandler());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,10 @@ import io.netty.channel.socket.nio.NioEventLoopGroup;
|
||||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||||
import io.netty.handler.codec.http.ClientCookieEncoder;
|
import io.netty.handler.codec.http.ClientCookieEncoder;
|
||||||
import io.netty.handler.codec.http.DefaultCookie;
|
import io.netty.handler.codec.http.DefaultCookie;
|
||||||
import io.netty.handler.codec.http.DefaultHttpRequest;
|
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
|
||||||
import io.netty.handler.codec.http.HttpVersion;
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
import io.netty.handler.codec.http.QueryStringEncoder;
|
import io.netty.handler.codec.http.QueryStringEncoder;
|
||||||
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
|
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
|
||||||
|
@ -178,33 +178,31 @@ public class HttpUploadClient {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uriGet.toASCIIString());
|
FullHttpRequest request =
|
||||||
request.setHeader(HttpHeaders.Names.HOST, host);
|
new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uriGet.toASCIIString());
|
||||||
request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
|
HttpHeaders headers = request.headers();
|
||||||
request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP + ','
|
headers.set(HttpHeaders.Names.HOST, host);
|
||||||
|
headers.set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
|
||||||
|
headers.set(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP + ','
|
||||||
+ HttpHeaders.Values.DEFLATE);
|
+ HttpHeaders.Values.DEFLATE);
|
||||||
|
|
||||||
request.setHeader(HttpHeaders.Names.ACCEPT_CHARSET, "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
|
headers.set(HttpHeaders.Names.ACCEPT_CHARSET, "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
|
||||||
request.setHeader(HttpHeaders.Names.ACCEPT_LANGUAGE, "fr");
|
headers.set(HttpHeaders.Names.ACCEPT_LANGUAGE, "fr");
|
||||||
request.setHeader(HttpHeaders.Names.REFERER, uriSimple.toString());
|
headers.set(HttpHeaders.Names.REFERER, uriSimple.toString());
|
||||||
request.setHeader(HttpHeaders.Names.USER_AGENT, "Netty Simple Http Client side");
|
headers.set(HttpHeaders.Names.USER_AGENT, "Netty Simple Http Client side");
|
||||||
request.setHeader(HttpHeaders.Names.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
|
headers.set(HttpHeaders.Names.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
|
||||||
|
|
||||||
// connection will not close but needed
|
headers.set(HttpHeaders.Names.COOKIE, ClientCookieEncoder.encode(new DefaultCookie("my-cookie", "foo"),
|
||||||
// request.setHeader("Connection","keep-alive");
|
|
||||||
// request.setHeader("Keep-Alive","300");
|
|
||||||
|
|
||||||
request.setHeader(HttpHeaders.Names.COOKIE, ClientCookieEncoder.encode(new DefaultCookie("my-cookie", "foo"),
|
|
||||||
new DefaultCookie("another-cookie", "bar")));
|
new DefaultCookie("another-cookie", "bar")));
|
||||||
|
|
||||||
// send request
|
// send request
|
||||||
List<Entry<String, String>> headers = request.getHeaders();
|
List<Entry<String, String>> entries = headers.entries();
|
||||||
channel.write(request);
|
channel.write(request).sync();
|
||||||
|
|
||||||
// Wait for the server to close the connection.
|
// Wait for the server to close the connection.
|
||||||
channel.closeFuture().sync();
|
channel.closeFuture().sync();
|
||||||
|
|
||||||
return headers;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -220,7 +218,8 @@ public class HttpUploadClient {
|
||||||
Channel channel = bootstrap.connect().sync().channel();
|
Channel channel = bootstrap.connect().sync().channel();
|
||||||
|
|
||||||
// Prepare the HTTP request.
|
// Prepare the HTTP request.
|
||||||
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriSimple.toASCIIString());
|
FullHttpRequest request =
|
||||||
|
new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriSimple.toASCIIString());
|
||||||
|
|
||||||
// Use the PostBody encoder
|
// Use the PostBody encoder
|
||||||
HttpPostRequestEncoder bodyRequestEncoder = null;
|
HttpPostRequestEncoder bodyRequestEncoder = null;
|
||||||
|
@ -236,7 +235,7 @@ public class HttpUploadClient {
|
||||||
|
|
||||||
// it is legal to add directly header or cookie into the request until finalize
|
// it is legal to add directly header or cookie into the request until finalize
|
||||||
for (Entry<String, String> entry : headers) {
|
for (Entry<String, String> entry : headers) {
|
||||||
request.setHeader(entry.getKey(), entry.getValue());
|
request.headers().set(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
// add Form attribute
|
// add Form attribute
|
||||||
|
@ -304,7 +303,8 @@ public class HttpUploadClient {
|
||||||
Channel channel = bootstrap.connect().sync().channel();
|
Channel channel = bootstrap.connect().sync().channel();
|
||||||
|
|
||||||
// Prepare the HTTP request.
|
// Prepare the HTTP request.
|
||||||
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriFile.toASCIIString());
|
FullHttpRequest request =
|
||||||
|
new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriFile.toASCIIString());
|
||||||
|
|
||||||
// Use the PostBody encoder
|
// Use the PostBody encoder
|
||||||
HttpPostRequestEncoder bodyRequestEncoder = null;
|
HttpPostRequestEncoder bodyRequestEncoder = null;
|
||||||
|
@ -320,7 +320,7 @@ public class HttpUploadClient {
|
||||||
|
|
||||||
// it is legal to add directly header or cookie into the request until finalize
|
// it is legal to add directly header or cookie into the request until finalize
|
||||||
for (Entry<String, String> entry : headers) {
|
for (Entry<String, String> entry : headers) {
|
||||||
request.setHeader(entry.getKey(), entry.getValue());
|
request.headers().set(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
// add Form attribute from previous request in formpost()
|
// add Form attribute from previous request in formpost()
|
||||||
|
|
|
@ -19,7 +19,7 @@ import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||||
import io.netty.handler.codec.http.HttpContent;
|
import io.netty.handler.codec.http.HttpContent;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpResponseHeader;
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
import io.netty.handler.codec.http.LastHttpContent;
|
import io.netty.handler.codec.http.LastHttpContent;
|
||||||
import io.netty.logging.InternalLogger;
|
import io.netty.logging.InternalLogger;
|
||||||
import io.netty.logging.InternalLoggerFactory;
|
import io.netty.logging.InternalLoggerFactory;
|
||||||
|
@ -36,21 +36,21 @@ public class HttpUploadClientHandler extends ChannelInboundMessageHandlerAdapter
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
if (msg instanceof HttpResponseHeader) {
|
if (msg instanceof HttpResponse) {
|
||||||
HttpResponseHeader response = (HttpResponseHeader) msg;
|
HttpResponse response = (HttpResponse) msg;
|
||||||
|
|
||||||
logger.info("STATUS: " + response.getStatus());
|
logger.info("STATUS: " + response.status());
|
||||||
logger.info("VERSION: " + response.getProtocolVersion());
|
logger.info("VERSION: " + response.protocolVersion());
|
||||||
|
|
||||||
if (!response.getHeaderNames().isEmpty()) {
|
if (!response.headers().isEmpty()) {
|
||||||
for (String name : response.getHeaderNames()) {
|
for (String name : response.headers().names()) {
|
||||||
for (String value : response.getHeaders(name)) {
|
for (String value : response.headers().getAll(name)) {
|
||||||
logger.info("HEADER: " + name + " = " + value);
|
logger.info("HEADER: " + name + " = " + value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.getStatus().getCode() == 200 && HttpHeaders.isTransferEncodingChunked(response)) {
|
if (response.status().code() == 200 && HttpHeaders.isTransferEncodingChunked(response)) {
|
||||||
readingChunks = true;
|
readingChunks = true;
|
||||||
logger.info("CHUNKED CONTENT {");
|
logger.info("CHUNKED CONTENT {");
|
||||||
} else {
|
} else {
|
||||||
|
@ -59,7 +59,7 @@ public class HttpUploadClientHandler extends ChannelInboundMessageHandlerAdapter
|
||||||
}
|
}
|
||||||
if (msg instanceof HttpContent) {
|
if (msg instanceof HttpContent) {
|
||||||
HttpContent chunk = (HttpContent) msg;
|
HttpContent chunk = (HttpContent) msg;
|
||||||
logger.info(chunk.getContent().toString(CharsetUtil.UTF_8));
|
logger.info(chunk.data().toString(CharsetUtil.UTF_8));
|
||||||
|
|
||||||
if (chunk instanceof LastHttpContent) {
|
if (chunk instanceof LastHttpContent) {
|
||||||
if (readingChunks) {
|
if (readingChunks) {
|
||||||
|
@ -69,7 +69,7 @@ public class HttpUploadClientHandler extends ChannelInboundMessageHandlerAdapter
|
||||||
}
|
}
|
||||||
readingChunks = false;
|
readingChunks = false;
|
||||||
} else {
|
} else {
|
||||||
logger.info(chunk.getContent().toString(CharsetUtil.UTF_8));
|
logger.info(chunk.data().toString(CharsetUtil.UTF_8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,11 +23,11 @@ import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||||
import io.netty.handler.codec.http.Cookie;
|
import io.netty.handler.codec.http.Cookie;
|
||||||
import io.netty.handler.codec.http.CookieDecoder;
|
import io.netty.handler.codec.http.CookieDecoder;
|
||||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||||
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpContent;
|
import io.netty.handler.codec.http.HttpContent;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpRequestHeader;
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http.HttpVersion;
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
import io.netty.handler.codec.http.QueryStringDecoder;
|
import io.netty.handler.codec.http.QueryStringDecoder;
|
||||||
|
@ -64,7 +64,7 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
|
||||||
|
|
||||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(HttpUploadServerHandler.class);
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(HttpUploadServerHandler.class);
|
||||||
|
|
||||||
private HttpRequestHeader request;
|
private HttpRequest request;
|
||||||
|
|
||||||
private boolean readingChunks;
|
private boolean readingChunks;
|
||||||
|
|
||||||
|
@ -95,15 +95,15 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
if (msg instanceof HttpRequestHeader) {
|
if (msg instanceof HttpRequest) {
|
||||||
// clean previous FileUpload if Any
|
// clean previous FileUpload if Any
|
||||||
if (decoder != null) {
|
if (decoder != null) {
|
||||||
decoder.cleanFiles();
|
decoder.cleanFiles();
|
||||||
decoder = null;
|
decoder = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpRequestHeader request = this.request = (HttpRequestHeader) msg;
|
HttpRequest request = this.request = (HttpRequest) msg;
|
||||||
URI uri = new URI(request.getUri());
|
URI uri = new URI(request.uri());
|
||||||
if (!uri.getPath().startsWith("/form")) {
|
if (!uri.getPath().startsWith("/form")) {
|
||||||
// Write Menu
|
// Write Menu
|
||||||
writeMenu(ctx);
|
writeMenu(ctx);
|
||||||
|
@ -113,13 +113,13 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
|
||||||
responseContent.append("WELCOME TO THE WILD WILD WEB SERVER\r\n");
|
responseContent.append("WELCOME TO THE WILD WILD WEB SERVER\r\n");
|
||||||
responseContent.append("===================================\r\n");
|
responseContent.append("===================================\r\n");
|
||||||
|
|
||||||
responseContent.append("VERSION: " + request.getProtocolVersion().getText() + "\r\n");
|
responseContent.append("VERSION: " + request.protocolVersion().getText() + "\r\n");
|
||||||
|
|
||||||
responseContent.append("REQUEST_URI: " + request.getUri() + "\r\n\r\n");
|
responseContent.append("REQUEST_URI: " + request.uri() + "\r\n\r\n");
|
||||||
responseContent.append("\r\n\r\n");
|
responseContent.append("\r\n\r\n");
|
||||||
|
|
||||||
// new method
|
// new method
|
||||||
List<Entry<String, String>> headers = request.getHeaders();
|
List<Entry<String, String>> headers = request.headers().entries();
|
||||||
for (Entry<String, String> entry : headers) {
|
for (Entry<String, String> entry : headers) {
|
||||||
responseContent.append("HEADER: " + entry.getKey() + '=' + entry.getValue() + "\r\n");
|
responseContent.append("HEADER: " + entry.getKey() + '=' + entry.getValue() + "\r\n");
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,7 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
|
||||||
|
|
||||||
// new method
|
// new method
|
||||||
Set<Cookie> cookies;
|
Set<Cookie> cookies;
|
||||||
String value = request.getHeader(COOKIE);
|
String value = request.headers().get(COOKIE);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
cookies = Collections.emptySet();
|
cookies = Collections.emptySet();
|
||||||
} else {
|
} else {
|
||||||
|
@ -138,7 +138,7 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
|
||||||
}
|
}
|
||||||
responseContent.append("\r\n\r\n");
|
responseContent.append("\r\n\r\n");
|
||||||
|
|
||||||
QueryStringDecoder decoderQuery = new QueryStringDecoder(request.getUri());
|
QueryStringDecoder decoderQuery = new QueryStringDecoder(request.uri());
|
||||||
Map<String, List<String>> uriAttributes = decoderQuery.getParameters();
|
Map<String, List<String>> uriAttributes = decoderQuery.getParameters();
|
||||||
for (Entry<String, List<String>> attr: uriAttributes.entrySet()) {
|
for (Entry<String, List<String>> attr: uriAttributes.entrySet()) {
|
||||||
for (String attrVal: attr.getValue()) {
|
for (String attrVal: attr.getValue()) {
|
||||||
|
@ -300,23 +300,23 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
|
||||||
responseContent.setLength(0);
|
responseContent.setLength(0);
|
||||||
|
|
||||||
// Decide whether to close the connection or not.
|
// Decide whether to close the connection or not.
|
||||||
boolean close = HttpHeaders.Values.CLOSE.equalsIgnoreCase(request.getHeader(CONNECTION))
|
boolean close = HttpHeaders.Values.CLOSE.equalsIgnoreCase(request.headers().get(CONNECTION))
|
||||||
|| request.getProtocolVersion().equals(HttpVersion.HTTP_1_0)
|
|| request.protocolVersion().equals(HttpVersion.HTTP_1_0)
|
||||||
&& !HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(request.getHeader(CONNECTION));
|
&& !HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(request.headers().get(CONNECTION));
|
||||||
|
|
||||||
// Build the response object.
|
// Build the response object.
|
||||||
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
|
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||||
response.setContent(buf);
|
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf);
|
||||||
response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
|
response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||||
|
|
||||||
if (!close) {
|
if (!close) {
|
||||||
// There's no need to add 'Content-Length' header
|
// There's no need to add 'Content-Length' header
|
||||||
// if this is the last response.
|
// if this is the last response.
|
||||||
response.setHeader(CONTENT_LENGTH, String.valueOf(buf.readableBytes()));
|
response.headers().set(CONTENT_LENGTH, String.valueOf(buf.readableBytes()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<Cookie> cookies;
|
Set<Cookie> cookies;
|
||||||
String value = request.getHeader(COOKIE);
|
String value = request.headers().get(COOKIE);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
cookies = Collections.emptySet();
|
cookies = Collections.emptySet();
|
||||||
} else {
|
} else {
|
||||||
|
@ -325,7 +325,7 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
|
||||||
if (!cookies.isEmpty()) {
|
if (!cookies.isEmpty()) {
|
||||||
// Reset the cookies if necessary.
|
// Reset the cookies if necessary.
|
||||||
for (Cookie cookie : cookies) {
|
for (Cookie cookie : cookies) {
|
||||||
response.addHeader(SET_COOKIE, ServerCookieEncoder.encode(cookie));
|
response.headers().add(SET_COOKIE, ServerCookieEncoder.encode(cookie));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Write the response.
|
// Write the response.
|
||||||
|
@ -410,10 +410,12 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
|
||||||
|
|
||||||
ByteBuf buf = copiedBuffer(responseContent.toString(), CharsetUtil.UTF_8);
|
ByteBuf buf = copiedBuffer(responseContent.toString(), CharsetUtil.UTF_8);
|
||||||
// Build the response object.
|
// Build the response object.
|
||||||
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
|
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||||
response.setContent(buf);
|
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf);
|
||||||
response.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8");
|
|
||||||
response.setHeader(CONTENT_LENGTH, String.valueOf(buf.readableBytes()));
|
response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
|
||||||
|
response.headers().set(CONTENT_LENGTH, String.valueOf(buf.readableBytes()));
|
||||||
|
|
||||||
// Write the response.
|
// Write the response.
|
||||||
ctx.channel().write(response);
|
ctx.channel().write(response);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,10 @@ import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
|
||||||
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
|
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
|
||||||
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
|
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
|
||||||
import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame;
|
import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame;
|
||||||
|
@ -52,23 +52,23 @@ public class AutobahnServerHandler extends ChannelInboundMessageHandlerAdapter<O
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
if (msg instanceof HttpRequest) {
|
if (msg instanceof FullHttpRequest) {
|
||||||
handleHttpRequest(ctx, (HttpRequest) msg);
|
handleHttpRequest(ctx, (FullHttpRequest) msg);
|
||||||
} else if (msg instanceof WebSocketFrame) {
|
} else if (msg instanceof WebSocketFrame) {
|
||||||
handleWebSocketFrame(ctx, (WebSocketFrame) msg);
|
handleWebSocketFrame(ctx, (WebSocketFrame) msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) throws Exception {
|
private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
|
||||||
// Handle a bad request.
|
// Handle a bad request.
|
||||||
if (!req.getDecoderResult().isSuccess()) {
|
if (!req.decoderResult().isSuccess()) {
|
||||||
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, BAD_REQUEST));
|
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow only GET methods.
|
// Allow only GET methods.
|
||||||
if (req.getMethod() != GET) {
|
if (req.method() != GET) {
|
||||||
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN));
|
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,16 +112,17 @@ public class AutobahnServerHandler extends ChannelInboundMessageHandlerAdapter<O
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) {
|
private static void sendHttpResponse(
|
||||||
|
ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {
|
||||||
// Generate an error page if response status code is not OK (200).
|
// Generate an error page if response status code is not OK (200).
|
||||||
if (res.getStatus().getCode() != 200) {
|
if (res.status().code() != 200) {
|
||||||
res.setContent(Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8));
|
res.data().writeBytes(Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8));
|
||||||
setContentLength(res, res.getContent().readableBytes());
|
setContentLength(res, res.data().readableBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the response and close the connection if necessary.
|
// Send the response and close the connection if necessary.
|
||||||
ChannelFuture f = ctx.channel().write(res);
|
ChannelFuture f = ctx.channel().write(res);
|
||||||
if (!isKeepAlive(req) || res.getStatus().getCode() != 200) {
|
if (!isKeepAlive(req) || res.status().code() != 200) {
|
||||||
f.addListener(ChannelFutureListener.CLOSE);
|
f.addListener(ChannelFutureListener.CLOSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,7 +133,7 @@ public class AutobahnServerHandler extends ChannelInboundMessageHandlerAdapter<O
|
||||||
ctx.close();
|
ctx.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getWebSocketLocation(HttpRequest req) {
|
private static String getWebSocketLocation(FullHttpRequest req) {
|
||||||
return "ws://" + req.getHeader(HttpHeaders.Names.HOST);
|
return "ws://" + req.headers().get(HttpHeaders.Names.HOST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.socket.SocketChannel;
|
import io.netty.channel.socket.SocketChannel;
|
||||||
import io.netty.channel.socket.nio.NioEventLoopGroup;
|
import io.netty.channel.socket.nio.NioEventLoopGroup;
|
||||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||||
|
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||||
import io.netty.handler.codec.http.HttpRequestEncoder;
|
import io.netty.handler.codec.http.HttpRequestEncoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseDecoder;
|
import io.netty.handler.codec.http.HttpResponseDecoder;
|
||||||
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
|
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
|
||||||
|
@ -91,6 +92,7 @@ public class WebSocketClient {
|
||||||
ChannelPipeline pipeline = ch.pipeline();
|
ChannelPipeline pipeline = ch.pipeline();
|
||||||
pipeline.addLast("decoder", new HttpResponseDecoder());
|
pipeline.addLast("decoder", new HttpResponseDecoder());
|
||||||
pipeline.addLast("encoder", new HttpRequestEncoder());
|
pipeline.addLast("encoder", new HttpRequestEncoder());
|
||||||
|
pipeline.addLast("aggregator", new HttpObjectAggregator(8192));
|
||||||
pipeline.addLast("ws-handler", handler);
|
pipeline.addLast("ws-handler", handler);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -101,7 +103,7 @@ public class WebSocketClient {
|
||||||
|
|
||||||
// Send 10 messages and wait for responses
|
// Send 10 messages and wait for responses
|
||||||
System.out.println("WebSocket Client sending message");
|
System.out.println("WebSocket Client sending message");
|
||||||
for (int i = 0; i < 1; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
ch.write(new TextWebSocketFrame("Message #" + i));
|
ch.write(new TextWebSocketFrame("Message #" + i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.channel.ChannelPromise;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
|
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
|
||||||
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
|
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
|
||||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||||
|
@ -82,16 +82,16 @@ public class WebSocketClientHandler extends ChannelInboundMessageHandlerAdapter<
|
||||||
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
Channel ch = ctx.channel();
|
Channel ch = ctx.channel();
|
||||||
if (!handshaker.isHandshakeComplete()) {
|
if (!handshaker.isHandshakeComplete()) {
|
||||||
handshaker.finishHandshake(ch, (HttpResponse) msg);
|
handshaker.finishHandshake(ch, (FullHttpResponse) msg);
|
||||||
System.out.println("WebSocket Client connected!");
|
System.out.println("WebSocket Client connected!");
|
||||||
handshakeFuture.setSuccess();
|
handshakeFuture.setSuccess();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg instanceof HttpResponse) {
|
if (msg instanceof FullHttpResponse) {
|
||||||
HttpResponse response = (HttpResponse) msg;
|
FullHttpResponse response = (FullHttpResponse) msg;
|
||||||
throw new Exception("Unexpected HttpResponse (status=" + response.getStatus() + ", content="
|
throw new Exception("Unexpected FullHttpResponse (status=" + response.status() + ", content="
|
||||||
+ response.getContent().toString(CharsetUtil.UTF_8) + ')');
|
+ response.data().toString(CharsetUtil.UTF_8) + ')');
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketFrame frame = (WebSocketFrame) msg;
|
WebSocketFrame frame = (WebSocketFrame) msg;
|
||||||
|
|
|
@ -21,9 +21,9 @@ import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
|
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
|
||||||
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
|
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
|
||||||
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
|
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
|
||||||
|
@ -53,41 +53,39 @@ public class WebSocketServerHandler extends ChannelInboundMessageHandlerAdapter<
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
if (msg instanceof HttpRequest) {
|
if (msg instanceof FullHttpRequest) {
|
||||||
handleHttpRequest(ctx, (HttpRequest) msg);
|
handleHttpRequest(ctx, (FullHttpRequest) msg);
|
||||||
} else if (msg instanceof WebSocketFrame) {
|
} else if (msg instanceof WebSocketFrame) {
|
||||||
handleWebSocketFrame(ctx, (WebSocketFrame) msg);
|
handleWebSocketFrame(ctx, (WebSocketFrame) msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) throws Exception {
|
private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
|
||||||
// Handle a bad request.
|
// Handle a bad request.
|
||||||
if (!req.getDecoderResult().isSuccess()) {
|
if (!req.decoderResult().isSuccess()) {
|
||||||
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, BAD_REQUEST));
|
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow only GET methods.
|
// Allow only GET methods.
|
||||||
if (req.getMethod() != GET) {
|
if (req.method() != GET) {
|
||||||
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN));
|
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the demo page and favicon.ico
|
// Send the demo page and favicon.ico
|
||||||
if ("/".equals(req.getUri())) {
|
if ("/".equals(req.uri())) {
|
||||||
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, OK);
|
|
||||||
|
|
||||||
ByteBuf content = WebSocketServerIndexPage.getContent(getWebSocketLocation(req));
|
ByteBuf content = WebSocketServerIndexPage.getContent(getWebSocketLocation(req));
|
||||||
|
FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, OK, content);
|
||||||
|
|
||||||
res.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8");
|
res.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
|
||||||
setContentLength(res, content.readableBytes());
|
setContentLength(res, content.readableBytes());
|
||||||
|
|
||||||
res.setContent(content);
|
|
||||||
sendHttpResponse(ctx, req, res);
|
sendHttpResponse(ctx, req, res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ("/favicon.ico".equals(req.getUri())) {
|
if ("/favicon.ico".equals(req.uri())) {
|
||||||
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, NOT_FOUND);
|
FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND);
|
||||||
sendHttpResponse(ctx, req, res);
|
sendHttpResponse(ctx, req, res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -127,16 +125,17 @@ public class WebSocketServerHandler extends ChannelInboundMessageHandlerAdapter<
|
||||||
ctx.channel().write(new TextWebSocketFrame(request.toUpperCase()));
|
ctx.channel().write(new TextWebSocketFrame(request.toUpperCase()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) {
|
private static void sendHttpResponse(
|
||||||
|
ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {
|
||||||
// Generate an error page if response status code is not OK (200).
|
// Generate an error page if response status code is not OK (200).
|
||||||
if (res.getStatus().getCode() != 200) {
|
if (res.status().code() != 200) {
|
||||||
res.setContent(Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8));
|
res.data().writeBytes(Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8));
|
||||||
setContentLength(res, res.getContent().readableBytes());
|
setContentLength(res, res.data().readableBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the response and close the connection if necessary.
|
// Send the response and close the connection if necessary.
|
||||||
ChannelFuture f = ctx.channel().write(res);
|
ChannelFuture f = ctx.channel().write(res);
|
||||||
if (!isKeepAlive(req) || res.getStatus().getCode() != 200) {
|
if (!isKeepAlive(req) || res.status().code() != 200) {
|
||||||
f.addListener(ChannelFutureListener.CLOSE);
|
f.addListener(ChannelFutureListener.CLOSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,7 +146,7 @@ public class WebSocketServerHandler extends ChannelInboundMessageHandlerAdapter<
|
||||||
ctx.close();
|
ctx.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getWebSocketLocation(HttpRequest req) {
|
private static String getWebSocketLocation(FullHttpRequest req) {
|
||||||
return "ws://" + req.getHeader(HOST) + WEBSOCKET_PATH;
|
return "ws://" + req.headers().get(HOST) + WEBSOCKET_PATH;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,9 +22,9 @@ import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||||
import io.netty.example.http.websocketx.server.WebSocketServerIndexPage;
|
import io.netty.example.http.websocketx.server.WebSocketServerIndexPage;
|
||||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
|
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
|
||||||
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
|
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
|
||||||
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
|
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
|
||||||
|
@ -54,42 +54,40 @@ public class WebSocketSslServerHandler extends ChannelInboundMessageHandlerAdapt
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
if (msg instanceof HttpRequest) {
|
if (msg instanceof FullHttpRequest) {
|
||||||
handleHttpRequest(ctx, (HttpRequest) msg);
|
handleHttpRequest(ctx, (FullHttpRequest) msg);
|
||||||
} else if (msg instanceof WebSocketFrame) {
|
} else if (msg instanceof WebSocketFrame) {
|
||||||
handleWebSocketFrame(ctx, (WebSocketFrame) msg);
|
handleWebSocketFrame(ctx, (WebSocketFrame) msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) throws Exception {
|
private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
|
||||||
// Handle a bad request.
|
// Handle a bad request.
|
||||||
if (!req.getDecoderResult().isSuccess()) {
|
if (!req.decoderResult().isSuccess()) {
|
||||||
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, BAD_REQUEST));
|
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow only GET methods.
|
// Allow only GET methods.
|
||||||
if (req.getMethod() != GET) {
|
if (req.method() != GET) {
|
||||||
sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN));
|
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the demo page and favicon.ico
|
// Send the demo page and favicon.ico
|
||||||
if ("/".equals(req.getUri())) {
|
if ("/".equals(req.uri())) {
|
||||||
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, OK);
|
|
||||||
|
|
||||||
ByteBuf content = WebSocketServerIndexPage.getContent(getWebSocketLocation(req));
|
ByteBuf content = WebSocketServerIndexPage.getContent(getWebSocketLocation(req));
|
||||||
|
FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, OK, content);
|
||||||
|
|
||||||
res.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8");
|
res.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
|
||||||
setContentLength(res, content.readableBytes());
|
setContentLength(res, content.readableBytes());
|
||||||
|
|
||||||
res.setContent(content);
|
|
||||||
sendHttpResponse(ctx, req, res);
|
sendHttpResponse(ctx, req, res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("/favicon.ico".equals(req.getUri())) {
|
if ("/favicon.ico".equals(req.uri())) {
|
||||||
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, NOT_FOUND);
|
FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND);
|
||||||
sendHttpResponse(ctx, req, res);
|
sendHttpResponse(ctx, req, res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -129,16 +127,17 @@ public class WebSocketSslServerHandler extends ChannelInboundMessageHandlerAdapt
|
||||||
ctx.channel().write(new TextWebSocketFrame(request.toUpperCase()));
|
ctx.channel().write(new TextWebSocketFrame(request.toUpperCase()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) {
|
private static void sendHttpResponse(
|
||||||
|
ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {
|
||||||
// Generate an error page if response status code is not OK (200).
|
// Generate an error page if response status code is not OK (200).
|
||||||
if (res.getStatus().getCode() != 200) {
|
if (res.status().code() != 200) {
|
||||||
res.setContent(Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8));
|
res.data().writeBytes(Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8));
|
||||||
setContentLength(res, res.getContent().readableBytes());
|
setContentLength(res, res.data().readableBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the response and close the connection if necessary.
|
// Send the response and close the connection if necessary.
|
||||||
ChannelFuture f = ctx.channel().write(res);
|
ChannelFuture f = ctx.channel().write(res);
|
||||||
if (!isKeepAlive(req) || res.getStatus().getCode() != 200) {
|
if (!isKeepAlive(req) || res.status().code() != 200) {
|
||||||
f.addListener(ChannelFutureListener.CLOSE);
|
f.addListener(ChannelFutureListener.CLOSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,7 +148,7 @@ public class WebSocketSslServerHandler extends ChannelInboundMessageHandlerAdapt
|
||||||
ctx.close();
|
ctx.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getWebSocketLocation(HttpRequest req) {
|
private static String getWebSocketLocation(FullHttpRequest req) {
|
||||||
return "wss://" + req.getHeader(HOST) + WEBSOCKET_PATH;
|
return "wss://" + req.headers().get(HOST) + WEBSOCKET_PATH;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user