@Test public void testChunkedRequestWithBodyAndTrailingHeaders() throws Exception { final String text = "foooooo"; final String text2 = "goooo"; final List<String> receivedBuffers = Collections.synchronizedList(new ArrayList<String>()); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock in) throws Throwable { receivedBuffers.add(((ByteBuf) in.getArguments()[2]).toString(UTF_8)); return null; } }).when(serverListener).onDataRead(any(ChannelHandlerContext.class), eq(3), any(ByteBuf.class), eq(0), eq(false)); bootstrapEnv(4, 1, 1); final HttpRequest request = new DefaultHttpRequest(HTTP_1_1, POST, "http: final HttpHeaders httpHeaders = request.headers(); httpHeaders.set(HttpHeaderNames.HOST, "www.example.org:5555"); httpHeaders.add(HttpHeaderNames.TRANSFER_ENCODING, "chunked"); httpHeaders.add(of("foo"), of("goo")); httpHeaders.add(of("foo"), of("goo2")); httpHeaders.add(of("foo2"), of("goo2")); final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("POST")).path(new AsciiString("/example")) .authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http")) .add(new AsciiString("foo"), new AsciiString("goo")) .add(new AsciiString("foo"), new AsciiString("goo2")) .add(new AsciiString("foo2"), new AsciiString("goo2")); final DefaultHttpContent httpContent = new DefaultHttpContent(Unpooled.copiedBuffer(text, UTF_8)); final LastHttpContent lastHttpContent = new DefaultLastHttpContent(Unpooled.copiedBuffer(text2, UTF_8)); lastHttpContent.trailingHeaders().add(of("trailing"), of("bar")); final Http2Headers http2TrailingHeaders = new DefaultHttp2Headers() .add(new AsciiString("trailing"), new AsciiString("bar")); ChannelPromise writePromise = newPromise(); ChannelFuture writeFuture = clientChannel.write(request, writePromise); ChannelPromise contentPromise = newPromise(); ChannelFuture contentFuture = clientChannel.write(httpContent, contentPromise); ChannelPromise lastContentPromise = newPromise(); ChannelFuture lastContentFuture = clientChannel.write(lastHttpContent, lastContentPromise); clientChannel.flush(); assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); assertTrue(writePromise.isSuccess()); assertTrue(writeFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); assertTrue(writeFuture.isSuccess()); assertTrue(contentPromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); assertTrue(contentPromise.isSuccess()); assertTrue(contentFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); assertTrue(contentFuture.isSuccess()); assertTrue(lastContentPromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); assertTrue(lastContentPromise.isSuccess()); assertTrue(lastContentFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); assertTrue(lastContentFuture.isSuccess()); awaitRequests(); verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(3), eq(http2Headers), eq(0), anyShort(), anyBoolean(), eq(0), eq(false)); verify(serverListener).onDataRead(any(ChannelHandlerContext.class), eq(3), any(ByteBuf.class), eq(0), eq(false)); verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(3), eq(http2TrailingHeaders), eq(0), anyShort(), anyBoolean(), eq(0), eq(true)); assertEquals(1, receivedBuffers.size()); assertEquals(text + text2, receivedBuffers.get(0)); }
@Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { if (!(msg instanceof HttpMessage || msg instanceof HttpContent)) { ctx.write(msg, promise); return; } boolean release = true; SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor()); try { Http2ConnectionEncoder encoder = encoder(); boolean endStream = false; if (msg instanceof HttpMessage) { final HttpMessage httpMsg = (HttpMessage) msg; currentStreamId = getStreamId(httpMsg.headers()); Http2Headers http2Headers = HttpConversionUtil.toHttp2Headers(httpMsg, validateHeaders); endStream = msg instanceof FullHttpMessage && !((FullHttpMessage) msg).content().isReadable(); writeHeaders(ctx, encoder, currentStreamId, httpMsg.headers(), http2Headers, endStream, promiseAggregator); } if (!endStream && msg instanceof HttpContent) { boolean isLastContent = false; HttpHeaders trailers = EmptyHttpHeaders.INSTANCE; Http2Headers http2Trailers = EmptyHttp2Headers.INSTANCE; if (msg instanceof LastHttpContent) { isLastContent = true; final LastHttpContent lastContent = (LastHttpContent) msg; trailers = lastContent.trailingHeaders(); http2Trailers = HttpConversionUtil.toHttp2Headers(trailers, validateHeaders); } final ByteBuf content = ((HttpContent) msg).content(); endStream = isLastContent && trailers.isEmpty(); release = false; encoder.writeData(ctx, currentStreamId, content, 0, endStream, promiseAggregator.newPromise()); if (!trailers.isEmpty()) { writeHeaders(ctx, encoder, currentStreamId, trailers, http2Trailers, true, promiseAggregator); } } } catch (Throwable t) { onError(ctx, true, t); promiseAggregator.setFailure(t); } finally { if (release) { ReferenceCountUtil.release(msg); } promiseAggregator.doneAllocatingPromises(); } }
HttpToHttp2ConnectionHandler extends Http2ConnectionHandler { @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { if (!(msg instanceof HttpMessage || msg instanceof HttpContent)) { ctx.write(msg, promise); return; } boolean release = true; SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor()); try { Http2ConnectionEncoder encoder = encoder(); boolean endStream = false; if (msg instanceof HttpMessage) { final HttpMessage httpMsg = (HttpMessage) msg; currentStreamId = getStreamId(httpMsg.headers()); Http2Headers http2Headers = HttpConversionUtil.toHttp2Headers(httpMsg, validateHeaders); endStream = msg instanceof FullHttpMessage && !((FullHttpMessage) msg).content().isReadable(); writeHeaders(ctx, encoder, currentStreamId, httpMsg.headers(), http2Headers, endStream, promiseAggregator); } if (!endStream && msg instanceof HttpContent) { boolean isLastContent = false; HttpHeaders trailers = EmptyHttpHeaders.INSTANCE; Http2Headers http2Trailers = EmptyHttp2Headers.INSTANCE; if (msg instanceof LastHttpContent) { isLastContent = true; final LastHttpContent lastContent = (LastHttpContent) msg; trailers = lastContent.trailingHeaders(); http2Trailers = HttpConversionUtil.toHttp2Headers(trailers, validateHeaders); } final ByteBuf content = ((HttpContent) msg).content(); endStream = isLastContent && trailers.isEmpty(); release = false; encoder.writeData(ctx, currentStreamId, content, 0, endStream, promiseAggregator.newPromise()); if (!trailers.isEmpty()) { writeHeaders(ctx, encoder, currentStreamId, trailers, http2Trailers, true, promiseAggregator); } } } catch (Throwable t) { onError(ctx, true, t); promiseAggregator.setFailure(t); } finally { if (release) { ReferenceCountUtil.release(msg); } promiseAggregator.doneAllocatingPromises(); } } }
HttpToHttp2ConnectionHandler extends Http2ConnectionHandler { @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { if (!(msg instanceof HttpMessage || msg instanceof HttpContent)) { ctx.write(msg, promise); return; } boolean release = true; SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor()); try { Http2ConnectionEncoder encoder = encoder(); boolean endStream = false; if (msg instanceof HttpMessage) { final HttpMessage httpMsg = (HttpMessage) msg; currentStreamId = getStreamId(httpMsg.headers()); Http2Headers http2Headers = HttpConversionUtil.toHttp2Headers(httpMsg, validateHeaders); endStream = msg instanceof FullHttpMessage && !((FullHttpMessage) msg).content().isReadable(); writeHeaders(ctx, encoder, currentStreamId, httpMsg.headers(), http2Headers, endStream, promiseAggregator); } if (!endStream && msg instanceof HttpContent) { boolean isLastContent = false; HttpHeaders trailers = EmptyHttpHeaders.INSTANCE; Http2Headers http2Trailers = EmptyHttp2Headers.INSTANCE; if (msg instanceof LastHttpContent) { isLastContent = true; final LastHttpContent lastContent = (LastHttpContent) msg; trailers = lastContent.trailingHeaders(); http2Trailers = HttpConversionUtil.toHttp2Headers(trailers, validateHeaders); } final ByteBuf content = ((HttpContent) msg).content(); endStream = isLastContent && trailers.isEmpty(); release = false; encoder.writeData(ctx, currentStreamId, content, 0, endStream, promiseAggregator.newPromise()); if (!trailers.isEmpty()) { writeHeaders(ctx, encoder, currentStreamId, trailers, http2Trailers, true, promiseAggregator); } } } catch (Throwable t) { onError(ctx, true, t); promiseAggregator.setFailure(t); } finally { if (release) { ReferenceCountUtil.release(msg); } promiseAggregator.doneAllocatingPromises(); } } protected  HttpToHttp2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
                                           Http2Settings initialSettings, boolean validateHeaders); }
HttpToHttp2ConnectionHandler extends Http2ConnectionHandler { @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { if (!(msg instanceof HttpMessage || msg instanceof HttpContent)) { ctx.write(msg, promise); return; } boolean release = true; SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor()); try { Http2ConnectionEncoder encoder = encoder(); boolean endStream = false; if (msg instanceof HttpMessage) { final HttpMessage httpMsg = (HttpMessage) msg; currentStreamId = getStreamId(httpMsg.headers()); Http2Headers http2Headers = HttpConversionUtil.toHttp2Headers(httpMsg, validateHeaders); endStream = msg instanceof FullHttpMessage && !((FullHttpMessage) msg).content().isReadable(); writeHeaders(ctx, encoder, currentStreamId, httpMsg.headers(), http2Headers, endStream, promiseAggregator); } if (!endStream && msg instanceof HttpContent) { boolean isLastContent = false; HttpHeaders trailers = EmptyHttpHeaders.INSTANCE; Http2Headers http2Trailers = EmptyHttp2Headers.INSTANCE; if (msg instanceof LastHttpContent) { isLastContent = true; final LastHttpContent lastContent = (LastHttpContent) msg; trailers = lastContent.trailingHeaders(); http2Trailers = HttpConversionUtil.toHttp2Headers(trailers, validateHeaders); } final ByteBuf content = ((HttpContent) msg).content(); endStream = isLastContent && trailers.isEmpty(); release = false; encoder.writeData(ctx, currentStreamId, content, 0, endStream, promiseAggregator.newPromise()); if (!trailers.isEmpty()) { writeHeaders(ctx, encoder, currentStreamId, trailers, http2Trailers, true, promiseAggregator); } } } catch (Throwable t) { onError(ctx, true, t); promiseAggregator.setFailure(t); } finally { if (release) { ReferenceCountUtil.release(msg); } promiseAggregator.doneAllocatingPromises(); } } protected  HttpToHttp2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
                                           Http2Settings initialSettings, boolean validateHeaders); @Override void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise); }
HttpToHttp2ConnectionHandler extends Http2ConnectionHandler { @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { if (!(msg instanceof HttpMessage || msg instanceof HttpContent)) { ctx.write(msg, promise); return; } boolean release = true; SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor()); try { Http2ConnectionEncoder encoder = encoder(); boolean endStream = false; if (msg instanceof HttpMessage) { final HttpMessage httpMsg = (HttpMessage) msg; currentStreamId = getStreamId(httpMsg.headers()); Http2Headers http2Headers = HttpConversionUtil.toHttp2Headers(httpMsg, validateHeaders); endStream = msg instanceof FullHttpMessage && !((FullHttpMessage) msg).content().isReadable(); writeHeaders(ctx, encoder, currentStreamId, httpMsg.headers(), http2Headers, endStream, promiseAggregator); } if (!endStream && msg instanceof HttpContent) { boolean isLastContent = false; HttpHeaders trailers = EmptyHttpHeaders.INSTANCE; Http2Headers http2Trailers = EmptyHttp2Headers.INSTANCE; if (msg instanceof LastHttpContent) { isLastContent = true; final LastHttpContent lastContent = (LastHttpContent) msg; trailers = lastContent.trailingHeaders(); http2Trailers = HttpConversionUtil.toHttp2Headers(trailers, validateHeaders); } final ByteBuf content = ((HttpContent) msg).content(); endStream = isLastContent && trailers.isEmpty(); release = false; encoder.writeData(ctx, currentStreamId, content, 0, endStream, promiseAggregator.newPromise()); if (!trailers.isEmpty()) { writeHeaders(ctx, encoder, currentStreamId, trailers, http2Trailers, true, promiseAggregator); } } } catch (Throwable t) { onError(ctx, true, t); promiseAggregator.setFailure(t); } finally { if (release) { ReferenceCountUtil.release(msg); } promiseAggregator.doneAllocatingPromises(); } } protected  HttpToHttp2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
                                           Http2Settings initialSettings, boolean validateHeaders); @Override void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise);  }