Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/quic httpreq init #2

Open
wants to merge 10 commits into
base: feature/quic
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.netty.handler.codec.quic;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

public class QuicFrame {
public static final byte CRYPTO_TYPE = 0x06;

private final byte type;
private final byte[] offset;
private final byte[] length;
private final byte[] data;

public QuicFrame(byte type, byte[] offset, byte[] length, byte[] data) {
this.type = type;
this.offset = offset;
this.length = length;
this.data = data;
}

public ByteBuf toByteBuf() {
return Unpooled.buffer().writeByte(type)
.writeBytes(offset)
.writeBytes(length)
.writeBytes(data);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package io.netty.handler.codec.quic;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

public abstract class QuicObjectDecoder extends MessageToMessageDecoder<DatagramPacket> {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(QuicObjectDecoder.class);

private enum State {
INITIAL,
HEADER,
DCID_LEN,
DCID,
SCID_LEN,
SCID,
TOKEN_LEN,
TOKEN,
LENGTH,
PAYLOAD,
}

static int typeSpecificBits(byte b) {
return b & 0x0f;
}

static short longPacketType(byte b) {
return (short) ((b & 0x30) >> 4);
}

static boolean fixedBit(byte b) {
return (b & 0x40) >> 6 == 1;
}

static boolean headerForm(byte b) {
return (b & 0x80) >> 7 == 1;
}

private int packetNumLength;

private State state = State.INITIAL;
private int dcidLen;
private int scidLen;
private int version;
private int tokenLen;
private ByteBuf token;
private int payloadLength;
private ByteBuf payload;

protected boolean parseLongPacketHeader(ByteBuf byteBuf) {
switch (state) {
case INITIAL:
if (!byteBuf.isReadable()) {
return true;
}
final byte headerByte = byteBuf.readByte();
final boolean headerForm = headerForm(headerByte);

// only process long header packets for now...
if (!headerForm) {
logger.info("headerForm: {}", headerForm);
return true;
}

final boolean fixedBit = fixedBit(headerByte);
final short longPacketType = longPacketType(headerByte);
final int typeSpecificBits = typeSpecificBits(headerByte);
packetNumLength = headerByte & 0x3 + 1;
logger.info("headerForm: {}, fixedBit: {}, longPacketType: {}, typeSpecificBits: {}, packetNumLength: {}",
headerForm, fixedBit, longPacketType, typeSpecificBits, packetNumLength);
state = State.HEADER;
case HEADER:
if (!byteBuf.isReadable(4)) {
return true;
}
final ByteBuf versionByteBuf = byteBuf.readBytes(4);
version = versionByteBuf.copy().readInt();
logger.info("version: {}", ByteBufUtil.prettyHexDump(versionByteBuf));
state = State.DCID_LEN;
case DCID_LEN:
if (!byteBuf.isReadable()) {
return true;
}
dcidLen = byteBuf.readByte();
logger.info("dcidLen: {}", dcidLen);
state = State.DCID;
case DCID:
if (!byteBuf.isReadable(dcidLen)) {
return true;
}
final ByteBuf dcid = byteBuf.readBytes(dcidLen);
logger.info("dcid: {}", ByteBufUtil.prettyHexDump(dcid));
state = State.SCID_LEN;
case SCID_LEN:
if (!byteBuf.isReadable()) {
return true;
}
scidLen = byteBuf.readByte();
logger.info("scidLen: {}", scidLen);
state = State.SCID;
case SCID:
if (!byteBuf.isReadable(scidLen)) {
return true;
}
final ByteBuf scid = byteBuf.readBytes(scidLen);
logger.info("scid: {}", ByteBufUtil.prettyHexDump(scid));
case TOKEN_LEN:
if (!byteBuf.isReadable()) {
return true;
}
tokenLen = (int) QuicRequest.variableLengthIntegerDecoding(byteBuf);
logger.info("tokenLen: {}", tokenLen);
case TOKEN:
if (!byteBuf.isReadable(tokenLen)) {
return true;
}
token = byteBuf.readBytes(tokenLen);
logger.info("token: {}", token);
case LENGTH:
if (!byteBuf.isReadable()) {
return true;
}
payloadLength = (int) QuicRequest.variableLengthIntegerDecoding(byteBuf);
logger.info("payloadLength: {}", payloadLength);
case PAYLOAD:
if (!byteBuf.isReadable(payloadLength)) {
return true;
}
payload = byteBuf.readBytes(payloadLength);
logger.info("payload: {}", ByteBufUtil.prettyHexDump(payload));
}

ByteBuf copiedPayload = payload.copy();
ByteBuf packetNumber = copiedPayload.readBytes(packetNumLength);
logger.info("packetNumber: {}", ByteBufUtil.prettyHexDump(packetNumber));

logger.info("first byte: {}", Integer.toBinaryString(copiedPayload.getByte(copiedPayload.readerIndex()) & 0xff));
logger.info("first byte: {}", Integer.toHexString(copiedPayload.getByte(copiedPayload.readerIndex()) & 0xff));
int firstFrameType = (int) QuicRequest.variableLengthIntegerDecoding(copiedPayload);
logger.info("firstFrameType: {}", firstFrameType);

logger.info("remaining bytes: {}", ByteBufUtil.prettyHexDump(byteBuf));

// it will be identified as a Version Negotiation packet based on the Version field having a value of 0
if (version == 0) {
while (byteBuf.readableBytes() > 0) {
ByteBuf versionBytBuf = byteBuf.readBytes(4);
logger.info("supported version hexdump: {}", ByteBufUtil.hexDump(versionBytBuf));
}
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -22,9 +22,11 @@ public class QuicRequest extends QuicMessage {
private final int tokenLength;
private final byte[] packetNumber;

private final QuicFrame quicFrame;

public QuicRequest(InetSocketAddress inetSocketAddress, int headerForm, int fixedBit,
int longPacketType, int typeSpecificBits, int version, byte[] dcid, byte[] scid, int tokenLength,
byte[] packetNumber) {
byte[] packetNumber, QuicFrame quicFrame) {
this.inetSocketAddress = inetSocketAddress;
this.headerForm = headerForm;
this.fixedBit = fixedBit;
@@ -35,6 +37,7 @@ public QuicRequest(InetSocketAddress inetSocketAddress, int headerForm, int fixe
this.scid = scid.clone();
this.tokenLength = tokenLength;
this.packetNumber = packetNumber.clone();
this.quicFrame = quicFrame;
}

public InetSocketAddress getInetSocketAddress() {
@@ -43,8 +46,8 @@ public InetSocketAddress getInetSocketAddress() {

@Override
public ByteBuf getByteBuf() {
byte header = (byte) (((headerForm & 0x01) << 7) + ((fixedBit & 0x01) << 6) + ((longPacketType & 0x03) << 5) + (typeSpecificBits & 0x0f));
System.out.println(header);
final byte header = (byte) (((headerForm & 0x01) << 7) + ((fixedBit & 0x01) << 6) + ((longPacketType & 0x03) << 5) + (typeSpecificBits & 0x0f));
final ByteBuf frameByteBuf = quicFrame.toByteBuf();
return Unpooled.buffer()
.writeByte(header)
.writeInt(version)
@@ -53,8 +56,8 @@ public ByteBuf getByteBuf() {
.writeByte(scid.length - 1)
.writeBytes(scid)
.writeByte(tokenLength)
.writeBytes(variableLengthIntegerEncoding(packetNumber.length))
.writeBytes(packetNumber);
.writeBytes(variableLengthIntegerEncoding(packetNumber.length + 1 + frameByteBuf.array().length))
.writeBytes(packetNumber).writeByte(0x01).writeBytes(quicFrame.toByteBuf());
}

static byte[] variableLengthIntegerEncoding(long length) {
@@ -84,4 +87,37 @@ static byte[] variableLengthIntegerEncoding(long length) {
throw new IllegalArgumentException("invalid length: " + length);
}
}

static long variableLengthIntegerDecoding(ByteBuf byteBuf) {
if (!byteBuf.isReadable()) {
throw new IllegalArgumentException("cannot read varint");
}
final byte b1 = byteBuf.readByte();
final int varIntLen = (b1 & 0xff) >> 6;
if (varIntLen == 0) {
return b1 & 0xff >> 2;
}
if (varIntLen == 1) {
final byte[] bytes = {b1, byteBuf.readByte()};
return (bytes[0] & 0xff >> 2) << 8 | bytes[1] & 0xff;
}
if (varIntLen == 2) {
final byte[] bytes = {b1, byteBuf.readByte(), byteBuf.readByte(), byteBuf.readByte()};
return (bytes[0] & 0xff >> 2) << 24 | (bytes[1] & 0xff) << 16 |
(bytes[2] & 0xff) << 8 | bytes[3] & 0xff;
}
if (varIntLen == 3) {
final byte[] bytes = {b1, byteBuf.readByte(), byteBuf.readByte(), byteBuf.readByte(),
byteBuf.readByte(), byteBuf.readByte(), byteBuf.readByte(), byteBuf.readByte()};
return Long.valueOf(bytes[0] & 0xff >> 2) << 56 |
Long.valueOf(bytes[1] & 0xff) << 48 |
Long.valueOf(bytes[2] & 0xff) << 40 |
Long.valueOf(bytes[3] & 0xff) << 32 |
Long.valueOf(bytes[4] & 0xff) << 24 |
Long.valueOf(bytes[5] & 0xff) << 16 |
Long.valueOf(bytes[6] & 0xff) << 8 |
Long.valueOf(bytes[7] & 0xff);
}
throw new IllegalArgumentException("invalid length: " + varIntLen);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.netty.handler.codec.quic;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.ReferenceCountUtil;

import java.util.List;

public class QuicRequestDecoder extends QuicObjectDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, DatagramPacket msg, List<Object> out) throws Exception {

final ByteBuf content = msg.content();
if (parseLongPacketHeader(content)) {
return;
}

out.add(new QuicMessage(ReferenceCountUtil.retain(msg.content())));
}
}
Original file line number Diff line number Diff line change
@@ -1,119 +1,22 @@
package io.netty.handler.codec.quic;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.util.List;

public class QuicResponseDecoder extends MessageToMessageDecoder<DatagramPacket> {

private static final InternalLogger logger = InternalLoggerFactory.getInstance(QuicResponseDecoder.class);

private enum State {
INITIAL,
HEADER,
DCID_LEN,
DCID,
SCID_LEN,
SCID,
}

private State state = State.INITIAL;
private int dcidLen;
private int scidLen;
private int version;
public class QuicResponseDecoder extends QuicObjectDecoder {

@Override
protected void decode(ChannelHandlerContext ctx, DatagramPacket msg, List<Object> out) throws Exception {
final ByteBuf byteBuf = msg.content();

switch (state) {
case INITIAL:
if (!byteBuf.isReadable()) {
return;
}
final byte headerByte = byteBuf.readByte();
final boolean headerForm = headerForm(headerByte);

// only process long header packets for now...
if (!headerForm) {
logger.info("headerForm: {}", headerForm);
return;
}

final boolean fixedBit = fixedBit(headerByte);
final short longPacketType = longPacketType(headerByte);
final int typeSpecificBits = typeSpecificBits(headerByte);
logger.info("headerForm: {}, fixedBit: {}, longPacketType: {}, typeSpecificBits: {}",
headerForm, fixedBit, longPacketType, typeSpecificBits);
state = State.HEADER;
case HEADER:
if (!byteBuf.isReadable(4)) {
return;
}
final ByteBuf versionByteBuf = byteBuf.readBytes(4);
version = versionByteBuf.readInt();
logger.info("version: {}", version);
state = State.DCID_LEN;
case DCID_LEN:
if (!byteBuf.isReadable()) {
return;
}
dcidLen = byteBuf.readByte();
logger.info("dcidLen: {}", dcidLen);
state = State.DCID;
case DCID:
if (!byteBuf.isReadable()) {
return;
}
final ByteBuf dcid = byteBuf.readBytes(dcidLen);
logger.info("dcid: {}", dcid);
state = State.SCID_LEN;
case SCID_LEN:
if (!byteBuf.isReadable()) {
return;
}
scidLen = byteBuf.readByte();
logger.info("scidLen: {}", scidLen);
state = State.SCID;
case SCID:
if (!byteBuf.isReadable()) {
return;
}
final ByteBuf scid = byteBuf.readBytes(scidLen);
logger.info("scid: {}", scid);
}

// it will be identified as a Version Negotiation packet based on the Version field having a value of 0
if (version == 0) {
while (byteBuf.readableBytes() > 0) {
ByteBuf versionBytBuf = byteBuf.readBytes(4);
logger.info("supported version hexdump: {}", ByteBufUtil.hexDump(versionBytBuf));
}
if (parseLongPacketHeader(byteBuf)) {
return;
}

out.add(new QuicMessage(ReferenceCountUtil.retain(byteBuf)));
}

static int typeSpecificBits(byte b) {
return b & 0x0f;
}

static short longPacketType(byte b) {
return (short) ((b & 0x30) >> 4);
}

static boolean fixedBit(byte b) {
return (b & 0x40) >> 6 == 1;
}

static boolean headerForm(byte b) {
return (b & 0x80) >> 7 == 1;
}
}
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ public class BasicQuicTest {

private static final String ECHO_MESSAGE = "hai:)";
private static final String CHROMIUM_ENDPOINT = "https://quic.rocks:4433/";

private static final String SAMPLE_INITIAL_HEXDUMP_PACKET = "cfff00001810b57f88e894e16fda41d0f29f49677f751477bc1a6f2950e4a601dd16df156bb93535e08711004482f6fff9222619c5ff07a85338139476ccda979adbc2f04e90fbbf8f8266af3005306fbba8b0f7dc6b89e0a4485ccfeaa8fe2d6c59bc0e1b376f6f02c5bc8ac90404bd7d2b3accd2c11e519092042957e9863a9ce927b7176f7536c46489ca7710b6e485b53edc43f44f5108f6a52d783a64d24857ba2ce24b5eeafe39cf02e8547fdb814e67d5c3c60045ce6c6a4f0113c895502c63f5c52c4c6dea34c91cc0691f6764507fc0f63af13a59728b063ada930234ba9b05f5f7d4add36b15028a2d892a55c798e96bbdc3bb91490cde96c9f37d336baa287584cc93101d5284ac340bbd29c3548c098532446e0bb95508fb878c33bc21a34273d15552a0fd450da2748700550ea33c187d0fd7bb0a94b57eb0803be430483118e8fe98fbeb8608436d8f39abe30c28fba360fc4675e401d64c1bd87bdfb647422a05432d7aaeeb600c85bbfbb99bda07deb2a5faf17f8c1b5de461fb6c41faa20338e54531b3080893af30c8aab269fef41051772df2b0a05ba393505b29c8b9edfd3085f359a64c2c8749aeb82e1ac7d0f3234af47b7ec9a51dbb81debbfc72490aad40c5377298d8cf0b21bf8f8750dfe3b45833f6a425d9cd8daaeb1aac1c44e69f198b5a9015364177bcba7f5022b6fdaf123de110360b0dd619a37dc96cbe2809df1e481a7e3b500911cb49c1bfe8dca4c6230ab816f187875a4c70198534f4e3e77ec76341f00135d3d7b701f3a1f2e6743b45d3af9a00fe4938fba2ab30cb3d3c67d0a237ab0be45e1229b72280bdec7e61c01da2329967a21ced88adc79e73818eb8aa583591c85f34dfe1231b70c5769cd8945bd642b607453c853b2061fd259b57e09743e54d6cd7b6344a1e9a817b94e2272f797a0a171ac14de318986bdf723eab648f3deeb4e18211f37e79a014e7b39ff07bf84b353936de53868f70a7a3d1be70186e741eeff84461afe9905a85ea18f0ec3586f332f3ef2db99921be70f2b89450b7e45b2ae381d4ef4e32abbfd5c11736da4c871bc7eacab8a5b7238925fd3cb8acd824de004b55bdc78bc6f518666772cd945444101d27f5141926246d7e8d95009ed2763d0bf88411f5eace577850d04d15e172116fc1fcd7d246d0a837627b6e7a610365e343d7544dd46c799daf74c05ed9c1a6b71c3598b9b4f7461f45cb88c6a6005269b9f8b1b3aefad2e7c3a12831d428337699fc414bde0320dc334ba06ac88c3522f8636b50c4bc15a1d088757c62882d5495b06d4d83fee1ecff34ab20f5e7003aae70a962d7ded730fc5ee75cb2fb8601429bec3211a70c194ba903bb38e1f182612dedfad96702c6cf55046fe04f89a3534d4006f288ffbd38d89dbbbd9d37ab5c188ee45264410f9fea51d393710b1ae834a80d91edda4aa63f60bfa582bb4451408f30b1e4dbef075b6f6554b7ac31ed7520158ee2fe7bff6a7038762826c6c40553f5b567f06fc75b3f387e9ddf3f1486348158868de7166679442cdee500ef61fb76dcf80d5a7c819831276e86d8736cdb21ee2aa22e0ca55c83bafc56e74be1ad935150653f6c34dd39bb67ab2357a873f0428df56d1c6f9a583c5feac8cfa71da32b8527d0de7baf725959b862b915bd091a7ea9ab6e4e72";

@Test
public void testSimpleEcho() throws Throwable {
@@ -52,8 +52,8 @@ public void testSimpleEcho() throws Throwable {

assertEquals(ECHO_MESSAGE, result.get());

serverChannel.closeFuture().await();
clientChannel.closeFuture().await();
serverChannel.close().await();
clientChannel.close().await();
}

@Test
@@ -66,8 +66,13 @@ public void trySendInitialPacket() throws Exception {
result.set(Unpooled.copiedBuffer(res));
latch.countDown();
});
client.writeAndFlush(new QuicRequest(remote, 1, 1, 0, 0,
1, new byte[] {0}, new byte[] {0}, 0, new byte[] {0})).sync();

// final QuicFrame quicFrame = new QuicFrame((byte) 0x06, new byte[]{0}, new byte[] {0}, new byte[]{0});
// client.writeAndFlush(new QuicRequest(remote, 1, 1, 0, 0,
// 0x00000001, new byte[] {0}, new byte[] {0}, 0, new byte[] {0},
// quicFrame)).sync();
client.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(ByteBufUtil.decodeHexDump(
SAMPLE_INITIAL_HEXDUMP_PACKET)), remote)).sync();

latch.await();
assertNotNull(result.get());
181 changes: 181 additions & 0 deletions codec-quic/src/test/java/io/netty/handler/codec/quic/BasicSslTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package io.netty.handler.codec.quic;

import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import org.junit.Test;

import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLEngineResult.Status;
import javax.net.ssl.SSLException;
import java.nio.ByteBuffer;
import java.security.cert.CertificateException;

public class BasicSslTest {

private volatile SSLEngine clientSslEngine;
private volatile SSLEngine serverSslEngine;

private final SelfSignedCertificate cert = new SelfSignedCertificate();
private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);

public BasicSslTest() throws CertificateException {
}

private SSLEngine clientSslEngine() throws SSLException {
if (clientSslEngine == null) {
synchronized (this) {
if (clientSslEngine == null) {
final SslContext sslContext = SslContextBuilder.forClient().trustManager(cert.cert()).protocols(
"TLSv1.2").build();
clientSslEngine = sslContext.newEngine(UnpooledByteBufAllocator.DEFAULT);
}
}
}
return clientSslEngine;
}

private SSLEngine serverSslEngine() throws SSLException, CertificateException {
if (serverSslEngine == null) {
synchronized (this) {
if (serverSslEngine == null) {

final SslContext sslContext = SslContextBuilder.forServer(
cert.certificate(), cert.privateKey()).protocols("TLSv1", "TLSv1.1", "TLSv1.2").build();
serverSslEngine = sslContext.newEngine(UnpooledByteBufAllocator.DEFAULT);
}
}
}
return serverSslEngine;
}

@Test
public void sandboxSslTest() throws Exception {

ByteBuffer clientPacketBuf = ByteBuffer.allocate(clientSslEngine().getSession().getPacketBufferSize());
ByteBuffer serverPacketBuf = ByteBuffer.allocate(serverSslEngine().getSession().getPacketBufferSize());

ByteBuffer serverAppBuf = ByteBuffer.allocate(
serverSslEngine().getSession().getApplicationBufferSize());
ByteBuffer clientAppBuf = ByteBuffer.allocate(
clientSslEngine().getSession().getApplicationBufferSize());

// Start client side handshaking
assert clientSslEngine().getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING;

clientSslEngine().beginHandshake();

assert clientSslEngine().getHandshakeStatus() == HandshakeStatus.NEED_WRAP;

SSLEngineResult result = clientSslEngine().wrap(EMPTY_BUFFER, clientPacketBuf);

assert result.getStatus() == Status.OK;
assert result.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP;

assert serverSslEngine().getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING;

serverSslEngine().beginHandshake();

assert serverSslEngine().getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP;

clientPacketBuf.flip();
SSLEngineResult serverResult1 = serverSslEngine().unwrap(clientPacketBuf, serverAppBuf);
clientPacketBuf.compact();

assert serverSslEngine().getHandshakeStatus() == HandshakeStatus.NEED_TASK;
assert serverResult1.getStatus() == Status.OK;

Runnable task;
while ((task = serverSslEngine().getDelegatedTask()) != null) {
task.run();
}

assert serverSslEngine().getHandshakeStatus() == HandshakeStatus.NEED_WRAP;

SSLEngineResult serverResult2 = serverSslEngine().wrap(EMPTY_BUFFER, serverPacketBuf);

assert serverResult2.getStatus() == Status.OK;

serverPacketBuf.flip();
SSLEngineResult clientResult2 = clientSslEngine().unwrap(serverPacketBuf, clientAppBuf);
serverPacketBuf.compact();

assert clientResult2.getHandshakeStatus() == HandshakeStatus.NEED_TASK;
assert clientResult2.getStatus() == Status.OK;

while ((task = clientSslEngine().getDelegatedTask()) != null) {
task.run();
}

assert clientSslEngine().getHandshakeStatus() == HandshakeStatus.NEED_WRAP;

SSLEngineResult clientResult3 = clientSslEngine().wrap(EMPTY_BUFFER, clientPacketBuf);

assert clientResult3.getStatus() == Status.OK;
assert clientResult3.getHandshakeStatus() == HandshakeStatus.NEED_WRAP;

clientPacketBuf.flip();
SSLEngineResult serverResult3 = serverSslEngine().unwrap(clientPacketBuf, serverAppBuf);
clientPacketBuf.compact();

assert serverResult3.getStatus() == Status.OK;
assert serverResult3.getHandshakeStatus() == HandshakeStatus.NEED_TASK;

while ((task = serverSslEngine().getDelegatedTask()) != null) {
task.run();
}

assert serverSslEngine().getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP;

SSLEngineResult clientResult4 = clientSslEngine().wrap(EMPTY_BUFFER, clientPacketBuf);

assert clientResult4.getStatus() == Status.OK;
assert clientResult4.getHandshakeStatus() == HandshakeStatus.NEED_WRAP;

clientPacketBuf.flip();
SSLEngineResult serverResult4 = serverSslEngine().unwrap(clientPacketBuf, serverAppBuf);
clientPacketBuf.compact();

assert serverResult4.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP;
assert serverResult4.getStatus() == Status.OK;

SSLEngineResult clientResult5 = clientSslEngine().wrap(EMPTY_BUFFER, clientPacketBuf);

assert clientResult5.getStatus() == Status.OK;
assert clientResult5.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP;

clientPacketBuf.flip();
SSLEngineResult serverResult5 = serverSslEngine().unwrap(clientPacketBuf, serverAppBuf);
clientPacketBuf.compact();

assert serverResult5.getStatus() == Status.OK;
assert serverResult5.getHandshakeStatus() == HandshakeStatus.NEED_WRAP;

SSLEngineResult serverResult6 = serverSslEngine().wrap(EMPTY_BUFFER, serverPacketBuf);

assert serverResult6.getHandshakeStatus() == HandshakeStatus.NEED_WRAP;
assert serverResult6.getStatus() == Status.OK;

serverPacketBuf.flip();
SSLEngineResult clientResult6 = clientSslEngine().unwrap(serverPacketBuf, clientAppBuf);
serverPacketBuf.compact();

assert clientResult6.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP;
assert clientResult6.getStatus() == Status.OK;

SSLEngineResult serverResult7 = serverSslEngine().wrap(EMPTY_BUFFER, serverPacketBuf);

assert serverResult7.getHandshakeStatus() == HandshakeStatus.FINISHED;
assert serverResult7.getStatus() == Status.OK;

serverPacketBuf.flip();
SSLEngineResult clientResult7 = clientSslEngine().unwrap(serverPacketBuf, clientAppBuf);
serverPacketBuf.compact();

assert clientResult7.getStatus() == Status.OK;
assert clientResult7.getHandshakeStatus() == HandshakeStatus.FINISHED;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.netty.handler.codec.quic;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.channel.socket.DatagramPacket;
import org.junit.Test;

import java.net.InetSocketAddress;

public class QuicRequestDecoderTest {
private static final String SAMPLE_INITIAL_HEXDUMP_PACKET = "cfff00001810b57f88e894e16fda41d0f29f49677f751477bc1a6f2950e4a601dd16df156bb93535e08711004482f6fff9222619c5ff07a85338139476ccda979adbc2f04e90fbbf8f8266af3005306fbba8b0f7dc6b89e0a4485ccfeaa8fe2d6c59bc0e1b376f6f02c5bc8ac90404bd7d2b3accd2c11e519092042957e9863a9ce927b7176f7536c46489ca7710b6e485b53edc43f44f5108f6a52d783a64d24857ba2ce24b5eeafe39cf02e8547fdb814e67d5c3c60045ce6c6a4f0113c895502c63f5c52c4c6dea34c91cc0691f6764507fc0f63af13a59728b063ada930234ba9b05f5f7d4add36b15028a2d892a55c798e96bbdc3bb91490cde96c9f37d336baa287584cc93101d5284ac340bbd29c3548c098532446e0bb95508fb878c33bc21a34273d15552a0fd450da2748700550ea33c187d0fd7bb0a94b57eb0803be430483118e8fe98fbeb8608436d8f39abe30c28fba360fc4675e401d64c1bd87bdfb647422a05432d7aaeeb600c85bbfbb99bda07deb2a5faf17f8c1b5de461fb6c41faa20338e54531b3080893af30c8aab269fef41051772df2b0a05ba393505b29c8b9edfd3085f359a64c2c8749aeb82e1ac7d0f3234af47b7ec9a51dbb81debbfc72490aad40c5377298d8cf0b21bf8f8750dfe3b45833f6a425d9cd8daaeb1aac1c44e69f198b5a9015364177bcba7f5022b6fdaf123de110360b0dd619a37dc96cbe2809df1e481a7e3b500911cb49c1bfe8dca4c6230ab816f187875a4c70198534f4e3e77ec76341f00135d3d7b701f3a1f2e6743b45d3af9a00fe4938fba2ab30cb3d3c67d0a237ab0be45e1229b72280bdec7e61c01da2329967a21ced88adc79e73818eb8aa583591c85f34dfe1231b70c5769cd8945bd642b607453c853b2061fd259b57e09743e54d6cd7b6344a1e9a817b94e2272f797a0a171ac14de318986bdf723eab648f3deeb4e18211f37e79a014e7b39ff07bf84b353936de53868f70a7a3d1be70186e741eeff84461afe9905a85ea18f0ec3586f332f3ef2db99921be70f2b89450b7e45b2ae381d4ef4e32abbfd5c11736da4c871bc7eacab8a5b7238925fd3cb8acd824de004b55bdc78bc6f518666772cd945444101d27f5141926246d7e8d95009ed2763d0bf88411f5eace577850d04d15e172116fc1fcd7d246d0a837627b6e7a610365e343d7544dd46c799daf74c05ed9c1a6b71c3598b9b4f7461f45cb88c6a6005269b9f8b1b3aefad2e7c3a12831d428337699fc414bde0320dc334ba06ac88c3522f8636b50c4bc15a1d088757c62882d5495b06d4d83fee1ecff34ab20f5e7003aae70a962d7ded730fc5ee75cb2fb8601429bec3211a70c194ba903bb38e1f182612dedfad96702c6cf55046fe04f89a3534d4006f288ffbd38d89dbbbd9d37ab5c188ee45264410f9fea51d393710b1ae834a80d91edda4aa63f60bfa582bb4451408f30b1e4dbef075b6f6554b7ac31ed7520158ee2fe7bff6a7038762826c6c40553f5b567f06fc75b3f387e9ddf3f1486348158868de7166679442cdee500ef61fb76dcf80d5a7c819831276e86d8736cdb21ee2aa22e0ca55c83bafc56e74be1ad935150653f6c34dd39bb67ab2357a873f0428df56d1c6f9a583c5feac8cfa71da32b8527d0de7baf725959b862b915bd091a7ea9ab6e4e72";

@Test
public void testEncoding() {
final byte[] bytes = ByteBufUtil.decodeHexDump(SAMPLE_INITIAL_HEXDUMP_PACKET);
final ByteBuf byteBuf = Unpooled.copiedBuffer(bytes);
final InetSocketAddress remote = new InetSocketAddress(0);
final DatagramPacket datagramPacket = new DatagramPacket(byteBuf, remote);

final EmbeddedChannel channel = new EmbeddedChannel(new QuicRequestDecoder());
channel.writeInbound(datagramPacket);

final QuicMessage quicMessage = channel.readInbound();
System.out.println(quicMessage);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package io.netty.handler.codec.quic;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import org.junit.Test;

import static org.junit.Assert.*;
@@ -14,24 +16,52 @@ public void test1byteEncoding() {
assertEquals("25", ByteBufUtil.hexDump(retval));
}

@Test
public void test1byteDecoding() {
final ByteBuf byteBuf = Unpooled.copiedBuffer(ByteBufUtil.decodeHexDump("25"));
final long result = QuicRequest.variableLengthIntegerDecoding(byteBuf);
assertEquals(37, result);
}

@Test
public void test2byteEncoding() {
final long length = 15293;
final byte[] retval = QuicRequest.variableLengthIntegerEncoding(length);
assertEquals("7bbd", ByteBufUtil.hexDump(retval));
}

@Test
public void test2byteDecoding() {
final ByteBuf byteBuf = Unpooled.copiedBuffer(ByteBufUtil.decodeHexDump("7bbd"));
final long result = QuicRequest.variableLengthIntegerDecoding(byteBuf);
assertEquals(15293, result);
}

@Test
public void test4byteEncoding() {
final long length = 494878333;
final byte[] retval = QuicRequest.variableLengthIntegerEncoding(length);
assertEquals("9d7f3e7d", ByteBufUtil.hexDump(retval));
}

@Test
public void test4byteDecoding() {
final ByteBuf byteBuf = Unpooled.copiedBuffer(ByteBufUtil.decodeHexDump("9d7f3e7d"));
final long result = QuicRequest.variableLengthIntegerDecoding(byteBuf);
assertEquals(494878333, result);
}

@Test
public void test8byteEncoding() {
final long length = 151288809941952652L;
final byte[] retval = QuicRequest.variableLengthIntegerEncoding(length);
assertEquals("c2197c5eff14e88c", ByteBufUtil.hexDump(retval));
}

@Test
public void test8byteDecoding() {
final ByteBuf byteBuf = Unpooled.copiedBuffer(ByteBufUtil.decodeHexDump("c2197c5eff14e88c"));
final long result = QuicRequest.variableLengthIntegerDecoding(byteBuf);
assertEquals(151288809941952652L, result);
}
}