Implement Encoder & Decoder Streams to encode instructions.
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
parent
2916b60091
commit
883e4f79c2
|
@ -13,11 +13,60 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.http3.qpack;
|
package org.eclipse.jetty.http3.qpack;
|
||||||
|
|
||||||
public interface DecoderStream
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
|
import org.eclipse.jetty.io.NullByteBufferPool;
|
||||||
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
|
|
||||||
|
public class DecoderStream
|
||||||
{
|
{
|
||||||
void sendSectionAcknowledgment(int streamId);
|
private final OutputStream _outputStream;
|
||||||
|
private final ByteBufferPool _bufferPool;
|
||||||
|
|
||||||
void sendStreamCancellation(int streamId);
|
public DecoderStream(OutputStream outputStream)
|
||||||
|
{
|
||||||
|
this (outputStream, new NullByteBufferPool());
|
||||||
|
}
|
||||||
|
|
||||||
void sendInsertCountIncrement(int increment);
|
public DecoderStream(OutputStream outputStream, ByteBufferPool bufferPool)
|
||||||
|
{
|
||||||
|
_outputStream = outputStream;
|
||||||
|
_bufferPool = bufferPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendSectionAcknowledgment(int streamId) throws IOException
|
||||||
|
{
|
||||||
|
int size = NBitInteger.octectsNeeded(7, streamId) + 1;
|
||||||
|
ByteBuffer buffer = _bufferPool.acquire(size, false);
|
||||||
|
BufferUtil.clearToFill(buffer);
|
||||||
|
buffer.put((byte)0x80);
|
||||||
|
NBitInteger.encode(buffer, 7, streamId);
|
||||||
|
BufferUtil.flipToFlush(buffer, 0);
|
||||||
|
BufferUtil.writeTo(buffer, _outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendStreamCancellation(int streamId) throws IOException
|
||||||
|
{
|
||||||
|
int size = NBitInteger.octectsNeeded(6, streamId) + 1;
|
||||||
|
ByteBuffer buffer = _bufferPool.acquire(size, false);
|
||||||
|
BufferUtil.clearToFill(buffer);
|
||||||
|
buffer.put((byte)0x40);
|
||||||
|
NBitInteger.encode(buffer, 6, streamId);
|
||||||
|
BufferUtil.flipToFlush(buffer, 0);
|
||||||
|
BufferUtil.writeTo(buffer, _outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendInsertCountIncrement(int increment) throws IOException
|
||||||
|
{
|
||||||
|
int size = NBitInteger.octectsNeeded(6, increment) + 1;
|
||||||
|
ByteBuffer buffer = _bufferPool.acquire(size, false);
|
||||||
|
BufferUtil.clearToFill(buffer);
|
||||||
|
buffer.put((byte)0x00);
|
||||||
|
NBitInteger.encode(buffer, 6, increment);
|
||||||
|
BufferUtil.flipToFlush(buffer, 0);
|
||||||
|
BufferUtil.writeTo(buffer, _outputStream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,13 +13,123 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.http3.qpack;
|
package org.eclipse.jetty.http3.qpack;
|
||||||
|
|
||||||
public interface EncoderStream
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
|
import org.eclipse.jetty.io.NullByteBufferPool;
|
||||||
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
|
|
||||||
|
public class EncoderStream
|
||||||
{
|
{
|
||||||
void setCapacity(int capacity);
|
private final OutputStream _outputStream;
|
||||||
|
private final ByteBufferPool _bufferPool;
|
||||||
|
|
||||||
void insertEntry(String name, String value);
|
public EncoderStream(OutputStream outputStream)
|
||||||
|
{
|
||||||
|
this (outputStream, new NullByteBufferPool());
|
||||||
|
}
|
||||||
|
|
||||||
void insertEntry(int nameRef, String value);
|
public EncoderStream(OutputStream outputStream, ByteBufferPool bufferPool)
|
||||||
|
{
|
||||||
|
_outputStream = outputStream;
|
||||||
|
_bufferPool = bufferPool;
|
||||||
|
}
|
||||||
|
|
||||||
void insertEntry(int ref);
|
private boolean shouldHuffmanEncode(String s)
|
||||||
|
{
|
||||||
|
return s.length() > 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCapacity(int capacity) throws IOException
|
||||||
|
{
|
||||||
|
int size = NBitInteger.octectsNeeded(5, capacity) + 1;
|
||||||
|
ByteBuffer buffer = _bufferPool.acquire(size, false);
|
||||||
|
BufferUtil.clearToFill(buffer);
|
||||||
|
buffer.put((byte)0x20);
|
||||||
|
NBitInteger.encode(buffer, 5, capacity);
|
||||||
|
BufferUtil.flipToFlush(buffer, 0);
|
||||||
|
BufferUtil.writeTo(buffer, _outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertEntry(String name, String value) throws IOException
|
||||||
|
{
|
||||||
|
boolean huffmanEncodeName = shouldHuffmanEncode(name);
|
||||||
|
boolean huffmanEncodeValue = shouldHuffmanEncode(value);
|
||||||
|
|
||||||
|
int size = (huffmanEncodeName ? Huffman.octetsNeeded(name) : name.length()) +
|
||||||
|
(huffmanEncodeValue ? Huffman.octetsNeeded(value) : value.length()) + 2;
|
||||||
|
ByteBuffer buffer = _bufferPool.acquire(size, false);
|
||||||
|
BufferUtil.clearToFill(buffer);
|
||||||
|
|
||||||
|
if (huffmanEncodeName)
|
||||||
|
{
|
||||||
|
buffer.put((byte)(0x40 | 0x20));
|
||||||
|
NBitInteger.encode(buffer, 5, Huffman.octetsNeeded(name));
|
||||||
|
Huffman.encode(buffer, name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer.put((byte)(0x40));
|
||||||
|
NBitInteger.encode(buffer, 5, name.length());
|
||||||
|
buffer.put(name.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (huffmanEncodeValue)
|
||||||
|
{
|
||||||
|
buffer.put((byte)(0x80));
|
||||||
|
NBitInteger.encode(buffer, 7, Huffman.octetsNeeded(value));
|
||||||
|
Huffman.encode(buffer, value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer.put((byte)(0x00));
|
||||||
|
NBitInteger.encode(buffer, 5, value.length());
|
||||||
|
buffer.put(value.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferUtil.flipToFlush(buffer, 0);
|
||||||
|
BufferUtil.writeTo(buffer, _outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertEntry(int nameRef, boolean dynamicTableReference, String value) throws IOException
|
||||||
|
{
|
||||||
|
boolean huffmanEncode = shouldHuffmanEncode(value);
|
||||||
|
int size = NBitInteger.octectsNeeded(6, nameRef) + (huffmanEncode ? Huffman.octetsNeeded(value) : value.length()) + 2;
|
||||||
|
ByteBuffer buffer = _bufferPool.acquire(size, false);
|
||||||
|
BufferUtil.clearToFill(buffer);
|
||||||
|
|
||||||
|
// First bit indicates the instruction, second bit is whether it is a dynamic table reference or not.
|
||||||
|
buffer.put((byte)(0x80 | (dynamicTableReference ? 0x00 : 0x40)));
|
||||||
|
NBitInteger.encode(buffer, 6, nameRef);
|
||||||
|
|
||||||
|
// We will not huffman encode the string.
|
||||||
|
if (huffmanEncode)
|
||||||
|
{
|
||||||
|
buffer.put((byte)(0x80));
|
||||||
|
NBitInteger.encode(buffer, 7, Huffman.octetsNeeded(value));
|
||||||
|
Huffman.encode(buffer, value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer.put((byte)(0x00));
|
||||||
|
NBitInteger.encode(buffer, 7, value.length());
|
||||||
|
buffer.put(value.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferUtil.flipToFlush(buffer, 0);
|
||||||
|
BufferUtil.writeTo(buffer, _outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertEntry(int ref) throws IOException
|
||||||
|
{
|
||||||
|
int size = NBitInteger.octectsNeeded(5, ref) + 1;
|
||||||
|
ByteBuffer buffer = _bufferPool.acquire(size, false);
|
||||||
|
BufferUtil.clearToFill(buffer);
|
||||||
|
buffer.put((byte)0x00);
|
||||||
|
NBitInteger.encode(buffer, 5, ref);
|
||||||
|
BufferUtil.flipToFlush(buffer, 0);
|
||||||
|
BufferUtil.writeTo(buffer, _outputStream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
//
|
//
|
||||||
|
|
||||||
package org.eclipse.jetty.http3.qpack;
|
package org.eclipse.jetty.http3.qpack.parser;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
|
@ -11,10 +11,12 @@
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
//
|
//
|
||||||
|
|
||||||
package org.eclipse.jetty.http3.qpack;
|
package org.eclipse.jetty.http3.qpack.parser;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http3.qpack.QpackException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receives instructions coming from the remote Decoder as a sequence of unframed instructions.
|
* Receives instructions coming from the remote Decoder as a sequence of unframed instructions.
|
||||||
*/
|
*/
|
|
@ -11,7 +11,7 @@
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
//
|
//
|
||||||
|
|
||||||
package org.eclipse.jetty.http3.qpack;
|
package org.eclipse.jetty.http3.qpack.parser;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
|
@ -11,10 +11,13 @@
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
//
|
//
|
||||||
|
|
||||||
package org.eclipse.jetty.http3.qpack;
|
package org.eclipse.jetty.http3.qpack.parser;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http3.qpack.HuffmanDecoder;
|
||||||
|
import org.eclipse.jetty.http3.qpack.QpackException;
|
||||||
|
|
||||||
public class NBitStringParser
|
public class NBitStringParser
|
||||||
{
|
{
|
||||||
private final NBitIntegerParser _integerParser;
|
private final NBitIntegerParser _integerParser;
|
|
@ -0,0 +1,40 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||||
|
//
|
||||||
|
// This program and the accompanying materials are made available under the
|
||||||
|
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||||
|
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||||
|
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.http3.qpack;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.TypeUtil;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.equalToIgnoringCase;
|
||||||
|
|
||||||
|
public class DecoderStreamTest
|
||||||
|
{
|
||||||
|
private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
private final DecoderStream decoderStream = new DecoderStream(outputStream);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() throws Exception
|
||||||
|
{
|
||||||
|
decoderStream.sendSectionAcknowledgment(4);
|
||||||
|
assertThat(TypeUtil.toHexString(outputStream.toByteArray()), equalToIgnoringCase("84"));
|
||||||
|
outputStream.reset();
|
||||||
|
|
||||||
|
decoderStream.sendSectionAcknowledgment(1337);
|
||||||
|
assertThat(TypeUtil.toHexString(outputStream.toByteArray()), equalToIgnoringCase("FFBA09"));
|
||||||
|
outputStream.reset();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||||
|
//
|
||||||
|
// This program and the accompanying materials are made available under the
|
||||||
|
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||||
|
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||||
|
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.http3.qpack;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.TypeUtil;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.equalToIgnoringCase;
|
||||||
|
|
||||||
|
public class EncoderStreamTest
|
||||||
|
{
|
||||||
|
private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
private final EncoderStream encoderStream = new EncoderStream(outputStream);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() throws Exception
|
||||||
|
{
|
||||||
|
encoderStream.insertEntry(0, false, "www.example.com");
|
||||||
|
assertThat(TypeUtil.toHexString(outputStream.toByteArray()), equalToIgnoringCase("c00f7777772e6578616d706c652e636f6d"));
|
||||||
|
outputStream.reset();
|
||||||
|
|
||||||
|
encoderStream.insertEntry(1, false, "/sample/path");
|
||||||
|
assertThat(TypeUtil.toHexString(outputStream.toByteArray()), equalToIgnoringCase("c10c2f73616d706c652f70617468"));
|
||||||
|
outputStream.reset();
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ import java.nio.ByteBuffer;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http3.qpack.parser.EncoderInstructionParser;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.TypeUtil;
|
import org.eclipse.jetty.util.TypeUtil;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
|
|
@ -17,6 +17,7 @@ import java.nio.ByteBuffer;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http3.qpack.parser.DecoderInstructionParser;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.TypeUtil;
|
import org.eclipse.jetty.util.TypeUtil;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
|
@ -15,6 +15,7 @@ package org.eclipse.jetty.http3.qpack;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http3.qpack.parser.NBitIntegerParser;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.TypeUtil;
|
import org.eclipse.jetty.util.TypeUtil;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
Loading…
Reference in New Issue