我有一个CompositeByteBuf,其中包含一些构成HTTP请求的缓冲区,我想在HTTP请求行之后插入一个额外的HTTP标头字段。 (我不想使用整个HTTP编码器/解码器,因为我只是代理数据,不需要将所有数据解析为HTTP)。

我该如何使用派生的缓冲区执行此操作,因此避免复制CompositeByteBuf的内容。我使用slice和readSlice进行的每一次尝试都产生indexoutofbounds错误或Stack Overflow。任何人都可以提出以下替代方案,而不需要复制整个CompositeBytebuf吗?

/**
 * Injects an XFF header into pendingBuf
 */
private void addXForwardedForHeaderToPendingBuf(
                 int pLFpos,
              String pRemoteIPaddr)
{
    //create a new buffer
    ByteBuf newBuf = inboundChannel.alloc().directBuffer();

    //add the HTTP request line to it
    ByteBufUtil.writeUtf8(newBuf,
                          pendingBuf.readCharSequence(pLFpos + 1,
                          CharsetUtil.UTF_8));

    //add the XFF header
    ByteBufUtil.writeUtf8(newBuf, "X-Forwarded-For: ");
    ByteBufUtil.writeUtf8(newBuf, pRemoteIPaddr);
    ByteBufUtil.writeUtf8(newBuf, "\r\n");

    //add anything from the original buffer that came after the request line
    int bytesRemaining = pendingBuf.readableBytes();
    if (bytesRemaining > 0)
    {
        newBuf.writeBytes(pendingBuf);
    }

    //clear pendingBuf
    pendingBuf.removeComponents(0, pendingBuf.numComponents());
    pendingBuf.setIndex(0, 0);

    //add newBuf into pendingBuf
    pendingBuf.addComponent(newBuf);
    pendingBuf.writerIndex(pendingBuf.writerIndex() + newBuf.writerIndex());
}

最佳答案

虽然编辑当前的字节缓冲区有一个缺点,那就是在最坏的情况下所有字节都需要移动,我们可以利用CompositeByteBuf具有可以编辑并根据需要移动的组件这一事实。

我们基本上想实现以下步骤:


由于Bytebuf中可能有多个CompositeByteBuf,因此我们要搜索要修改的buf的索引。

ByteBuf为我们提供了以下方法:


toComponentIndex
toByteIndex


遗憾的是,这些方法在字符串末尾插入的情况下将无法正常工作,因为从技术上讲,这对于原始缓冲区是超出范围的,因此我们需要为此添加一个特殊情况。
我们想要实现一种特殊情况,即我们想精确地插入多个缓冲区之间的边界,因为在这些情况下,我们实际上可以使用零拷贝。
如果拆分索引恰好位于一个字节缓冲区的中间,则需要对其进行拆分,并将其自身添加为2个单独的缓冲区。
我们需要更新组合for some reason this doesn't happen by default.上的writer索引


使用以上流程,我们可以创建以下代码:

public static void insertString(CompositeByteBuf buffer, int index, ByteBuf insertion) {
    try {
        if (buffer == null) {
            throw new NullPointerException("buffer");
        }
        if (insertion == null) {
            throw new NullPointerException("insertion");
        }
        if (buffer.readableBytes() < index) {
            throw new IllegalArgumentException("buffer.readableBytes() < index: "
                    + buffer.readableBytes() + " < " + index);
        }

        // Start by checking the offset where we need to inject the insertion
        int injectionBufOffset;
        int injectionByteOffset;
        if (index == buffer.readableBytes()) {
            injectionBufOffset = buffer.numComponents();
            injectionByteOffset = 0;
        } else {
            injectionBufOffset = buffer.toComponentIndex(index);
            injectionByteOffset = index - buffer.toByteIndex(injectionBufOffset);
        }

        // Optimalize in the case of offset 0
        if (injectionByteOffset == 0) {
            buffer.addComponent(injectionBufOffset, insertion.retain());
            buffer.writerIndex(buffer.writerIndex() + insertion.readableBytes());
            return;
        }
        // Do the split technique
        ByteBuf toSplit = buffer.internalComponent(injectionBufOffset).retain();
        try {
            buffer.removeComponent(injectionBufOffset);
            buffer.addComponent(injectionBufOffset + 0,
                    toSplit.readSlice(injectionByteOffset).retain());
            buffer.addComponent(injectionBufOffset + 1, insertion.retain());
            buffer.addComponent(injectionBufOffset + 2,
                    toSplit.retain());
            buffer.writerIndex(buffer.writerIndex() + insertion.readableBytes());
        } finally {
            ReferenceCountUtil.release(toSplit);
        }
    } finally {
        if (insertion != null) {
            ReferenceCountUtil.release(insertion);
        }
    }
}


由于此代码非常复杂,因此我们还想确保对其进行了正确的测试,因此,我们需要一些单元测试(JUnit):

import static test.NettySplit.insertString;

public class NettySplitTest {

    CompositeByteBuf buffer;
    ByteBuf test;

    private void addByteBuf(CompositeByteBuf target, ByteBuf source) {
        target.addComponent(source);
        target.writerIndex(target.writerIndex() + source.readableBytes());
    }

    @Before
    public void before() {
        buffer = ByteBufAllocator.DEFAULT.compositeBuffer();
    }

    @After
    public void after() {
        ReferenceCountUtil.release(buffer);
        buffer = null;
        ReferenceCountUtil.release(test);
        test = null;
    }

    @Test
    public void testSplitting() {
        addByteBuf(buffer, Unpooled.wrappedBuffer(new byte[]{0, 1, 2, 3}));

        insertString(buffer, 2, Unpooled.wrappedBuffer(new byte[]{5}));

        test = Unpooled.wrappedBuffer(new byte[]{0, 1, 5, 2, 3});
        assertEquals(test, buffer);

    }

    @Test
    public void testInsertionStart() {
        addByteBuf(buffer, Unpooled.wrappedBuffer(new byte[]{0, 1, 2, 3}));

        insertString(buffer, 0, Unpooled.wrappedBuffer(new byte[]{5}));

        test = Unpooled.wrappedBuffer(new byte[]{5, 0, 1, 2, 3});
        assertEquals(test, buffer);
    }

    @Test
    public void testInsertionEnd() {
        addByteBuf(buffer, Unpooled.wrappedBuffer(new byte[]{0, 1, 2, 3}));

        insertString(buffer, 4, Unpooled.wrappedBuffer(new byte[]{5}));

        test = Unpooled.wrappedBuffer(new byte[]{0, 1, 2, 3, 5});
        assertEquals(test, buffer);
    }

    @Test
    public void testInsertionSplitEnd() {
        addByteBuf(buffer, Unpooled.wrappedBuffer(new byte[]{0, 1, 2, 3}));
        addByteBuf(buffer, Unpooled.wrappedBuffer(new byte[]{0, 1, 2, 3}));

        insertString(buffer, 6, Unpooled.wrappedBuffer(new byte[]{5}));

        test = Unpooled.wrappedBuffer(new byte[]{0, 1, 2, 3, 0, 1, 5, 2, 3});
        assertEquals(test, buffer);
    }

}

关于java - 如何在Netty CompositeByteBuf的中间插入字符串,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44678122/

10-11 17:12