Adapt Http2ServerDowngrader to work with clients
Motivation: Http2ServerDowngrader is specifically built for server channels where inbound Http2StreamFrames are converted into HttpRequests, and outbound HttpResponses are converted into Http2StreamFrames. It can be easily made to be more generic to work with client channels where inbound Http2StreamFrames are converted into HttpResponses, and outbound HttpRequests are converted into Http2StreamFrames. Modification: - Renamed Http2ServerDowngrader to a more general Http2StreamFrameToHttpObjectCodec - Made it take in an "isServer" parameter to determine whether encoding inbound Http2StreamFrames should create HttpRequests (for server) or HttpResponses (for client) - Norman fixed a leak in the unit test. Thanks! :-) Result: Now Http2StreamFrameToHttpObjectCodec can be used to translate Http2StreamFrame to HttpObject for both server and client.
This commit is contained in:
parent
9d5daa37cc
commit
e069079aff
@ -16,46 +16,56 @@
|
||||
|
||||
package io.netty.handler.codec.http2;
|
||||
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageCodec;
|
||||
import io.netty.handler.codec.http.DefaultHttpContent;
|
||||
import io.netty.handler.codec.http.DefaultLastHttpContent;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpMessage;
|
||||
import io.netty.handler.codec.http.HttpContent;
|
||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||
import io.netty.handler.codec.http.HttpHeaderValues;
|
||||
import io.netty.handler.codec.http.HttpMessage;
|
||||
import io.netty.handler.codec.http.HttpObject;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpScheme;
|
||||
import io.netty.handler.codec.http.HttpUtil;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import io.netty.handler.codec.http.LastHttpContent;
|
||||
import io.netty.handler.ssl.SslHandler;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is a server-side adapter so that an http2 codec can be downgraded to
|
||||
* appear as if it's speaking http/1.1.
|
||||
* This handler converts from {@link Http2StreamFrame} to {@link HttpObject},
|
||||
* and back. It can be used as an adapter in conjunction with {@link
|
||||
* Http2MultiplexCodec} to make http/2 connections backward-compatible with
|
||||
* {@link ChannelHandler}s expecting {@link HttpObject}
|
||||
*
|
||||
* In particular, this handler converts from {@link Http2StreamFrame} to {@link
|
||||
* HttpObject}, and back. For simplicity, it converts to chunked encoding
|
||||
* unless the entire stream is a single header.
|
||||
* For simplicity, it converts to chunked encoding unless the entire stream
|
||||
* is a single header.
|
||||
*/
|
||||
@UnstableApi
|
||||
public class Http2ServerDowngrader extends MessageToMessageCodec<Http2StreamFrame, HttpObject> {
|
||||
|
||||
public class Http2StreamFrameToHttpObjectCodec extends MessageToMessageCodec<Http2StreamFrame, HttpObject> {
|
||||
private final boolean isServer;
|
||||
private final boolean validateHeaders;
|
||||
|
||||
public Http2ServerDowngrader(boolean validateHeaders) {
|
||||
private HttpScheme scheme;
|
||||
|
||||
public Http2StreamFrameToHttpObjectCodec(final boolean isServer,
|
||||
final boolean validateHeaders) {
|
||||
this.isServer = isServer;
|
||||
this.validateHeaders = validateHeaders;
|
||||
scheme = HttpScheme.HTTP;
|
||||
}
|
||||
|
||||
public Http2ServerDowngrader() {
|
||||
this(true);
|
||||
public Http2StreamFrameToHttpObjectCodec(final boolean isServer) {
|
||||
this(isServer, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -71,18 +81,17 @@ public class Http2ServerDowngrader extends MessageToMessageCodec<Http2StreamFram
|
||||
Http2Headers headers = headersFrame.headers();
|
||||
|
||||
if (headersFrame.isEndStream()) {
|
||||
if (headers.method() == null) {
|
||||
if (headers.method() == null && headers.status() == null) {
|
||||
LastHttpContent last = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, validateHeaders);
|
||||
HttpConversionUtil.addHttp2ToHttpHeaders(id, headers, last.trailingHeaders(),
|
||||
HttpVersion.HTTP_1_1, true, true);
|
||||
out.add(last);
|
||||
} else {
|
||||
FullHttpRequest full = HttpConversionUtil.toFullHttpRequest(id, headers, ctx.alloc(),
|
||||
validateHeaders);
|
||||
FullHttpMessage full = newFullMessage(id, headers, ctx.alloc());
|
||||
out.add(full);
|
||||
}
|
||||
} else {
|
||||
HttpRequest req = HttpConversionUtil.toHttpRequest(id, headersFrame.headers(), validateHeaders);
|
||||
HttpMessage req = newMessage(id, headers);
|
||||
if (!HttpUtil.isContentLengthSet(req)) {
|
||||
req.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED);
|
||||
}
|
||||
@ -91,18 +100,17 @@ public class Http2ServerDowngrader extends MessageToMessageCodec<Http2StreamFram
|
||||
} else if (frame instanceof Http2DataFrame) {
|
||||
Http2DataFrame dataFrame = (Http2DataFrame) frame;
|
||||
if (dataFrame.isEndStream()) {
|
||||
out.add(new DefaultLastHttpContent(dataFrame.content(), validateHeaders));
|
||||
out.add(new DefaultLastHttpContent(dataFrame.content().retain(), validateHeaders));
|
||||
} else {
|
||||
out.add(new DefaultHttpContent(dataFrame.content()));
|
||||
out.add(new DefaultHttpContent(dataFrame.content().retain()));
|
||||
}
|
||||
}
|
||||
ReferenceCountUtil.retain(frame);
|
||||
}
|
||||
|
||||
private void encodeLastContent(LastHttpContent last, List<Object> out) {
|
||||
boolean needFiller = !(last instanceof FullHttpResponse) && last.trailingHeaders().isEmpty();
|
||||
boolean needFiller = !(last instanceof FullHttpMessage) && last.trailingHeaders().isEmpty();
|
||||
if (last.content().isReadable() || needFiller) {
|
||||
out.add(new DefaultHttp2DataFrame(last.content(), last.trailingHeaders().isEmpty()));
|
||||
out.add(new DefaultHttp2DataFrame(last.content().retain(), last.trailingHeaders().isEmpty()));
|
||||
}
|
||||
if (!last.trailingHeaders().isEmpty()) {
|
||||
Http2Headers headers = HttpConversionUtil.toHttp2Headers(last.trailingHeaders(), validateHeaders);
|
||||
@ -112,11 +120,11 @@ public class Http2ServerDowngrader extends MessageToMessageCodec<Http2StreamFram
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, HttpObject obj, List<Object> out) throws Exception {
|
||||
if (obj instanceof HttpResponse) {
|
||||
Http2Headers headers = HttpConversionUtil.toHttp2Headers((HttpResponse) obj, validateHeaders);
|
||||
if (obj instanceof HttpMessage) {
|
||||
Http2Headers headers = toHttp2Headers((HttpMessage) obj);
|
||||
boolean noMoreFrames = false;
|
||||
if (obj instanceof FullHttpResponse) {
|
||||
FullHttpResponse full = (FullHttpResponse) obj;
|
||||
if (obj instanceof FullHttpMessage) {
|
||||
FullHttpMessage full = (FullHttpMessage) obj;
|
||||
noMoreFrames = !full.content().isReadable() && full.trailingHeaders().isEmpty();
|
||||
}
|
||||
|
||||
@ -128,8 +136,50 @@ public class Http2ServerDowngrader extends MessageToMessageCodec<Http2StreamFram
|
||||
encodeLastContent(last, out);
|
||||
} else if (obj instanceof HttpContent) {
|
||||
HttpContent cont = (HttpContent) obj;
|
||||
out.add(new DefaultHttp2DataFrame(cont.content(), false));
|
||||
out.add(new DefaultHttp2DataFrame(cont.content().retain(), false));
|
||||
}
|
||||
ReferenceCountUtil.retain(obj);
|
||||
}
|
||||
|
||||
private Http2Headers toHttp2Headers(final HttpMessage msg) {
|
||||
if (msg instanceof HttpRequest) {
|
||||
msg.headers().set(
|
||||
HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(),
|
||||
scheme.name());
|
||||
}
|
||||
|
||||
return HttpConversionUtil.toHttp2Headers(msg, validateHeaders);
|
||||
}
|
||||
|
||||
private HttpMessage newMessage(final int id,
|
||||
final Http2Headers headers) throws Http2Exception {
|
||||
return isServer ?
|
||||
HttpConversionUtil.toHttpRequest(id, headers, validateHeaders) :
|
||||
HttpConversionUtil.toHttpResponse(id, headers, validateHeaders);
|
||||
}
|
||||
|
||||
private FullHttpMessage newFullMessage(final int id,
|
||||
final Http2Headers headers,
|
||||
final ByteBufAllocator alloc) throws Http2Exception {
|
||||
return isServer ?
|
||||
HttpConversionUtil.toFullHttpRequest(id, headers, alloc, validateHeaders) :
|
||||
HttpConversionUtil.toFullHttpResponse(id, headers, alloc, validateHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlerAdded(final ChannelHandlerContext ctx) throws Exception {
|
||||
super.handlerAdded(ctx);
|
||||
|
||||
// this handler is typically used on an Http2StreamChannel. at this
|
||||
// stage, ssl handshake should've been established. checking for the
|
||||
// presence of SslHandler in the parent's channel pipeline to
|
||||
// determine the HTTP scheme should suffice, even for the case where
|
||||
// SniHandler is used.
|
||||
scheme = isSsl(ctx) ? HttpScheme.HTTPS : HttpScheme.HTTP;
|
||||
}
|
||||
|
||||
protected boolean isSsl(final ChannelHandlerContext ctx) {
|
||||
final Channel ch = ctx.channel();
|
||||
final Channel connChannel = (ch instanceof Http2StreamChannel) ? ch.parent() : ch;
|
||||
return null != connChannel.pipeline().get(SslHandler.class);
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.DefaultHttpRequest;
|
||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpMessage;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
@ -198,8 +199,8 @@ public final class HttpConversionUtil {
|
||||
* @return A new response object which represents headers/data
|
||||
* @throws Http2Exception see {@link #addHttp2ToHttpHeaders(int, Http2Headers, FullHttpMessage, boolean)}
|
||||
*/
|
||||
public static FullHttpResponse toHttpResponse(int streamId, Http2Headers http2Headers, ByteBufAllocator alloc,
|
||||
boolean validateHttpHeaders)
|
||||
public static FullHttpResponse toFullHttpResponse(int streamId, Http2Headers http2Headers, ByteBufAllocator alloc,
|
||||
boolean validateHttpHeaders)
|
||||
throws Http2Exception {
|
||||
HttpResponseStatus status = parseStatus(http2Headers.status());
|
||||
// HTTP/2 does not define a way to carry the version or reason phrase that is included in an
|
||||
@ -284,6 +285,36 @@ public final class HttpConversionUtil {
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new object to contain the response data.
|
||||
*
|
||||
* @param streamId The stream associated with the response
|
||||
* @param http2Headers The initial set of HTTP/2 headers to create the response with
|
||||
* @param validateHttpHeaders <ul>
|
||||
* <li>{@code true} to validate HTTP headers in the http-codec</li>
|
||||
* <li>{@code false} not to validate HTTP headers in the http-codec</li>
|
||||
* </ul>
|
||||
* @return A new response object which represents headers for a chunked response
|
||||
* @throws Http2Exception see {@link #addHttp2ToHttpHeaders(int, Http2Headers,
|
||||
* HttpHeaders, HttpVersion, boolean, boolean)}
|
||||
*/
|
||||
public static HttpResponse toHttpResponse(final int streamId,
|
||||
final Http2Headers http2Headers,
|
||||
final boolean validateHttpHeaders) throws Http2Exception {
|
||||
final HttpResponseStatus status = parseStatus(http2Headers.status());
|
||||
// HTTP/2 does not define a way to carry the version or reason phrase that is included in an
|
||||
// HTTP/1.1 status line.
|
||||
final HttpResponse msg = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status, validateHttpHeaders);
|
||||
try {
|
||||
addHttp2ToHttpHeaders(streamId, http2Headers, msg.headers(), msg.protocolVersion(), false, true);
|
||||
} catch (final Http2Exception e) {
|
||||
throw e;
|
||||
} catch (final Throwable t) {
|
||||
throw streamError(streamId, PROTOCOL_ERROR, t, "HTTP/2 to HTTP/1.x headers conversion error");
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate and add HTTP/2 headers to HTTP/1.x headers.
|
||||
*
|
||||
|
@ -155,7 +155,7 @@ public class InboundHttp2ToHttpAdapter extends Http2EventAdapter {
|
||||
ByteBufAllocator alloc)
|
||||
throws Http2Exception {
|
||||
return connection.isServer() ? HttpConversionUtil.toFullHttpRequest(stream.id(), headers, alloc,
|
||||
validateHttpHeaders) : HttpConversionUtil.toHttpResponse(stream.id(), headers, alloc,
|
||||
validateHttpHeaders) : HttpConversionUtil.toFullHttpResponse(stream.id(), headers, alloc,
|
||||
validateHttpHeaders);
|
||||
}
|
||||
|
||||
|
@ -1,387 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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.http2;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.DefaultHttpContent;
|
||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||
import io.netty.handler.codec.http.DefaultLastHttpContent;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpContent;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import io.netty.handler.codec.http.HttpUtil;
|
||||
import io.netty.handler.codec.http.LastHttpContent;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
public class Http2ServerDowngraderTest {
|
||||
|
||||
@Test
|
||||
public void testUpgradeEmptyFullResponse() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2ServerDowngrader());
|
||||
assertTrue(ch.writeOutbound(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)));
|
||||
|
||||
Http2HeadersFrame headersFrame = ch.readOutbound();
|
||||
assertThat(headersFrame.headers().status().toString(), is("200"));
|
||||
assertTrue(headersFrame.isEndStream());
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeNonEmptyFullResponse() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2ServerDowngrader());
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
assertTrue(ch.writeOutbound(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, hello)));
|
||||
|
||||
Http2HeadersFrame headersFrame = ch.readOutbound();
|
||||
assertThat(headersFrame.headers().status().toString(), is("200"));
|
||||
assertFalse(headersFrame.isEndStream());
|
||||
|
||||
Http2DataFrame dataFrame = ch.readOutbound();
|
||||
try {
|
||||
assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertTrue(dataFrame.isEndStream());
|
||||
} finally {
|
||||
dataFrame.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeEmptyFullResponseWithTrailers() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2ServerDowngrader());
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
|
||||
HttpHeaders trailers = response.trailingHeaders();
|
||||
trailers.set("key", "value");
|
||||
assertTrue(ch.writeOutbound(response));
|
||||
|
||||
Http2HeadersFrame headersFrame = ch.readOutbound();
|
||||
assertThat(headersFrame.headers().status().toString(), is("200"));
|
||||
assertFalse(headersFrame.isEndStream());
|
||||
|
||||
Http2HeadersFrame trailersFrame = ch.readOutbound();
|
||||
assertThat(trailersFrame.headers().get("key").toString(), is("value"));
|
||||
assertTrue(trailersFrame.isEndStream());
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeNonEmptyFullResponseWithTrailers() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2ServerDowngrader());
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, hello);
|
||||
HttpHeaders trailers = response.trailingHeaders();
|
||||
trailers.set("key", "value");
|
||||
assertTrue(ch.writeOutbound(response));
|
||||
|
||||
Http2HeadersFrame headersFrame = ch.readOutbound();
|
||||
assertThat(headersFrame.headers().status().toString(), is("200"));
|
||||
assertFalse(headersFrame.isEndStream());
|
||||
|
||||
Http2DataFrame dataFrame = ch.readOutbound();
|
||||
try {
|
||||
assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertFalse(dataFrame.isEndStream());
|
||||
} finally {
|
||||
dataFrame.release();
|
||||
}
|
||||
|
||||
Http2HeadersFrame trailersFrame = ch.readOutbound();
|
||||
assertThat(trailersFrame.headers().get("key").toString(), is("value"));
|
||||
assertTrue(trailersFrame.isEndStream());
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeHeaders() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2ServerDowngrader());
|
||||
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
|
||||
assertTrue(ch.writeOutbound(response));
|
||||
|
||||
Http2HeadersFrame headersFrame = ch.readOutbound();
|
||||
assertThat(headersFrame.headers().status().toString(), is("200"));
|
||||
assertFalse(headersFrame.isEndStream());
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeChunk() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2ServerDowngrader());
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
HttpContent content = new DefaultHttpContent(hello);
|
||||
assertTrue(ch.writeOutbound(content));
|
||||
|
||||
Http2DataFrame dataFrame = ch.readOutbound();
|
||||
try {
|
||||
assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertFalse(dataFrame.isEndStream());
|
||||
} finally {
|
||||
dataFrame.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeEmptyEnd() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2ServerDowngrader());
|
||||
LastHttpContent end = LastHttpContent.EMPTY_LAST_CONTENT;
|
||||
assertTrue(ch.writeOutbound(end));
|
||||
|
||||
Http2DataFrame emptyFrame = ch.readOutbound();
|
||||
try {
|
||||
assertThat(emptyFrame.content().readableBytes(), is(0));
|
||||
assertTrue(emptyFrame.isEndStream());
|
||||
} finally {
|
||||
emptyFrame.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeDataEnd() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2ServerDowngrader());
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
LastHttpContent end = new DefaultLastHttpContent(hello, true);
|
||||
assertTrue(ch.writeOutbound(end));
|
||||
|
||||
Http2DataFrame dataFrame = ch.readOutbound();
|
||||
try {
|
||||
assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertTrue(dataFrame.isEndStream());
|
||||
} finally {
|
||||
dataFrame.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeTrailers() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2ServerDowngrader());
|
||||
LastHttpContent trailers = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, true);
|
||||
HttpHeaders headers = trailers.trailingHeaders();
|
||||
headers.set("key", "value");
|
||||
assertTrue(ch.writeOutbound(trailers));
|
||||
|
||||
Http2HeadersFrame headerFrame = ch.readOutbound();
|
||||
assertThat(headerFrame.headers().get("key").toString(), is("value"));
|
||||
assertTrue(headerFrame.isEndStream());
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeDataEndWithTrailers() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2ServerDowngrader());
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
LastHttpContent trailers = new DefaultLastHttpContent(hello, true);
|
||||
HttpHeaders headers = trailers.trailingHeaders();
|
||||
headers.set("key", "value");
|
||||
assertTrue(ch.writeOutbound(trailers));
|
||||
|
||||
Http2DataFrame dataFrame = ch.readOutbound();
|
||||
try {
|
||||
assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertFalse(dataFrame.isEndStream());
|
||||
} finally {
|
||||
dataFrame.release();
|
||||
}
|
||||
|
||||
Http2HeadersFrame headerFrame = ch.readOutbound();
|
||||
assertThat(headerFrame.headers().get("key").toString(), is("value"));
|
||||
assertTrue(headerFrame.isEndStream());
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDowngradeHeaders() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2ServerDowngrader());
|
||||
Http2Headers headers = new DefaultHttp2Headers();
|
||||
headers.path("/");
|
||||
headers.method("GET");
|
||||
|
||||
assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers)));
|
||||
|
||||
HttpRequest request = ch.readInbound();
|
||||
assertThat(request.uri(), is("/"));
|
||||
assertThat(request.method(), is(HttpMethod.GET));
|
||||
assertThat(request.protocolVersion(), is(HttpVersion.HTTP_1_1));
|
||||
assertFalse(request instanceof FullHttpRequest);
|
||||
assertTrue(HttpUtil.isTransferEncodingChunked(request));
|
||||
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDowngradeHeadersWithContentLength() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2ServerDowngrader());
|
||||
Http2Headers headers = new DefaultHttp2Headers();
|
||||
headers.path("/");
|
||||
headers.method("GET");
|
||||
headers.setInt("content-length", 0);
|
||||
|
||||
assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers)));
|
||||
|
||||
HttpRequest request = ch.readInbound();
|
||||
assertThat(request.uri(), is("/"));
|
||||
assertThat(request.method(), is(HttpMethod.GET));
|
||||
assertThat(request.protocolVersion(), is(HttpVersion.HTTP_1_1));
|
||||
assertFalse(request instanceof FullHttpRequest);
|
||||
assertFalse(HttpUtil.isTransferEncodingChunked(request));
|
||||
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDowngradeFullHeaders() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2ServerDowngrader());
|
||||
Http2Headers headers = new DefaultHttp2Headers();
|
||||
headers.path("/");
|
||||
headers.method("GET");
|
||||
|
||||
assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers, true)));
|
||||
|
||||
FullHttpRequest request = ch.readInbound();
|
||||
try {
|
||||
assertThat(request.uri(), is("/"));
|
||||
assertThat(request.method(), is(HttpMethod.GET));
|
||||
assertThat(request.protocolVersion(), is(HttpVersion.HTTP_1_1));
|
||||
assertThat(request.content().readableBytes(), is(0));
|
||||
assertTrue(request.trailingHeaders().isEmpty());
|
||||
assertFalse(HttpUtil.isTransferEncodingChunked(request));
|
||||
} finally {
|
||||
request.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDowngradeTrailers() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2ServerDowngrader());
|
||||
Http2Headers headers = new DefaultHttp2Headers();
|
||||
headers.set("key", "value");
|
||||
assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers, true)));
|
||||
|
||||
LastHttpContent trailers = ch.readInbound();
|
||||
try {
|
||||
assertThat(trailers.content().readableBytes(), is(0));
|
||||
assertThat(trailers.trailingHeaders().get("key").toString(), is("value"));
|
||||
assertFalse(trailers instanceof FullHttpRequest);
|
||||
} finally {
|
||||
trailers.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDowngradeData() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2ServerDowngrader());
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
assertTrue(ch.writeInbound(new DefaultHttp2DataFrame(hello)));
|
||||
|
||||
HttpContent content = ch.readInbound();
|
||||
try {
|
||||
assertThat(content.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertFalse(content instanceof LastHttpContent);
|
||||
} finally {
|
||||
content.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDowngradeEndData() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2ServerDowngrader());
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
assertTrue(ch.writeInbound(new DefaultHttp2DataFrame(hello, true)));
|
||||
|
||||
LastHttpContent content = ch.readInbound();
|
||||
try {
|
||||
assertThat(content.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertTrue(content.trailingHeaders().isEmpty());
|
||||
} finally {
|
||||
content.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPassThroughOther() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2ServerDowngrader());
|
||||
Http2ResetFrame reset = new DefaultHttp2ResetFrame(0);
|
||||
Http2GoAwayFrame goaway = new DefaultHttp2GoAwayFrame(0);
|
||||
assertTrue(ch.writeInbound(reset));
|
||||
assertTrue(ch.writeInbound(goaway.retain()));
|
||||
|
||||
assertEquals(reset, ch.readInbound());
|
||||
|
||||
Http2GoAwayFrame frame = ch.readInbound();
|
||||
try {
|
||||
assertEquals(goaway, frame);
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
} finally {
|
||||
goaway.release();
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,801 @@
|
||||
/*
|
||||
* Copyright 2017 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.http2;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.DefaultHttpContent;
|
||||
import io.netty.handler.codec.http.DefaultHttpRequest;
|
||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||
import io.netty.handler.codec.http.DefaultLastHttpContent;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpContent;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.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.HttpScheme;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import io.netty.handler.codec.http.HttpUtil;
|
||||
import io.netty.handler.codec.http.LastHttpContent;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.SslHandler;
|
||||
import io.netty.handler.ssl.SslProvider;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
public class Http2StreamFrameToHttpObjectCodecTest {
|
||||
|
||||
@Test
|
||||
public void testUpgradeEmptyFullResponse() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true));
|
||||
assertTrue(ch.writeOutbound(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)));
|
||||
|
||||
Http2HeadersFrame headersFrame = ch.readOutbound();
|
||||
assertThat(headersFrame.headers().status().toString(), is("200"));
|
||||
assertTrue(headersFrame.isEndStream());
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeNonEmptyFullResponse() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true));
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
assertTrue(ch.writeOutbound(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, hello)));
|
||||
|
||||
Http2HeadersFrame headersFrame = ch.readOutbound();
|
||||
assertThat(headersFrame.headers().status().toString(), is("200"));
|
||||
assertFalse(headersFrame.isEndStream());
|
||||
|
||||
Http2DataFrame dataFrame = ch.readOutbound();
|
||||
try {
|
||||
assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertTrue(dataFrame.isEndStream());
|
||||
} finally {
|
||||
dataFrame.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeEmptyFullResponseWithTrailers() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true));
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
|
||||
HttpHeaders trailers = response.trailingHeaders();
|
||||
trailers.set("key", "value");
|
||||
assertTrue(ch.writeOutbound(response));
|
||||
|
||||
Http2HeadersFrame headersFrame = ch.readOutbound();
|
||||
assertThat(headersFrame.headers().status().toString(), is("200"));
|
||||
assertFalse(headersFrame.isEndStream());
|
||||
|
||||
Http2HeadersFrame trailersFrame = ch.readOutbound();
|
||||
assertThat(trailersFrame.headers().get("key").toString(), is("value"));
|
||||
assertTrue(trailersFrame.isEndStream());
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeNonEmptyFullResponseWithTrailers() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true));
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, hello);
|
||||
HttpHeaders trailers = response.trailingHeaders();
|
||||
trailers.set("key", "value");
|
||||
assertTrue(ch.writeOutbound(response));
|
||||
|
||||
Http2HeadersFrame headersFrame = ch.readOutbound();
|
||||
assertThat(headersFrame.headers().status().toString(), is("200"));
|
||||
assertFalse(headersFrame.isEndStream());
|
||||
|
||||
Http2DataFrame dataFrame = ch.readOutbound();
|
||||
try {
|
||||
assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertFalse(dataFrame.isEndStream());
|
||||
} finally {
|
||||
dataFrame.release();
|
||||
}
|
||||
|
||||
Http2HeadersFrame trailersFrame = ch.readOutbound();
|
||||
assertThat(trailersFrame.headers().get("key").toString(), is("value"));
|
||||
assertTrue(trailersFrame.isEndStream());
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeHeaders() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true));
|
||||
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
|
||||
assertTrue(ch.writeOutbound(response));
|
||||
|
||||
Http2HeadersFrame headersFrame = ch.readOutbound();
|
||||
assertThat(headersFrame.headers().status().toString(), is("200"));
|
||||
assertFalse(headersFrame.isEndStream());
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeChunk() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true));
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
HttpContent content = new DefaultHttpContent(hello);
|
||||
assertTrue(ch.writeOutbound(content));
|
||||
|
||||
Http2DataFrame dataFrame = ch.readOutbound();
|
||||
try {
|
||||
assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertFalse(dataFrame.isEndStream());
|
||||
} finally {
|
||||
dataFrame.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeEmptyEnd() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true));
|
||||
LastHttpContent end = LastHttpContent.EMPTY_LAST_CONTENT;
|
||||
assertTrue(ch.writeOutbound(end));
|
||||
|
||||
Http2DataFrame emptyFrame = ch.readOutbound();
|
||||
try {
|
||||
assertThat(emptyFrame.content().readableBytes(), is(0));
|
||||
assertTrue(emptyFrame.isEndStream());
|
||||
} finally {
|
||||
emptyFrame.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeDataEnd() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true));
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
LastHttpContent end = new DefaultLastHttpContent(hello, true);
|
||||
assertTrue(ch.writeOutbound(end));
|
||||
|
||||
Http2DataFrame dataFrame = ch.readOutbound();
|
||||
try {
|
||||
assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertTrue(dataFrame.isEndStream());
|
||||
} finally {
|
||||
dataFrame.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeTrailers() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true));
|
||||
LastHttpContent trailers = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, true);
|
||||
HttpHeaders headers = trailers.trailingHeaders();
|
||||
headers.set("key", "value");
|
||||
assertTrue(ch.writeOutbound(trailers));
|
||||
|
||||
Http2HeadersFrame headerFrame = ch.readOutbound();
|
||||
assertThat(headerFrame.headers().get("key").toString(), is("value"));
|
||||
assertTrue(headerFrame.isEndStream());
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeDataEndWithTrailers() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true));
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
LastHttpContent trailers = new DefaultLastHttpContent(hello, true);
|
||||
HttpHeaders headers = trailers.trailingHeaders();
|
||||
headers.set("key", "value");
|
||||
assertTrue(ch.writeOutbound(trailers));
|
||||
|
||||
Http2DataFrame dataFrame = ch.readOutbound();
|
||||
try {
|
||||
assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertFalse(dataFrame.isEndStream());
|
||||
} finally {
|
||||
dataFrame.release();
|
||||
}
|
||||
|
||||
Http2HeadersFrame headerFrame = ch.readOutbound();
|
||||
assertThat(headerFrame.headers().get("key").toString(), is("value"));
|
||||
assertTrue(headerFrame.isEndStream());
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDowngradeHeaders() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true));
|
||||
Http2Headers headers = new DefaultHttp2Headers();
|
||||
headers.path("/");
|
||||
headers.method("GET");
|
||||
|
||||
assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers)));
|
||||
|
||||
HttpRequest request = ch.readInbound();
|
||||
assertThat(request.uri(), is("/"));
|
||||
assertThat(request.method(), is(HttpMethod.GET));
|
||||
assertThat(request.protocolVersion(), is(HttpVersion.HTTP_1_1));
|
||||
assertFalse(request instanceof FullHttpRequest);
|
||||
assertTrue(HttpUtil.isTransferEncodingChunked(request));
|
||||
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDowngradeHeadersWithContentLength() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true));
|
||||
Http2Headers headers = new DefaultHttp2Headers();
|
||||
headers.path("/");
|
||||
headers.method("GET");
|
||||
headers.setInt("content-length", 0);
|
||||
|
||||
assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers)));
|
||||
|
||||
HttpRequest request = ch.readInbound();
|
||||
assertThat(request.uri(), is("/"));
|
||||
assertThat(request.method(), is(HttpMethod.GET));
|
||||
assertThat(request.protocolVersion(), is(HttpVersion.HTTP_1_1));
|
||||
assertFalse(request instanceof FullHttpRequest);
|
||||
assertFalse(HttpUtil.isTransferEncodingChunked(request));
|
||||
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDowngradeFullHeaders() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true));
|
||||
Http2Headers headers = new DefaultHttp2Headers();
|
||||
headers.path("/");
|
||||
headers.method("GET");
|
||||
|
||||
assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers, true)));
|
||||
|
||||
FullHttpRequest request = ch.readInbound();
|
||||
try {
|
||||
assertThat(request.uri(), is("/"));
|
||||
assertThat(request.method(), is(HttpMethod.GET));
|
||||
assertThat(request.protocolVersion(), is(HttpVersion.HTTP_1_1));
|
||||
assertThat(request.content().readableBytes(), is(0));
|
||||
assertTrue(request.trailingHeaders().isEmpty());
|
||||
assertFalse(HttpUtil.isTransferEncodingChunked(request));
|
||||
} finally {
|
||||
request.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDowngradeTrailers() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true));
|
||||
Http2Headers headers = new DefaultHttp2Headers();
|
||||
headers.set("key", "value");
|
||||
assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers, true)));
|
||||
|
||||
LastHttpContent trailers = ch.readInbound();
|
||||
try {
|
||||
assertThat(trailers.content().readableBytes(), is(0));
|
||||
assertThat(trailers.trailingHeaders().get("key"), is("value"));
|
||||
assertFalse(trailers instanceof FullHttpRequest);
|
||||
} finally {
|
||||
trailers.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDowngradeData() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true));
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
assertTrue(ch.writeInbound(new DefaultHttp2DataFrame(hello)));
|
||||
|
||||
HttpContent content = ch.readInbound();
|
||||
try {
|
||||
assertThat(content.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertFalse(content instanceof LastHttpContent);
|
||||
} finally {
|
||||
content.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDowngradeEndData() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true));
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
assertTrue(ch.writeInbound(new DefaultHttp2DataFrame(hello, true)));
|
||||
|
||||
LastHttpContent content = ch.readInbound();
|
||||
try {
|
||||
assertThat(content.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertTrue(content.trailingHeaders().isEmpty());
|
||||
} finally {
|
||||
content.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPassThroughOther() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true));
|
||||
Http2ResetFrame reset = new DefaultHttp2ResetFrame(0);
|
||||
Http2GoAwayFrame goaway = new DefaultHttp2GoAwayFrame(0);
|
||||
assertTrue(ch.writeInbound(reset));
|
||||
assertTrue(ch.writeInbound(goaway.retain()));
|
||||
|
||||
assertEquals(reset, ch.readInbound());
|
||||
|
||||
Http2GoAwayFrame frame = ch.readInbound();
|
||||
try {
|
||||
assertEquals(goaway, frame);
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
} finally {
|
||||
goaway.release();
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
// client-specific tests
|
||||
@Test
|
||||
public void testEncodeEmptyFullRequest() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false));
|
||||
assertTrue(ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/hello/world")));
|
||||
|
||||
Http2HeadersFrame headersFrame = ch.readOutbound();
|
||||
Http2Headers headers = headersFrame.headers();
|
||||
|
||||
assertThat(headers.scheme().toString(), is("http"));
|
||||
assertThat(headers.method().toString(), is("GET"));
|
||||
assertThat(headers.path().toString(), is("/hello/world"));
|
||||
assertTrue(headersFrame.isEndStream());
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeHttpsSchemeWhenSslHandlerExists() throws Exception {
|
||||
final Queue<Http2StreamFrame> frames = new ConcurrentLinkedQueue<Http2StreamFrame>();
|
||||
|
||||
final SslContext ctx = SslContextBuilder.forClient().sslProvider(SslProvider.JDK).build();
|
||||
EmbeddedChannel ch = new EmbeddedChannel(ctx.newHandler(ByteBufAllocator.DEFAULT),
|
||||
new ChannelOutboundHandlerAdapter() {
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||
if (msg instanceof Http2StreamFrame) {
|
||||
frames.add((Http2StreamFrame) msg);
|
||||
ctx.write(Unpooled.EMPTY_BUFFER, promise);
|
||||
} else {
|
||||
ctx.write(msg, promise);
|
||||
}
|
||||
}
|
||||
}, new Http2StreamFrameToHttpObjectCodec(false));
|
||||
|
||||
try {
|
||||
FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/hello/world");
|
||||
assertTrue(ch.writeOutbound(req));
|
||||
|
||||
ch.finishAndReleaseAll();
|
||||
|
||||
Http2HeadersFrame headersFrame = (Http2HeadersFrame) frames.poll();
|
||||
Http2Headers headers = headersFrame.headers();
|
||||
|
||||
assertThat(headers.scheme().toString(), is("https"));
|
||||
assertThat(headers.method().toString(), is("GET"));
|
||||
assertThat(headers.path().toString(), is("/hello/world"));
|
||||
assertTrue(headersFrame.isEndStream());
|
||||
assertNull(frames.poll());
|
||||
} finally {
|
||||
ch.finishAndReleaseAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeNonEmptyFullRequest() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false));
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
assertTrue(ch.writeOutbound(new DefaultFullHttpRequest(
|
||||
HttpVersion.HTTP_1_1, HttpMethod.PUT, "/hello/world", hello)));
|
||||
|
||||
Http2HeadersFrame headersFrame = ch.readOutbound();
|
||||
Http2Headers headers = headersFrame.headers();
|
||||
|
||||
assertThat(headers.scheme().toString(), is("http"));
|
||||
assertThat(headers.method().toString(), is("PUT"));
|
||||
assertThat(headers.path().toString(), is("/hello/world"));
|
||||
assertFalse(headersFrame.isEndStream());
|
||||
|
||||
Http2DataFrame dataFrame = ch.readOutbound();
|
||||
try {
|
||||
assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertTrue(dataFrame.isEndStream());
|
||||
} finally {
|
||||
dataFrame.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeEmptyFullRequestWithTrailers() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false));
|
||||
FullHttpRequest request = new DefaultFullHttpRequest(
|
||||
HttpVersion.HTTP_1_1, HttpMethod.PUT, "/hello/world");
|
||||
|
||||
HttpHeaders trailers = request.trailingHeaders();
|
||||
trailers.set("key", "value");
|
||||
assertTrue(ch.writeOutbound(request));
|
||||
|
||||
Http2HeadersFrame headersFrame = ch.readOutbound();
|
||||
Http2Headers headers = headersFrame.headers();
|
||||
|
||||
assertThat(headers.scheme().toString(), is("http"));
|
||||
assertThat(headers.method().toString(), is("PUT"));
|
||||
assertThat(headers.path().toString(), is("/hello/world"));
|
||||
assertFalse(headersFrame.isEndStream());
|
||||
|
||||
Http2HeadersFrame trailersFrame = ch.readOutbound();
|
||||
assertThat(trailersFrame.headers().get("key").toString(), is("value"));
|
||||
assertTrue(trailersFrame.isEndStream());
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeNonEmptyFullRequestWithTrailers() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false));
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
FullHttpRequest request = new DefaultFullHttpRequest(
|
||||
HttpVersion.HTTP_1_1, HttpMethod.PUT, "/hello/world", hello);
|
||||
|
||||
HttpHeaders trailers = request.trailingHeaders();
|
||||
trailers.set("key", "value");
|
||||
assertTrue(ch.writeOutbound(request));
|
||||
|
||||
Http2HeadersFrame headersFrame = ch.readOutbound();
|
||||
Http2Headers headers = headersFrame.headers();
|
||||
|
||||
assertThat(headers.scheme().toString(), is("http"));
|
||||
assertThat(headers.method().toString(), is("PUT"));
|
||||
assertThat(headers.path().toString(), is("/hello/world"));
|
||||
assertFalse(headersFrame.isEndStream());
|
||||
|
||||
Http2DataFrame dataFrame = ch.readOutbound();
|
||||
try {
|
||||
assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertFalse(dataFrame.isEndStream());
|
||||
} finally {
|
||||
dataFrame.release();
|
||||
}
|
||||
|
||||
Http2HeadersFrame trailersFrame = ch.readOutbound();
|
||||
assertThat(trailersFrame.headers().get("key").toString(), is("value"));
|
||||
assertTrue(trailersFrame.isEndStream());
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeRequestHeaders() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false));
|
||||
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/hello/world");
|
||||
assertTrue(ch.writeOutbound(request));
|
||||
|
||||
Http2HeadersFrame headersFrame = ch.readOutbound();
|
||||
Http2Headers headers = headersFrame.headers();
|
||||
|
||||
assertThat(headers.scheme().toString(), is("http"));
|
||||
assertThat(headers.method().toString(), is("GET"));
|
||||
assertThat(headers.path().toString(), is("/hello/world"));
|
||||
assertFalse(headersFrame.isEndStream());
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeChunkAsClient() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false));
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
HttpContent content = new DefaultHttpContent(hello);
|
||||
assertTrue(ch.writeOutbound(content));
|
||||
|
||||
Http2DataFrame dataFrame = ch.readOutbound();
|
||||
try {
|
||||
assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertFalse(dataFrame.isEndStream());
|
||||
} finally {
|
||||
dataFrame.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeEmptyEndAsClient() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false));
|
||||
LastHttpContent end = LastHttpContent.EMPTY_LAST_CONTENT;
|
||||
assertTrue(ch.writeOutbound(end));
|
||||
|
||||
Http2DataFrame emptyFrame = ch.readOutbound();
|
||||
try {
|
||||
assertThat(emptyFrame.content().readableBytes(), is(0));
|
||||
assertTrue(emptyFrame.isEndStream());
|
||||
} finally {
|
||||
emptyFrame.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeDataEndAsClient() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false));
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
LastHttpContent end = new DefaultLastHttpContent(hello, true);
|
||||
assertTrue(ch.writeOutbound(end));
|
||||
|
||||
Http2DataFrame dataFrame = ch.readOutbound();
|
||||
try {
|
||||
assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertTrue(dataFrame.isEndStream());
|
||||
} finally {
|
||||
dataFrame.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeTrailersAsClient() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false));
|
||||
LastHttpContent trailers = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, true);
|
||||
HttpHeaders headers = trailers.trailingHeaders();
|
||||
headers.set("key", "value");
|
||||
assertTrue(ch.writeOutbound(trailers));
|
||||
|
||||
Http2HeadersFrame headerFrame = ch.readOutbound();
|
||||
assertThat(headerFrame.headers().get("key").toString(), is("value"));
|
||||
assertTrue(headerFrame.isEndStream());
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeDataEndWithTrailersAsClient() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false));
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
LastHttpContent trailers = new DefaultLastHttpContent(hello, true);
|
||||
HttpHeaders headers = trailers.trailingHeaders();
|
||||
headers.set("key", "value");
|
||||
assertTrue(ch.writeOutbound(trailers));
|
||||
|
||||
Http2DataFrame dataFrame = ch.readOutbound();
|
||||
try {
|
||||
assertThat(dataFrame.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertFalse(dataFrame.isEndStream());
|
||||
} finally {
|
||||
dataFrame.release();
|
||||
}
|
||||
|
||||
Http2HeadersFrame headerFrame = ch.readOutbound();
|
||||
assertThat(headerFrame.headers().get("key").toString(), is("value"));
|
||||
assertTrue(headerFrame.isEndStream());
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodeResponseHeaders() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false));
|
||||
Http2Headers headers = new DefaultHttp2Headers();
|
||||
headers.scheme(HttpScheme.HTTP.name());
|
||||
headers.status(HttpResponseStatus.OK.codeAsText());
|
||||
|
||||
assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers)));
|
||||
|
||||
HttpResponse response = ch.readInbound();
|
||||
assertThat(response.status(), is(HttpResponseStatus.OK));
|
||||
assertThat(response.protocolVersion(), is(HttpVersion.HTTP_1_1));
|
||||
assertFalse(response instanceof FullHttpResponse);
|
||||
assertTrue(HttpUtil.isTransferEncodingChunked(response));
|
||||
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodeResponseHeadersWithContentLength() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false));
|
||||
Http2Headers headers = new DefaultHttp2Headers();
|
||||
headers.scheme(HttpScheme.HTTP.name());
|
||||
headers.status(HttpResponseStatus.OK.codeAsText());
|
||||
headers.setInt("content-length", 0);
|
||||
|
||||
assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers)));
|
||||
|
||||
HttpResponse response = ch.readInbound();
|
||||
assertThat(response.status(), is(HttpResponseStatus.OK));
|
||||
assertThat(response.protocolVersion(), is(HttpVersion.HTTP_1_1));
|
||||
assertFalse(response instanceof FullHttpResponse);
|
||||
assertFalse(HttpUtil.isTransferEncodingChunked(response));
|
||||
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodeFullResponseHeaders() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false));
|
||||
Http2Headers headers = new DefaultHttp2Headers();
|
||||
headers.scheme(HttpScheme.HTTP.name());
|
||||
headers.status(HttpResponseStatus.OK.codeAsText());
|
||||
|
||||
assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers, true)));
|
||||
|
||||
FullHttpResponse response = ch.readInbound();
|
||||
try {
|
||||
assertThat(response.status(), is(HttpResponseStatus.OK));
|
||||
assertThat(response.protocolVersion(), is(HttpVersion.HTTP_1_1));
|
||||
assertThat(response.content().readableBytes(), is(0));
|
||||
assertTrue(response.trailingHeaders().isEmpty());
|
||||
assertFalse(HttpUtil.isTransferEncodingChunked(response));
|
||||
} finally {
|
||||
response.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodeResponseTrailersAsClient() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false));
|
||||
Http2Headers headers = new DefaultHttp2Headers();
|
||||
headers.set("key", "value");
|
||||
assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers, true)));
|
||||
|
||||
LastHttpContent trailers = ch.readInbound();
|
||||
try {
|
||||
assertThat(trailers.content().readableBytes(), is(0));
|
||||
assertThat(trailers.trailingHeaders().get("key"), is("value"));
|
||||
assertFalse(trailers instanceof FullHttpRequest);
|
||||
} finally {
|
||||
trailers.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodeDataAsClient() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false));
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
assertTrue(ch.writeInbound(new DefaultHttp2DataFrame(hello)));
|
||||
|
||||
HttpContent content = ch.readInbound();
|
||||
try {
|
||||
assertThat(content.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertFalse(content instanceof LastHttpContent);
|
||||
} finally {
|
||||
content.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodeEndDataAsClient() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false));
|
||||
ByteBuf hello = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
|
||||
assertTrue(ch.writeInbound(new DefaultHttp2DataFrame(hello, true)));
|
||||
|
||||
LastHttpContent content = ch.readInbound();
|
||||
try {
|
||||
assertThat(content.content().toString(CharsetUtil.UTF_8), is("hello world"));
|
||||
assertTrue(content.trailingHeaders().isEmpty());
|
||||
} finally {
|
||||
content.release();
|
||||
}
|
||||
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPassThroughOtherAsClient() throws Exception {
|
||||
EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false));
|
||||
Http2ResetFrame reset = new DefaultHttp2ResetFrame(0);
|
||||
Http2GoAwayFrame goaway = new DefaultHttp2GoAwayFrame(0);
|
||||
assertTrue(ch.writeInbound(reset));
|
||||
assertTrue(ch.writeInbound(goaway.retain()));
|
||||
|
||||
assertEquals(reset, ch.readInbound());
|
||||
|
||||
Http2GoAwayFrame frame = ch.readInbound();
|
||||
try {
|
||||
assertEquals(goaway, frame);
|
||||
assertThat(ch.readInbound(), is(nullValue()));
|
||||
assertFalse(ch.finish());
|
||||
} finally {
|
||||
goaway.release();
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user