@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;

                // Provide the user the opportunity to specify the streamId
                currentStreamId = getStreamId(httpMsg.headers());

                // Add HttpScheme if it's defined in constructor and header does not contain it.
                if (httpScheme != null &&
                        !httpMsg.headers().contains(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text())) {
                    httpMsg.headers().set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), httpScheme.name());
                }

                // Convert and write the 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;

                    // Convert any trailing headers.
                    final LastHttpContent lastContent = (LastHttpContent) msg;
                    trailers = lastContent.trailingHeaders();
                    http2Trailers = HttpConversionUtil.toHttp2Headers(trailers, validateHeaders);
                }

                // Write the data
                final ByteBuf content = ((HttpContent) msg).content();
                endStream = isLastContent && trailers.isEmpty();
                encoder.writeData(ctx, currentStreamId, content, 0, endStream, promiseAggregator.newPromise());
                release = false;

                if (!trailers.isEmpty()) {
                    // Write trailing headers.
                    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();
        }
    }
@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://your_user-name123@www.example.org:5555/example");
        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));
    }