下面列出了怎么用 io.netty.handler.codec.http.HttpHeaders.Names 的API类实例代码及写法,或者点击链接到github查看源代码。
/**
* <p>
* Process server response:
* </p>
*
* <pre>
* HTTP/1.1 101 Switching Protocols
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
* Sec-WebSocket-Protocol: chat
* </pre>
*
* @param response
* HTTP response returned from the server for the request sent by beginOpeningHandshake00().
* @throws WebSocketHandshakeException
*/
@Override
protected void verify(FullHttpResponse response) {
final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
final HttpHeaders headers = response.headers();
if (!response.getStatus().equals(status)) {
throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.getStatus());
}
String upgrade = headers.get(Names.UPGRADE);
if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade);
}
String connection = headers.get(Names.CONNECTION);
if (!Values.UPGRADE.equalsIgnoreCase(connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection);
}
String accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT);
if (accept == null || !accept.equals(expectedChallengeResponseString)) {
throw new WebSocketHandshakeException(String.format(
"Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString));
}
}
/**
* Instances a new handshaker
*
* @return A new WebSocketServerHandshaker for the requested web socket version. Null if web
* socket version is not supported.
*/
public WebSocketServerHandshaker newHandshaker(HttpRequest req) {
String version = req.headers().get(Names.SEC_WEBSOCKET_VERSION);
if (version != null) {
if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) {
// Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification).
return new WebSocketServerHandshaker13(
webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength);
} else if (version.equals(WebSocketVersion.V08.toHttpHeaderValue())) {
// Version 8 of the wire protocol - version 10 of the draft hybi specification.
return new WebSocketServerHandshaker08(
webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength);
} else if (version.equals(WebSocketVersion.V07.toHttpHeaderValue())) {
// Version 8 of the wire protocol - version 07 of the draft hybi specification.
return new WebSocketServerHandshaker07(
webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength);
} else {
return null;
}
} else {
// Assume version 00 where version header was not specified
return new WebSocketServerHandshaker00(webSocketURL, subprotocols, maxFramePayloadLength);
}
}
/**
* <p>
* Process server response:
* </p>
*
* <pre>
* HTTP/1.1 101 Switching Protocols
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
* Sec-WebSocket-Protocol: chat
* </pre>
*
* @param response
* HTTP response returned from the server for the request sent by beginOpeningHandshake00().
* @throws WebSocketHandshakeException
*/
@Override
protected void verify(FullHttpResponse response) {
final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
final HttpHeaders headers = response.headers();
if (!response.getStatus().equals(status)) {
throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.getStatus());
}
String upgrade = headers.get(Names.UPGRADE);
if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade);
}
String connection = headers.get(Names.CONNECTION);
if (!Values.UPGRADE.equalsIgnoreCase(connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection);
}
String accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT);
if (accept == null || !accept.equals(expectedChallengeResponseString)) {
throw new WebSocketHandshakeException(String.format(
"Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString));
}
}
/**
* <p>
* Process server response:
* </p>
*
* <pre>
* HTTP/1.1 101 Switching Protocols
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
* Sec-WebSocket-Protocol: chat
* </pre>
*
* @param response
* HTTP response returned from the server for the request sent by beginOpeningHandshake00().
* @throws WebSocketHandshakeException
*/
@Override
protected void verify(FullHttpResponse response) {
final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
final HttpHeaders headers = response.headers();
if (!response.getStatus().equals(status)) {
throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.getStatus());
}
String upgrade = headers.get(Names.UPGRADE);
if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade);
}
String connection = headers.get(Names.CONNECTION);
if (!Values.UPGRADE.equalsIgnoreCase(connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection);
}
String accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT);
if (accept == null || !accept.equals(expectedChallengeResponseString)) {
throw new WebSocketHandshakeException(String.format(
"Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString));
}
}
@Test
public void testFullContent() throws Exception {
EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder());
ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"));
FullHttpResponse res = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(new byte[42]));
res.headers().set(Names.CONTENT_LENGTH, 42);
ch.writeOutbound(res);
assertEncodedResponse(ch);
HttpContent c = (HttpContent) ch.readOutbound();
assertThat(c.content().readableBytes(), is(2));
assertThat(c.content().toString(CharsetUtil.US_ASCII), is("42"));
c.release();
LastHttpContent last = (LastHttpContent) ch.readOutbound();
assertThat(last.content().readableBytes(), is(0));
last.release();
assertThat(ch.readOutbound(), is(nullValue()));
}
/**
* If the length of the content is 0 for sure, {@link HttpContentEncoder} should skip encoding.
*/
@Test
public void testEmptyFullContent() throws Exception {
EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder());
ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"));
FullHttpResponse res = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.EMPTY_BUFFER);
ch.writeOutbound(res);
Object o = ch.readOutbound();
assertThat(o, is(instanceOf(FullHttpResponse.class)));
res = (FullHttpResponse) o;
assertThat(res.headers().get(Names.TRANSFER_ENCODING), is(nullValue()));
// Content encoding shouldn't be modified.
assertThat(res.headers().get(Names.CONTENT_ENCODING), is(nullValue()));
assertThat(res.content().readableBytes(), is(0));
assertThat(res.content().toString(CharsetUtil.US_ASCII), is(""));
res.release();
assertThat(ch.readOutbound(), is(nullValue()));
}
@Test
public void testEmptyFullContentWithTrailer() throws Exception {
EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder());
ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"));
FullHttpResponse res = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.EMPTY_BUFFER);
res.trailingHeaders().set("X-Test", "Netty");
ch.writeOutbound(res);
Object o = ch.readOutbound();
assertThat(o, is(instanceOf(FullHttpResponse.class)));
res = (FullHttpResponse) o;
assertThat(res.headers().get(Names.TRANSFER_ENCODING), is(nullValue()));
// Content encoding shouldn't be modified.
assertThat(res.headers().get(Names.CONTENT_ENCODING), is(nullValue()));
assertThat(res.content().readableBytes(), is(0));
assertThat(res.content().toString(CharsetUtil.US_ASCII), is(""));
assertEquals("Netty", res.trailingHeaders().get("X-Test"));
assertThat(ch.readOutbound(), is(nullValue()));
}
public FullHttpRequest build() {
FullHttpRequest req = new DefaultFullHttpRequest(httpVersion, method, uri);
HttpHeaders headers = req.headers();
if (host != null) {
headers.set(Names.HOST, host);
}
if (upgrade != null) {
headers.set(Names.UPGRADE, upgrade);
}
if (connection != null) {
headers.set(Names.CONNECTION, connection);
}
if (key != null) {
headers.set(Names.SEC_WEBSOCKET_KEY, key);
}
if (origin != null) {
headers.set(Names.SEC_WEBSOCKET_ORIGIN, origin);
}
if (version != null) {
headers.set(Names.SEC_WEBSOCKET_VERSION, version.toHttpHeaderValue());
}
return req;
}
@Test
public void testPrematureClosureWithChunkedEncoding1() throws Exception {
EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder());
ch.writeInbound(
Unpooled.copiedBuffer("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n", CharsetUtil.US_ASCII));
// Read the response headers.
HttpResponse res = (HttpResponse) ch.readInbound();
assertThat(res.getProtocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
assertThat(res.getStatus(), is(HttpResponseStatus.OK));
assertThat(res.headers().get(Names.TRANSFER_ENCODING), is("chunked"));
assertThat(ch.readInbound(), is(nullValue()));
// Close the connection without sending anything.
ch.finish();
// The decoder should not generate the last chunk because it's closed prematurely.
assertThat(ch.readInbound(), is(nullValue()));
}
@Test
public void testFullContent() throws Exception {
EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor());
ch.writeInbound(newRequest());
FullHttpResponse res = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
Unpooled.copiedBuffer("Hello, World", CharsetUtil.US_ASCII));
res.headers().set(Names.CONTENT_LENGTH, res.content().readableBytes());
ch.writeOutbound(res);
assertEncodedResponse(ch);
HttpContent c = (HttpContent) ch.readOutbound();
assertThat(ByteBufUtil.hexDump(c.content()), is("1f8b0800000000000000f248cdc9c9d75108cf2fca4901000000ffff"));
c.release();
c = (HttpContent) ch.readOutbound();
assertThat(ByteBufUtil.hexDump(c.content()), is("0300c6865b260c000000"));
c.release();
LastHttpContent last = (LastHttpContent) ch.readOutbound();
assertThat(last.content().readableBytes(), is(0));
last.release();
assertThat(ch.readOutbound(), is(nullValue()));
}
/**
* If the length of the content is 0 for sure, {@link HttpContentEncoder} should skip encoding.
*/
@Test
public void testEmptyFullContent() throws Exception {
EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor());
ch.writeInbound(newRequest());
FullHttpResponse res = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.EMPTY_BUFFER);
ch.writeOutbound(res);
Object o = ch.readOutbound();
assertThat(o, is(instanceOf(FullHttpResponse.class)));
res = (FullHttpResponse) o;
assertThat(res.headers().get(Names.TRANSFER_ENCODING), is(nullValue()));
// Content encoding shouldn't be modified.
assertThat(res.headers().get(Names.CONTENT_ENCODING), is(nullValue()));
assertThat(res.content().readableBytes(), is(0));
assertThat(res.content().toString(CharsetUtil.US_ASCII), is(""));
res.release();
assertThat(ch.readOutbound(), is(nullValue()));
}
@Test
public void testEmptyFullContentWithTrailer() throws Exception {
EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor());
ch.writeInbound(newRequest());
FullHttpResponse res = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.EMPTY_BUFFER);
res.trailingHeaders().set("X-Test", "Netty");
ch.writeOutbound(res);
Object o = ch.readOutbound();
assertThat(o, is(instanceOf(FullHttpResponse.class)));
res = (FullHttpResponse) o;
assertThat(res.headers().get(Names.TRANSFER_ENCODING), is(nullValue()));
// Content encoding shouldn't be modified.
assertThat(res.headers().get(Names.CONTENT_ENCODING), is(nullValue()));
assertThat(res.content().readableBytes(), is(0));
assertThat(res.content().toString(CharsetUtil.US_ASCII), is(""));
assertEquals("Netty", res.trailingHeaders().get("X-Test"));
assertThat(ch.readOutbound(), is(nullValue()));
}
private void processHead(ChannelHandlerContext context, FullHttpRequest request) {
HttpHeaders headers = request.headers();
FullHttpResponse response = null;
if (headers.contains(Names.CONTENT_LENGTH)) {
try {
File file = getRequestedFile(request.getUri());
response = new DefaultFullHttpResponse(
HTTP_1_1,
request.getDecoderResult().isSuccess() ? OK : BAD_REQUEST
);
HttpHeaders.setContentLength(response, file.length());
} catch (FileNotFoundException | URISyntaxException e) {
response = getBadRequest(e.getMessage());
}
}
context.writeAndFlush(response);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
BackupState backupState = BackupState.ERROR;
if (msg instanceof FullHttpRequest) {
FullHttpRequest req = (FullHttpRequest) msg;
if (req.getUri().equals("/chronos/backup")) {
backupState = BackupDB.backup();
LOGGER.info("backupState:{}", backupState.getDesc());
RestoreState restoreState = BackupDB.restore();
LOGGER.info("restoreState:{}", restoreState.getDesc());
}
} else {
LOGGER.error("request is not FullHttpRequest");
}
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.wrappedBuffer(backupState.getDesc().getBytes("utf-8")));
response.headers().set(Names.CONTENT_TYPE, "text/plain;charset=UTF-8");
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes());
response.headers().set(Names.CONNECTION, Values.KEEP_ALIVE);
ctx.write(response);
ctx.flush();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
BackupState backupState = BackupState.ERROR;
if (msg instanceof FullHttpRequest) {
FullHttpRequest req = (FullHttpRequest) msg;
if (req.getUri().equals("/chronos/backup")) {
backupState = BackupDB.backup();
LOGGER.info("backupState:{}", backupState.getDesc());
RestoreState restoreState = BackupDB.restore();
LOGGER.info("restoreState:{}", restoreState.getDesc());
}
} else {
LOGGER.error("request is not FullHttpRequest");
}
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.wrappedBuffer(backupState.getDesc().getBytes("utf-8")));
response.headers().set(Names.CONTENT_TYPE, "text/plain;charset=UTF-8");
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes());
response.headers().set(Names.CONNECTION, Values.KEEP_ALIVE);
ctx.write(response);
ctx.flush();
}
@Override
public Enumeration<Locale> getLocales() {
Collection<Locale> locales = Utils.parseAcceptLanguageHeader(HttpHeaders.getHeader(this.request,
Names.ACCEPT_LANGUAGE));
if (locales == null || locales.isEmpty()) {
locales = new ArrayList<Locale>();
locales.add(Locale.getDefault());
}
return Utils.enumeration(locales);
}
@Override
protected void decode(ChannelHandlerContext ctx, HttpRequest msg, List<Object> out)
throws Exception {
String acceptedEncoding = msg.headers().get(HttpHeaders.Names.ACCEPT_ENCODING);
if (acceptedEncoding == null) {
acceptedEncoding = HttpHeaders.Values.IDENTITY;
}
acceptEncodingQueue.add(acceptedEncoding);
out.add(ReferenceCountUtil.retain(msg));
}
/**
* <p>
* Handle the web socket handshake for the web socket specification <a href=
* "http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17">HyBi versions 13-17</a>. Versions 13-17
* share the same wire protocol.
* </p>
*
* <p>
* Browser request to the server:
* </p>
*
* <pre>
* GET /chat HTTP/1.1
* Host: server.example.com
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
* Sec-WebSocket-Origin: http://example.com
* Sec-WebSocket-Protocol: chat, superchat
* Sec-WebSocket-Version: 13
* </pre>
*
* <p>
* Server response:
* </p>
*
* <pre>
* HTTP/1.1 101 Switching Protocols
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
* Sec-WebSocket-Protocol: chat
* </pre>
*/
@Override
protected FullHttpResponse newHandshakeResponse(FullHttpRequest req, HttpHeaders headers) {
FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS);
if (headers != null) {
res.headers().add(headers);
}
String key = req.headers().get(Names.SEC_WEBSOCKET_KEY);
if (key == null) {
throw new WebSocketHandshakeException("not a WebSocket request: missing key");
}
String acceptSeed = key + WEBSOCKET_13_ACCEPT_GUID;
byte[] sha1 = WebSocketUtil.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII));
String accept = WebSocketUtil.base64(sha1);
if (logger.isDebugEnabled()) {
logger.debug("WebSocket version 13 server handshake key: {}, response: {}", key, accept);
}
res.headers().add(Names.UPGRADE, WEBSOCKET.toLowerCase());
res.headers().add(Names.CONNECTION, Names.UPGRADE);
res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept);
String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
if (subprotocols != null) {
String selectedSubprotocol = selectSubprotocol(subprotocols);
if (selectedSubprotocol == null) {
if (logger.isDebugEnabled()) {
logger.debug("Requested subprotocol(s) not supported: {}", subprotocols);
}
} else {
res.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
}
}
return res;
}
/**
* <p>
* Handle the web socket handshake for the web socket specification <a href=
* "http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-07">HyBi version 7</a>.
* </p>
*
* <p>
* Browser request to the server:
* </p>
*
* <pre>
* GET /chat HTTP/1.1
* Host: server.example.com
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
* Sec-WebSocket-Origin: http://example.com
* Sec-WebSocket-Protocol: chat, superchat
* Sec-WebSocket-Version: 7
* </pre>
*
* <p>
* Server response:
* </p>
*
* <pre>
* HTTP/1.1 101 Switching Protocols
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
* Sec-WebSocket-Protocol: chat
* </pre>
*/
@Override
protected FullHttpResponse newHandshakeResponse(FullHttpRequest req, HttpHeaders headers) {
FullHttpResponse res =
new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS);
if (headers != null) {
res.headers().add(headers);
}
String key = req.headers().get(Names.SEC_WEBSOCKET_KEY);
if (key == null) {
throw new WebSocketHandshakeException("not a WebSocket request: missing key");
}
String acceptSeed = key + WEBSOCKET_07_ACCEPT_GUID;
byte[] sha1 = WebSocketUtil.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII));
String accept = WebSocketUtil.base64(sha1);
if (logger.isDebugEnabled()) {
logger.debug("WebSocket version 07 server handshake key: {}, response: {}.", key, accept);
}
res.headers().add(Names.UPGRADE, WEBSOCKET.toLowerCase());
res.headers().add(Names.CONNECTION, Names.UPGRADE);
res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept);
String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
if (subprotocols != null) {
String selectedSubprotocol = selectSubprotocol(subprotocols);
if (selectedSubprotocol == null) {
if (logger.isDebugEnabled()) {
logger.debug("Requested subprotocol(s) not supported: {}", subprotocols);
}
} else {
res.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
}
}
return res;
}
/**
* <p>
* Handle the web socket handshake for the web socket specification <a href=
* "http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-08">HyBi version 8 to 10</a>. Version 8, 9 and
* 10 share the same wire protocol.
* </p>
*
* <p>
* Browser request to the server:
* </p>
*
* <pre>
* GET /chat HTTP/1.1
* Host: server.example.com
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
* Sec-WebSocket-Origin: http://example.com
* Sec-WebSocket-Protocol: chat, superchat
* Sec-WebSocket-Version: 8
* </pre>
*
* <p>
* Server response:
* </p>
*
* <pre>
* HTTP/1.1 101 Switching Protocols
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
* Sec-WebSocket-Protocol: chat
* </pre>
*/
@Override
protected FullHttpResponse newHandshakeResponse(FullHttpRequest req, HttpHeaders headers) {
FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS);
if (headers != null) {
res.headers().add(headers);
}
String key = req.headers().get(Names.SEC_WEBSOCKET_KEY);
if (key == null) {
throw new WebSocketHandshakeException("not a WebSocket request: missing key");
}
String acceptSeed = key + WEBSOCKET_08_ACCEPT_GUID;
byte[] sha1 = WebSocketUtil.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII));
String accept = WebSocketUtil.base64(sha1);
if (logger.isDebugEnabled()) {
logger.debug("WebSocket version 08 server handshake key: {}, response: {}", key, accept);
}
res.headers().add(Names.UPGRADE, WEBSOCKET.toLowerCase());
res.headers().add(Names.CONNECTION, Names.UPGRADE);
res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept);
String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL);
if (subprotocols != null) {
String selectedSubprotocol = selectSubprotocol(subprotocols);
if (selectedSubprotocol == null) {
if (logger.isDebugEnabled()) {
logger.debug("Requested subprotocol(s) not supported: {}", subprotocols);
}
} else {
res.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
}
}
return res;
}
/**
* Return that we need cannot not support the web socket version
*/
public static ChannelFuture sendUnsupportedVersionResponse(Channel channel, ChannelPromise promise) {
HttpResponse res = new DefaultHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.UPGRADE_REQUIRED);
res.headers().set(Names.SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue());
return channel.write(res, promise);
}
/**
* <p>
* Process server response:
* </p>
*
* <pre>
* HTTP/1.1 101 WebSocket Protocol Handshake
* Upgrade: WebSocket
* Connection: Upgrade
* Sec-WebSocket-Origin: http://example.com
* Sec-WebSocket-Location: ws://example.com/demo
* Sec-WebSocket-Protocol: sample
*
* 8jKS'y:G*Co,Wxa-
* </pre>
*
* @param response
* HTTP response returned from the server for the request sent by beginOpeningHandshake00().
* @throws WebSocketHandshakeException
*/
@Override
protected void verify(FullHttpResponse response) {
final HttpResponseStatus status = new HttpResponseStatus(101, "WebSocket Protocol Handshake");
if (!response.getStatus().equals(status)) {
throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.getStatus());
}
HttpHeaders headers = response.headers();
String upgrade = headers.get(Names.UPGRADE);
if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
+ upgrade);
}
String connection = headers.get(Names.CONNECTION);
if (!Values.UPGRADE.equalsIgnoreCase(connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: "
+ connection);
}
ByteBuf challenge = response.content();
if (!challenge.equals(expectedChallengeResponseBytes)) {
throw new WebSocketHandshakeException("Invalid challenge");
}
}
/**
* Builds a {@link CorsConfig} with settings specified by previous method calls.
*
* @return {@link CorsConfig} the configured CorsConfig instance.
*/
public CorsConfig build() {
if (preflightHeaders.isEmpty() && !noPreflightHeaders) {
preflightHeaders.put(Names.DATE, new DateValueGenerator());
preflightHeaders.put(Names.CONTENT_LENGTH, new ConstantValueGenerator("0"));
}
return new CorsConfig(this);
}
@Test
public void testChunkedContent() throws Exception {
EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder());
ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"));
HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
res.headers().set(Names.TRANSFER_ENCODING, Values.CHUNKED);
ch.writeOutbound(res);
assertEncodedResponse(ch);
ch.writeOutbound(new DefaultHttpContent(Unpooled.wrappedBuffer(new byte[3])));
ch.writeOutbound(new DefaultHttpContent(Unpooled.wrappedBuffer(new byte[2])));
ch.writeOutbound(new DefaultLastHttpContent(Unpooled.wrappedBuffer(new byte[1])));
HttpContent chunk;
chunk = (HttpContent) ch.readOutbound();
assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("3"));
chunk.release();
chunk = (HttpContent) ch.readOutbound();
assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("2"));
chunk.release();
chunk = (HttpContent) ch.readOutbound();
assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("1"));
assertThat(chunk, is(instanceOf(HttpContent.class)));
chunk.release();
chunk = (HttpContent) ch.readOutbound();
assertThat(chunk.content().isReadable(), is(false));
assertThat(chunk, is(instanceOf(LastHttpContent.class)));
chunk.release();
assertThat(ch.readOutbound(), is(nullValue()));
}
@Test
public void testChunkedContentWithTrailingHeader() throws Exception {
EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder());
ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"));
HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
res.headers().set(Names.TRANSFER_ENCODING, Values.CHUNKED);
ch.writeOutbound(res);
assertEncodedResponse(ch);
ch.writeOutbound(new DefaultHttpContent(Unpooled.wrappedBuffer(new byte[3])));
ch.writeOutbound(new DefaultHttpContent(Unpooled.wrappedBuffer(new byte[2])));
LastHttpContent content = new DefaultLastHttpContent(Unpooled.wrappedBuffer(new byte[1]));
content.trailingHeaders().set("X-Test", "Netty");
ch.writeOutbound(content);
HttpContent chunk;
chunk = (HttpContent) ch.readOutbound();
assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("3"));
chunk.release();
chunk = (HttpContent) ch.readOutbound();
assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("2"));
chunk.release();
chunk = (HttpContent) ch.readOutbound();
assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("1"));
assertThat(chunk, is(instanceOf(HttpContent.class)));
chunk.release();
chunk = (HttpContent) ch.readOutbound();
assertThat(chunk.content().isReadable(), is(false));
assertThat(chunk, is(instanceOf(LastHttpContent.class)));
assertEquals("Netty", ((LastHttpContent) chunk).trailingHeaders().get("X-Test"));
chunk.release();
assertThat(ch.readOutbound(), is(nullValue()));
}
private static void assertEncodedResponse(EmbeddedChannel ch) {
Object o = ch.readOutbound();
assertThat(o, is(instanceOf(HttpResponse.class)));
HttpResponse res = (HttpResponse) o;
assertThat(res, is(not(instanceOf(HttpContent.class))));
assertThat(res.headers().get(Names.TRANSFER_ENCODING), is("chunked"));
assertThat(res.headers().get(Names.CONTENT_LENGTH), is(nullValue()));
assertThat(res.headers().get(Names.CONTENT_ENCODING), is("test"));
}
private static void testPerformOpeningHandshake0(boolean subProtocol) {
EmbeddedChannel ch = new EmbeddedChannel(
new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder());
FullHttpRequest req = ReferenceCountUtil.releaseLater(
new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat"));
req.headers().set(Names.HOST, "server.example.com");
req.headers().set(Names.UPGRADE, WEBSOCKET.toLowerCase());
req.headers().set(Names.CONNECTION, "Upgrade");
req.headers().set(Names.SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ==");
req.headers().set(Names.SEC_WEBSOCKET_ORIGIN, "http://example.com");
req.headers().set(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
req.headers().set(Names.SEC_WEBSOCKET_VERSION, "8");
if (subProtocol) {
new WebSocketServerHandshaker08(
"ws://example.com/chat", "chat", false, Integer.MAX_VALUE).handshake(ch, req);
} else {
new WebSocketServerHandshaker08(
"ws://example.com/chat", null, false, Integer.MAX_VALUE).handshake(ch, req);
}
ByteBuf resBuf = (ByteBuf) ch.readOutbound();
EmbeddedChannel ch2 = new EmbeddedChannel(new HttpResponseDecoder());
ch2.writeInbound(resBuf);
HttpResponse res = (HttpResponse) ch2.readInbound();
Assert.assertEquals(
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.headers().get(Names.SEC_WEBSOCKET_ACCEPT));
if (subProtocol) {
Assert.assertEquals("chat", res.headers().get(Names.SEC_WEBSOCKET_PROTOCOL));
} else {
Assert.assertNull(res.headers().get(Names.SEC_WEBSOCKET_PROTOCOL));
}
ReferenceCountUtil.release(res);
}
private static void testPerformOpeningHandshake0(boolean subProtocol) {
EmbeddedChannel ch = new EmbeddedChannel(
new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder());
FullHttpRequest req = ReferenceCountUtil.releaseLater(new DefaultFullHttpRequest(
HTTP_1_1, HttpMethod.GET, "/chat", Unpooled.copiedBuffer("^n:ds[4U", CharsetUtil.US_ASCII)));
req.headers().set(Names.HOST, "server.example.com");
req.headers().set(Names.UPGRADE, WEBSOCKET.toLowerCase());
req.headers().set(Names.CONNECTION, "Upgrade");
req.headers().set(Names.ORIGIN, "http://example.com");
req.headers().set(Names.SEC_WEBSOCKET_KEY1, "4 @1 46546xW%0l 1 5");
req.headers().set(Names.SEC_WEBSOCKET_KEY2, "12998 5 Y3 1 .P00");
req.headers().set(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
if (subProtocol) {
new WebSocketServerHandshaker00(
"ws://example.com/chat", "chat", Integer.MAX_VALUE).handshake(ch, req);
} else {
new WebSocketServerHandshaker00(
"ws://example.com/chat", null, Integer.MAX_VALUE).handshake(ch, req);
}
EmbeddedChannel ch2 = new EmbeddedChannel(new HttpResponseDecoder());
ch2.writeInbound(ch.readOutbound());
HttpResponse res = (HttpResponse) ch2.readInbound();
Assert.assertEquals("ws://example.com/chat", res.headers().get(Names.SEC_WEBSOCKET_LOCATION));
if (subProtocol) {
Assert.assertEquals("chat", res.headers().get(Names.SEC_WEBSOCKET_PROTOCOL));
} else {
Assert.assertNull(res.headers().get(Names.SEC_WEBSOCKET_PROTOCOL));
}
LastHttpContent content = (LastHttpContent) ch2.readInbound();
Assert.assertEquals("8jKS'y:G*Co,Wxa-", content.content().toString(CharsetUtil.US_ASCII));
content.release();
}
private static void testPerformOpeningHandshake0(boolean subProtocol) {
EmbeddedChannel ch = new EmbeddedChannel(
new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder());
FullHttpRequest req = ReferenceCountUtil.releaseLater(
new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat"));
req.headers().set(Names.HOST, "server.example.com");
req.headers().set(Names.UPGRADE, WEBSOCKET.toLowerCase());
req.headers().set(Names.CONNECTION, "Upgrade");
req.headers().set(Names.SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ==");
req.headers().set(Names.SEC_WEBSOCKET_ORIGIN, "http://example.com");
req.headers().set(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
req.headers().set(Names.SEC_WEBSOCKET_VERSION, "13");
if (subProtocol) {
new WebSocketServerHandshaker13(
"ws://example.com/chat", "chat", false, Integer.MAX_VALUE).handshake(ch, req);
} else {
new WebSocketServerHandshaker13(
"ws://example.com/chat", null, false, Integer.MAX_VALUE).handshake(ch, req);
}
ByteBuf resBuf = (ByteBuf) ch.readOutbound();
EmbeddedChannel ch2 = new EmbeddedChannel(new HttpResponseDecoder());
ch2.writeInbound(resBuf);
HttpResponse res = (HttpResponse) ch2.readInbound();
Assert.assertEquals(
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.headers().get(Names.SEC_WEBSOCKET_ACCEPT));
if (subProtocol) {
Assert.assertEquals("chat", res.headers().get(Names.SEC_WEBSOCKET_PROTOCOL));
} else {
Assert.assertNull(res.headers().get(Names.SEC_WEBSOCKET_PROTOCOL));
}
ReferenceCountUtil.release(res);
}
@Test
public void testPrematureClosureWithChunkedEncoding2() throws Exception {
EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder());
// Write the partial response.
ch.writeInbound(Unpooled.copiedBuffer(
"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n8\r\n12345678", CharsetUtil.US_ASCII));
// Read the response headers.
HttpResponse res = (HttpResponse) ch.readInbound();
assertThat(res.getProtocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
assertThat(res.getStatus(), is(HttpResponseStatus.OK));
assertThat(res.headers().get(Names.TRANSFER_ENCODING), is("chunked"));
// Read the partial content.
HttpContent content = (HttpContent) ch.readInbound();
assertThat(content.content().toString(CharsetUtil.US_ASCII), is("12345678"));
assertThat(content, is(not(instanceOf(LastHttpContent.class))));
content.release();
assertThat(ch.readInbound(), is(nullValue()));
// Close the connection.
ch.finish();
// The decoder should not generate the last chunk because it's closed prematurely.
assertThat(ch.readInbound(), is(nullValue()));
}