/* * 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.ChannelHandler; 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.EncoderException; 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.SslProvider; import io.netty.util.CharsetUtil; import org.junit.Test; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; 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 encode100ContinueAsHttp2HeadersFrameThatIsNotEndStream() throws Exception { EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); assertTrue(ch.writeOutbound(new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE))); Http2HeadersFrame headersFrame = ch.readOutbound(); assertThat(headersFrame.headers().status().toString(), is("100")); assertFalse(headersFrame.isEndStream()); assertThat(ch.readOutbound(), is(nullValue())); assertFalse(ch.finish()); } @Test (expected = EncoderException.class) public void encodeNonFullHttpResponse100ContinueIsRejected() throws Exception { EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(true)); try { ch.writeOutbound(new DefaultHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE)); } finally { ch.finishAndReleaseAll(); } } @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 frames = new ConcurrentLinkedQueue(); 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 decode100ContinueHttp2HeadersAsFullHttpResponse() throws Exception { EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); Http2Headers headers = new DefaultHttp2Headers(); headers.scheme(HttpScheme.HTTP.name()); headers.status(HttpResponseStatus.CONTINUE.codeAsText()); assertTrue(ch.writeInbound(new DefaultHttp2HeadersFrame(headers, false))); final FullHttpResponse response = ch.readInbound(); try { assertThat(response.status(), is(HttpResponseStatus.CONTINUE)); assertThat(response.protocolVersion(), is(HttpVersion.HTTP_1_1)); } finally { response.release(); } assertThat(ch.readInbound(), 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 { testDecodeFullResponseHeaders(false); } @Test public void testDecodeFullResponseHeadersWithStreamID() throws Exception { testDecodeFullResponseHeaders(true); } private void testDecodeFullResponseHeaders(boolean withStreamId) throws Exception { EmbeddedChannel ch = new EmbeddedChannel(new Http2StreamFrameToHttpObjectCodec(false)); Http2Headers headers = new DefaultHttp2Headers(); headers.scheme(HttpScheme.HTTP.name()); headers.status(HttpResponseStatus.OK.codeAsText()); Http2HeadersFrame frame = new DefaultHttp2HeadersFrame(headers, true); if (withStreamId) { frame.stream(new Http2FrameStream() { @Override public int id() { return 1; } @Override public Http2Stream.State state() { return Http2Stream.State.OPEN; } }); } assertTrue(ch.writeInbound(frame)); 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)); if (withStreamId) { assertEquals(1, (int) response.headers().getInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text())); } } 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(); } } @Test public void testIsSharableBetweenChannels() throws Exception { final Queue frames = new ConcurrentLinkedQueue(); final ChannelHandler sharedHandler = new Http2StreamFrameToHttpObjectCodec(false); final SslContext ctx = SslContextBuilder.forClient().sslProvider(SslProvider.JDK).build(); EmbeddedChannel tlsCh = new EmbeddedChannel(ctx.newHandler(ByteBufAllocator.DEFAULT), new ChannelOutboundHandlerAdapter() { @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { if (msg instanceof Http2StreamFrame) { frames.add((Http2StreamFrame) msg); promise.setSuccess(); } else { ctx.write(msg, promise); } } }, sharedHandler); EmbeddedChannel plaintextCh = new EmbeddedChannel( new ChannelOutboundHandlerAdapter() { @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { if (msg instanceof Http2StreamFrame) { frames.add((Http2StreamFrame) msg); promise.setSuccess(); } else { ctx.write(msg, promise); } } }, sharedHandler); FullHttpRequest req = new DefaultFullHttpRequest( HttpVersion.HTTP_1_1, HttpMethod.GET, "/hello/world"); assertTrue(tlsCh.writeOutbound(req)); assertTrue(tlsCh.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()); // Run the plaintext channel req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/hello/world"); assertFalse(plaintextCh.writeOutbound(req)); assertFalse(plaintextCh.finishAndReleaseAll()); headersFrame = (Http2HeadersFrame) frames.poll(); 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()); assertNull(frames.poll()); } }