Issue #6728 - Fixes for QPACK
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
parent
0d2fe5657a
commit
80d2cee238
|
@ -1,3 +1,16 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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;
|
||||
|
||||
public enum ErrorCode
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.internal.parser;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
|
|
@ -43,13 +43,32 @@ public class QpackDecoder implements Dumpable
|
|||
public static final Logger LOG = LoggerFactory.getLogger(QpackDecoder.class);
|
||||
|
||||
private final List<Instruction> _instructions = new ArrayList<>();
|
||||
private final List<MetaDataNotification> _metaDataNotifications = new ArrayList<>();
|
||||
private final Instruction.Handler _handler;
|
||||
private final QpackContext _context;
|
||||
private final DecoderInstructionParser _parser;
|
||||
private final List<EncodedFieldSection> _encodedFieldSections = new ArrayList<>();
|
||||
private final NBitIntegerParser _integerDecoder = new NBitIntegerParser();
|
||||
private final int _maxHeaderSize;
|
||||
private MetaData _metaData;
|
||||
|
||||
private static class MetaDataNotification
|
||||
{
|
||||
private final MetaData _metaData;
|
||||
private final Handler _handler;
|
||||
private final long _streamId;
|
||||
|
||||
public MetaDataNotification(long streamId, MetaData metaData, Handler handler)
|
||||
{
|
||||
_streamId = streamId;
|
||||
_metaData = metaData;
|
||||
_handler = handler;
|
||||
}
|
||||
|
||||
public void notifyHandler()
|
||||
{
|
||||
_handler.onMetaData(_streamId, _metaData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxHeaderSize The maximum allowed size of a headers block, expressed as total of all name and value characters, plus 32 per field
|
||||
|
@ -82,13 +101,13 @@ public class QpackDecoder implements Dumpable
|
|||
throw new QpackException.SessionException("431 Request Header Fields too large");
|
||||
|
||||
_integerDecoder.setPrefix(8);
|
||||
int encodedInsertCount = _integerDecoder.decode(buffer);
|
||||
int encodedInsertCount = _integerDecoder.decodeInt(buffer);
|
||||
if (encodedInsertCount < 0)
|
||||
throw new QpackException.CompressionException("Could not parse Required Insert Count");
|
||||
|
||||
_integerDecoder.setPrefix(7);
|
||||
boolean signBit = (buffer.get(buffer.position()) & 0x80) != 0;
|
||||
int deltaBase = _integerDecoder.decode(buffer);
|
||||
int deltaBase = _integerDecoder.decodeInt(buffer);
|
||||
if (deltaBase < 0)
|
||||
throw new QpackException.CompressionException("Could not parse Delta Base");
|
||||
|
||||
|
@ -100,15 +119,15 @@ public class QpackDecoder implements Dumpable
|
|||
|
||||
// Parse the buffer into an Encoded Field Section.
|
||||
int base = signBit ? requiredInsertCount - deltaBase - 1 : requiredInsertCount + deltaBase;
|
||||
EncodedFieldSection encodedFieldSection = new EncodedFieldSection(streamId, requiredInsertCount, base, buffer);
|
||||
EncodedFieldSection encodedFieldSection = new EncodedFieldSection(streamId, handler, requiredInsertCount, base, buffer);
|
||||
|
||||
// Decode it straight away if we can, otherwise add it to the list of EncodedFieldSections.
|
||||
if (requiredInsertCount <= insertCount)
|
||||
{
|
||||
_metaData = encodedFieldSection.decode(_context, _maxHeaderSize);
|
||||
MetaData metaData = encodedFieldSection.decode(_context, _maxHeaderSize);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Decoded: streamId={}, metadata={}", streamId, _metaData);
|
||||
|
||||
LOG.debug("Decoded: streamId={}, metadata={}", streamId, metaData);
|
||||
_metaDataNotifications.add(new MetaDataNotification(streamId, metaData, handler));
|
||||
_instructions.add(new SectionAcknowledgmentInstruction(streamId));
|
||||
}
|
||||
else
|
||||
|
@ -118,21 +137,21 @@ public class QpackDecoder implements Dumpable
|
|||
_encodedFieldSections.add(encodedFieldSection);
|
||||
}
|
||||
|
||||
if (!_instructions.isEmpty())
|
||||
_handler.onInstructions(_instructions);
|
||||
_instructions.clear();
|
||||
|
||||
MetaData metaData = _metaData;
|
||||
_metaData = null;
|
||||
if (metaData != null)
|
||||
handler.onMetaData(streamId, metaData);
|
||||
|
||||
return metaData != null;
|
||||
boolean hadMetaData = !_metaDataNotifications.isEmpty();
|
||||
notifyInstructionHandler();
|
||||
notifyMetaDataHandler();
|
||||
return hadMetaData;
|
||||
}
|
||||
|
||||
void parseInstruction(ByteBuffer buffer) throws QpackException
|
||||
public void parseInstructionBuffer(ByteBuffer buffer) throws QpackException
|
||||
{
|
||||
_parser.parse(buffer);
|
||||
while (BufferUtil.hasContent(buffer))
|
||||
{
|
||||
_parser.parse(buffer);
|
||||
}
|
||||
|
||||
notifyInstructionHandler();
|
||||
notifyMetaDataHandler();
|
||||
}
|
||||
|
||||
private void checkEncodedFieldSections() throws QpackException
|
||||
|
@ -143,10 +162,11 @@ public class QpackDecoder implements Dumpable
|
|||
if (encodedFieldSection.getRequiredInsertCount() <= insertCount)
|
||||
{
|
||||
long streamId = encodedFieldSection.getStreamId();
|
||||
_metaData = encodedFieldSection.decode(_context, _maxHeaderSize);
|
||||
MetaData metaData = encodedFieldSection.decode(_context, _maxHeaderSize);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Decoded: streamId={}, metadata={}", streamId, _metaData);
|
||||
LOG.debug("Decoded: streamId={}, metadata={}", streamId, metaData);
|
||||
|
||||
_metaDataNotifications.add(new MetaDataNotification(streamId, metaData, encodedFieldSection.getHandler()));
|
||||
_instructions.add(new SectionAcknowledgmentInstruction(streamId));
|
||||
}
|
||||
}
|
||||
|
@ -244,6 +264,22 @@ public class QpackDecoder implements Dumpable
|
|||
return String.format("QpackDecoder@%x{%s}", hashCode(), _context);
|
||||
}
|
||||
|
||||
private void notifyInstructionHandler() throws QpackException
|
||||
{
|
||||
if (!_instructions.isEmpty())
|
||||
_handler.onInstructions(_instructions);
|
||||
_instructions.clear();
|
||||
}
|
||||
|
||||
private void notifyMetaDataHandler()
|
||||
{
|
||||
for (MetaDataNotification notification : _metaDataNotifications)
|
||||
{
|
||||
notification.notifyHandler();
|
||||
}
|
||||
_metaDataNotifications.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* This delivers notifications from the DecoderInstruction parser directly into the Decoder.
|
||||
*/
|
||||
|
|
|
@ -36,7 +36,7 @@ import org.eclipse.jetty.http3.qpack.internal.metadata.Http3Fields;
|
|||
import org.eclipse.jetty.http3.qpack.internal.parser.EncoderInstructionParser;
|
||||
import org.eclipse.jetty.http3.qpack.internal.table.DynamicTable;
|
||||
import org.eclipse.jetty.http3.qpack.internal.table.Entry;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitLongEncoder;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -85,7 +85,7 @@ public class QpackEncoder implements Dumpable
|
|||
private final Instruction.Handler _handler;
|
||||
private final QpackContext _context;
|
||||
private final int _maxBlockedStreams;
|
||||
private final Map<Integer, StreamInfo> _streamInfoMap = new HashMap<>();
|
||||
private final Map<Long, StreamInfo> _streamInfoMap = new HashMap<>();
|
||||
private final EncoderInstructionParser _parser;
|
||||
private int _knownInsertCount = 0;
|
||||
private int _blockedStreams = 0;
|
||||
|
@ -118,12 +118,16 @@ public class QpackEncoder implements Dumpable
|
|||
return !DO_NOT_HUFFMAN.contains(httpField.getHeader());
|
||||
}
|
||||
|
||||
public void parseInstruction(ByteBuffer buffer) throws QpackException
|
||||
public void parseInstructionBuffer(ByteBuffer buffer) throws QpackException
|
||||
{
|
||||
_parser.parse(buffer);
|
||||
while (BufferUtil.hasContent(buffer))
|
||||
{
|
||||
_parser.parse(buffer);
|
||||
}
|
||||
notifyInstructionHandler();
|
||||
}
|
||||
|
||||
boolean insert(HttpField field) throws QpackException
|
||||
public boolean insert(HttpField field) throws QpackException
|
||||
{
|
||||
DynamicTable dynamicTable = _context.getDynamicTable();
|
||||
|
||||
|
@ -141,6 +145,7 @@ public class QpackEncoder implements Dumpable
|
|||
int index = _context.indexOf(entry);
|
||||
dynamicTable.add(new Entry(field));
|
||||
_instructions.add(new DuplicateInstruction(index));
|
||||
notifyInstructionHandler();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -151,19 +156,18 @@ public class QpackEncoder implements Dumpable
|
|||
int index = _context.indexOf(nameEntry);
|
||||
dynamicTable.add(new Entry(field));
|
||||
_instructions.add(new IndexedNameEntryInstruction(!nameEntry.isStatic(), index, huffman, field.getValue()));
|
||||
notifyInstructionHandler();
|
||||
return true;
|
||||
}
|
||||
|
||||
dynamicTable.add(new Entry(field));
|
||||
_instructions.add(new LiteralNameEntryInstruction(field, huffman));
|
||||
|
||||
_handler.onInstructions(_instructions);
|
||||
_instructions.clear();
|
||||
|
||||
notifyInstructionHandler();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void encode(ByteBuffer buffer, int streamId, MetaData metadata) throws QpackException
|
||||
public void encode(ByteBuffer buffer, long streamId, MetaData metadata) throws QpackException
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Encoding: streamId={}, metadata={}", streamId, metadata);
|
||||
|
@ -217,9 +221,9 @@ public class QpackEncoder implements Dumpable
|
|||
int pos = BufferUtil.flipToFill(buffer);
|
||||
|
||||
// Encode the Field Section Prefix into the ByteBuffer.
|
||||
NBitLongEncoder.encode(buffer, 8, encodedInsertCount);
|
||||
NBitIntegerEncoder.encode(buffer, 8, encodedInsertCount);
|
||||
buffer.put(signBit ? (byte)0x80 : (byte)0x00);
|
||||
NBitLongEncoder.encode(buffer, 7, deltaBase);
|
||||
NBitIntegerEncoder.encode(buffer, 7, deltaBase);
|
||||
|
||||
// Encode the field lines into the ByteBuffer.
|
||||
for (EncodableEntry entry : encodableEntries)
|
||||
|
@ -228,10 +232,7 @@ public class QpackEncoder implements Dumpable
|
|||
}
|
||||
|
||||
BufferUtil.flipToFlush(buffer, pos);
|
||||
|
||||
if (!_instructions.isEmpty())
|
||||
_handler.onInstructions(_instructions);
|
||||
_instructions.clear();
|
||||
notifyInstructionHandler();
|
||||
}
|
||||
|
||||
private EncodableEntry encode(StreamInfo streamInfo, HttpField field)
|
||||
|
@ -316,7 +317,7 @@ public class QpackEncoder implements Dumpable
|
|||
_knownInsertCount += increment;
|
||||
}
|
||||
|
||||
void sectionAcknowledgement(int streamId) throws QpackException
|
||||
void sectionAcknowledgement(long streamId) throws QpackException
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("SectionAcknowledgement: streamId={}", streamId);
|
||||
|
@ -335,7 +336,7 @@ public class QpackEncoder implements Dumpable
|
|||
_streamInfoMap.remove(streamId);
|
||||
}
|
||||
|
||||
void streamCancellation(int streamId) throws QpackException
|
||||
void streamCancellation(long streamId) throws QpackException
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("StreamCancellation: streamId={}", streamId);
|
||||
|
@ -400,16 +401,23 @@ public class QpackEncoder implements Dumpable
|
|||
return (reqInsertCount % (2 * maxEntries)) + 1;
|
||||
}
|
||||
|
||||
private void notifyInstructionHandler() throws QpackException
|
||||
{
|
||||
if (!_instructions.isEmpty())
|
||||
_handler.onInstructions(_instructions);
|
||||
_instructions.clear();
|
||||
}
|
||||
|
||||
public class EncoderAdapter implements EncoderInstructionParser.Handler
|
||||
{
|
||||
@Override
|
||||
public void onSectionAcknowledgement(int streamId) throws QpackException
|
||||
public void onSectionAcknowledgement(long streamId) throws QpackException
|
||||
{
|
||||
sectionAcknowledgement(streamId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStreamCancellation(int streamId) throws QpackException
|
||||
public void onStreamCancellation(long streamId) throws QpackException
|
||||
{
|
||||
streamCancellation(streamId);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import org.eclipse.jetty.http.HttpVersion;
|
|||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||
import org.eclipse.jetty.http3.qpack.internal.table.Entry;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.HuffmanEncoder;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitLongEncoder;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder;
|
||||
|
||||
public abstract class EncodableEntry
|
||||
{
|
||||
|
@ -69,21 +69,21 @@ public abstract class EncodableEntry
|
|||
// Indexed Field Line with Static Reference.
|
||||
buffer.put((byte)(0x80 | 0x40));
|
||||
int relativeIndex = _entry.getIndex();
|
||||
NBitLongEncoder.encode(buffer, 6, relativeIndex);
|
||||
NBitIntegerEncoder.encode(buffer, 6, relativeIndex);
|
||||
}
|
||||
else if (_entry.getIndex() < base)
|
||||
{
|
||||
// Indexed Field Line with Dynamic Reference.
|
||||
buffer.put((byte)0x80);
|
||||
int relativeIndex = base - (_entry.getIndex() + 1);
|
||||
NBitLongEncoder.encode(buffer, 6, relativeIndex);
|
||||
NBitIntegerEncoder.encode(buffer, 6, relativeIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Indexed Field Line with Post-Base Index.
|
||||
buffer.put((byte)0x10);
|
||||
int relativeIndex = _entry.getIndex() - base;
|
||||
NBitLongEncoder.encode(buffer, 4, relativeIndex);
|
||||
NBitIntegerEncoder.encode(buffer, 4, relativeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,19 +95,19 @@ public abstract class EncodableEntry
|
|||
{
|
||||
// Indexed Field Line with Static Reference.
|
||||
int relativeIndex = _entry.getIndex();
|
||||
return 1 + NBitLongEncoder.octectsNeeded(6, relativeIndex);
|
||||
return 1 + NBitIntegerEncoder.octectsNeeded(6, relativeIndex);
|
||||
}
|
||||
else if (_entry.getIndex() < base)
|
||||
{
|
||||
// Indexed Field Line with Dynamic Reference.
|
||||
int relativeIndex = base - (_entry.getIndex() + 1);
|
||||
return 1 + NBitLongEncoder.octectsNeeded(6, relativeIndex);
|
||||
return 1 + NBitIntegerEncoder.octectsNeeded(6, relativeIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Indexed Field Line with Post-Base Index.
|
||||
int relativeIndex = _entry.getIndex() - base;
|
||||
return 1 + NBitLongEncoder.octectsNeeded(4, relativeIndex);
|
||||
return 1 + NBitIntegerEncoder.octectsNeeded(4, relativeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,21 +144,21 @@ public abstract class EncodableEntry
|
|||
// Literal Field Line with Static Name Reference.
|
||||
buffer.put((byte)(0x40 | 0x10 | (allowIntermediary ? 0x20 : 0x00)));
|
||||
int relativeIndex = _nameEntry.getIndex();
|
||||
NBitLongEncoder.encode(buffer, 4, relativeIndex);
|
||||
NBitIntegerEncoder.encode(buffer, 4, relativeIndex);
|
||||
}
|
||||
else if (_nameEntry.getIndex() < base)
|
||||
{
|
||||
// Literal Field Line with Dynamic Name Reference.
|
||||
buffer.put((byte)(0x40 | (allowIntermediary ? 0x20 : 0x00)));
|
||||
int relativeIndex = base - (_nameEntry.getIndex() + 1);
|
||||
NBitLongEncoder.encode(buffer, 4, relativeIndex);
|
||||
NBitIntegerEncoder.encode(buffer, 4, relativeIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Literal Field Line with Post-Base Name Reference.
|
||||
buffer.put((byte)(allowIntermediary ? 0x08 : 0x00));
|
||||
int relativeIndex = _nameEntry.getIndex() - base;
|
||||
NBitLongEncoder.encode(buffer, 3, relativeIndex);
|
||||
NBitIntegerEncoder.encode(buffer, 3, relativeIndex);
|
||||
}
|
||||
|
||||
// Encode the value.
|
||||
|
@ -166,13 +166,13 @@ public abstract class EncodableEntry
|
|||
if (_huffman)
|
||||
{
|
||||
buffer.put((byte)0x80);
|
||||
NBitLongEncoder.encode(buffer, 7, HuffmanEncoder.octetsNeeded(value));
|
||||
NBitIntegerEncoder.encode(buffer, 7, HuffmanEncoder.octetsNeeded(value));
|
||||
HuffmanEncoder.encode(buffer, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.put((byte)0x00);
|
||||
NBitLongEncoder.encode(buffer, 7, value.length());
|
||||
NBitIntegerEncoder.encode(buffer, 7, value.length());
|
||||
buffer.put(value.getBytes());
|
||||
}
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ public abstract class EncodableEntry
|
|||
String value = getValue();
|
||||
int relativeIndex = _nameEntry.getIndex() - base;
|
||||
int valueLength = _huffman ? HuffmanEncoder.octetsNeeded(value) : value.length();
|
||||
return 1 + NBitLongEncoder.octectsNeeded(4, relativeIndex) + 1 + NBitLongEncoder.octectsNeeded(7, valueLength) + valueLength;
|
||||
return 1 + NBitIntegerEncoder.octectsNeeded(4, relativeIndex) + 1 + NBitIntegerEncoder.octectsNeeded(7, valueLength) + valueLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -221,20 +221,20 @@ public abstract class EncodableEntry
|
|||
if (_huffman)
|
||||
{
|
||||
buffer.put((byte)(0x28 | allowIntermediary));
|
||||
NBitLongEncoder.encode(buffer, 3, HuffmanEncoder.octetsNeeded(name));
|
||||
NBitIntegerEncoder.encode(buffer, 3, HuffmanEncoder.octetsNeeded(name));
|
||||
HuffmanEncoder.encode(buffer, name);
|
||||
buffer.put((byte)0x80);
|
||||
NBitLongEncoder.encode(buffer, 7, HuffmanEncoder.octetsNeeded(value));
|
||||
NBitIntegerEncoder.encode(buffer, 7, HuffmanEncoder.octetsNeeded(value));
|
||||
HuffmanEncoder.encode(buffer, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: What charset should we be using? (this applies to the instruction generators as well).
|
||||
buffer.put((byte)(0x20 | allowIntermediary));
|
||||
NBitLongEncoder.encode(buffer, 3, name.length());
|
||||
NBitIntegerEncoder.encode(buffer, 3, name.length());
|
||||
buffer.put(name.getBytes());
|
||||
buffer.put((byte)0x00);
|
||||
NBitLongEncoder.encode(buffer, 7, value.length());
|
||||
NBitIntegerEncoder.encode(buffer, 7, value.length());
|
||||
buffer.put(value.getBytes());
|
||||
}
|
||||
}
|
||||
|
@ -246,7 +246,7 @@ public abstract class EncodableEntry
|
|||
String value = getValue();
|
||||
int nameLength = _huffman ? HuffmanEncoder.octetsNeeded(name) : name.length();
|
||||
int valueLength = _huffman ? HuffmanEncoder.octetsNeeded(value) : value.length();
|
||||
return 2 + NBitLongEncoder.octectsNeeded(3, nameLength) + nameLength + NBitLongEncoder.octectsNeeded(7, valueLength) + valueLength;
|
||||
return 2 + NBitIntegerEncoder.octectsNeeded(3, nameLength) + nameLength + NBitIntegerEncoder.octectsNeeded(7, valueLength) + valueLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,15 +23,15 @@ import org.eclipse.jetty.http3.qpack.internal.table.Entry;
|
|||
|
||||
public class StreamInfo implements Iterable<StreamInfo.SectionInfo>
|
||||
{
|
||||
private final int _streamId;
|
||||
private final long _streamId;
|
||||
private final Queue<SectionInfo> _sectionInfos = new LinkedList<>();
|
||||
|
||||
public StreamInfo(int streamId)
|
||||
public StreamInfo(long streamId)
|
||||
{
|
||||
_streamId = streamId;
|
||||
}
|
||||
|
||||
public int getStreamId()
|
||||
public long getStreamId()
|
||||
{
|
||||
return _streamId;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ package org.eclipse.jetty.http3.qpack.internal.instruction;
|
|||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.http3.qpack.Instruction;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitLongEncoder;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
|
||||
|
@ -37,10 +37,10 @@ public class DuplicateInstruction implements Instruction
|
|||
@Override
|
||||
public void encode(ByteBufferPool.Lease lease)
|
||||
{
|
||||
int size = NBitLongEncoder.octectsNeeded(5, _index) + 1;
|
||||
int size = NBitIntegerEncoder.octectsNeeded(5, _index) + 1;
|
||||
ByteBuffer buffer = lease.acquire(size, false);
|
||||
buffer.put((byte)0x00);
|
||||
NBitLongEncoder.encode(buffer, 5, _index);
|
||||
NBitIntegerEncoder.encode(buffer, 5, _index);
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
lease.append(buffer, true);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import java.nio.ByteBuffer;
|
|||
|
||||
import org.eclipse.jetty.http3.qpack.Instruction;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.HuffmanEncoder;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitLongEncoder;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
|
||||
|
@ -54,24 +54,24 @@ public class IndexedNameEntryInstruction implements Instruction
|
|||
@Override
|
||||
public void encode(ByteBufferPool.Lease lease)
|
||||
{
|
||||
int size = NBitLongEncoder.octectsNeeded(6, _index) + (_huffman ? HuffmanEncoder.octetsNeeded(_value) : _value.length()) + 2;
|
||||
int size = NBitIntegerEncoder.octectsNeeded(6, _index) + (_huffman ? HuffmanEncoder.octetsNeeded(_value) : _value.length()) + 2;
|
||||
ByteBuffer buffer = lease.acquire(size, false);
|
||||
|
||||
// First bit indicates the instruction, second bit is whether it is a dynamic table reference or not.
|
||||
buffer.put((byte)(0x80 | (_dynamic ? 0x00 : 0x40)));
|
||||
NBitLongEncoder.encode(buffer, 6, _index);
|
||||
NBitIntegerEncoder.encode(buffer, 6, _index);
|
||||
|
||||
// We will not huffman encode the string.
|
||||
if (_huffman)
|
||||
{
|
||||
buffer.put((byte)(0x80));
|
||||
NBitLongEncoder.encode(buffer, 7, HuffmanEncoder.octetsNeeded(_value));
|
||||
NBitIntegerEncoder.encode(buffer, 7, HuffmanEncoder.octetsNeeded(_value));
|
||||
HuffmanEncoder.encode(buffer, _value);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.put((byte)(0x00));
|
||||
NBitLongEncoder.encode(buffer, 7, _value.length());
|
||||
NBitIntegerEncoder.encode(buffer, 7, _value.length());
|
||||
buffer.put(_value.getBytes());
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ package org.eclipse.jetty.http3.qpack.internal.instruction;
|
|||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.http3.qpack.Instruction;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitLongEncoder;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
|
||||
|
@ -32,10 +32,10 @@ public class InsertCountIncrementInstruction implements Instruction
|
|||
@Override
|
||||
public void encode(ByteBufferPool.Lease lease)
|
||||
{
|
||||
int size = NBitLongEncoder.octectsNeeded(6, _increment) + 1;
|
||||
int size = NBitIntegerEncoder.octectsNeeded(6, _increment) + 1;
|
||||
ByteBuffer buffer = lease.acquire(size, false);
|
||||
buffer.put((byte)0x00);
|
||||
NBitLongEncoder.encode(buffer, 6, _increment);
|
||||
NBitIntegerEncoder.encode(buffer, 6, _increment);
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
lease.append(buffer, true);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import java.nio.ByteBuffer;
|
|||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http3.qpack.Instruction;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.HuffmanEncoder;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitLongEncoder;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
|
||||
|
@ -62,26 +62,26 @@ public class LiteralNameEntryInstruction implements Instruction
|
|||
if (_huffmanName)
|
||||
{
|
||||
buffer.put((byte)(0x40 | 0x20));
|
||||
NBitLongEncoder.encode(buffer, 5, HuffmanEncoder.octetsNeeded(_name));
|
||||
NBitIntegerEncoder.encode(buffer, 5, HuffmanEncoder.octetsNeeded(_name));
|
||||
HuffmanEncoder.encode(buffer, _name);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.put((byte)(0x40));
|
||||
NBitLongEncoder.encode(buffer, 5, _name.length());
|
||||
NBitIntegerEncoder.encode(buffer, 5, _name.length());
|
||||
buffer.put(_name.getBytes());
|
||||
}
|
||||
|
||||
if (_huffmanValue)
|
||||
{
|
||||
buffer.put((byte)(0x80));
|
||||
NBitLongEncoder.encode(buffer, 7, HuffmanEncoder.octetsNeeded(_value));
|
||||
NBitIntegerEncoder.encode(buffer, 7, HuffmanEncoder.octetsNeeded(_value));
|
||||
HuffmanEncoder.encode(buffer, _value);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.put((byte)(0x00));
|
||||
NBitLongEncoder.encode(buffer, 5, _value.length());
|
||||
NBitIntegerEncoder.encode(buffer, 5, _value.length());
|
||||
buffer.put(_value.getBytes());
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ package org.eclipse.jetty.http3.qpack.internal.instruction;
|
|||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.http3.qpack.Instruction;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitLongEncoder;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
|
||||
|
@ -32,10 +32,10 @@ public class SectionAcknowledgmentInstruction implements Instruction
|
|||
@Override
|
||||
public void encode(ByteBufferPool.Lease lease)
|
||||
{
|
||||
int size = NBitLongEncoder.octectsNeeded(7, _streamId) + 1;
|
||||
int size = NBitIntegerEncoder.octectsNeeded(7, _streamId) + 1;
|
||||
ByteBuffer buffer = lease.acquire(size, false);
|
||||
buffer.put((byte)0x80);
|
||||
NBitLongEncoder.encode(buffer, 7, _streamId);
|
||||
NBitIntegerEncoder.encode(buffer, 7, _streamId);
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
lease.append(buffer, true);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ package org.eclipse.jetty.http3.qpack.internal.instruction;
|
|||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.http3.qpack.Instruction;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitLongEncoder;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
|
||||
|
@ -37,10 +37,10 @@ public class SetCapacityInstruction implements Instruction
|
|||
@Override
|
||||
public void encode(ByteBufferPool.Lease lease)
|
||||
{
|
||||
int size = NBitLongEncoder.octectsNeeded(5, _capacity) + 1;
|
||||
int size = NBitIntegerEncoder.octectsNeeded(5, _capacity) + 1;
|
||||
ByteBuffer buffer = lease.acquire(size, false);
|
||||
buffer.put((byte)0x20);
|
||||
NBitLongEncoder.encode(buffer, 5, _capacity);
|
||||
NBitIntegerEncoder.encode(buffer, 5, _capacity);
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
lease.append(buffer, true);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ package org.eclipse.jetty.http3.qpack.internal.instruction;
|
|||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.http3.qpack.Instruction;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitLongEncoder;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
|
||||
|
@ -32,10 +32,10 @@ public class StreamCancellationInstruction implements Instruction
|
|||
@Override
|
||||
public void encode(ByteBufferPool.Lease lease)
|
||||
{
|
||||
int size = NBitLongEncoder.octectsNeeded(6, _streamId) + 1;
|
||||
int size = NBitIntegerEncoder.octectsNeeded(6, _streamId) + 1;
|
||||
ByteBuffer buffer = lease.acquire(size, false);
|
||||
buffer.put((byte)0x40);
|
||||
NBitLongEncoder.encode(buffer, 6, _streamId);
|
||||
NBitIntegerEncoder.encode(buffer, 6, _streamId);
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
lease.append(buffer, true);
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ public class DecoderInstructionParser
|
|||
continue;
|
||||
|
||||
case INDEX:
|
||||
_index = _integerParser.decode(buffer);
|
||||
_index = _integerParser.decodeInt(buffer);
|
||||
if (_index < 0)
|
||||
return;
|
||||
|
||||
|
@ -200,7 +200,7 @@ public class DecoderInstructionParser
|
|||
|
||||
private void parseDuplicate(ByteBuffer buffer) throws QpackException
|
||||
{
|
||||
int index = _integerParser.decode(buffer);
|
||||
int index = _integerParser.decodeInt(buffer);
|
||||
if (index >= 0)
|
||||
{
|
||||
reset();
|
||||
|
@ -210,7 +210,7 @@ public class DecoderInstructionParser
|
|||
|
||||
private void parseSetDynamicTableCapacity(ByteBuffer buffer) throws QpackException
|
||||
{
|
||||
int capacity = _integerParser.decode(buffer);
|
||||
int capacity = _integerParser.decodeInt(buffer);
|
||||
if (capacity >= 0)
|
||||
{
|
||||
reset();
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.util.List;
|
|||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http3.qpack.QpackDecoder;
|
||||
import org.eclipse.jetty.http3.qpack.QpackException;
|
||||
import org.eclipse.jetty.http3.qpack.internal.QpackContext;
|
||||
import org.eclipse.jetty.http3.qpack.internal.metadata.MetaDataBuilder;
|
||||
|
@ -39,12 +40,14 @@ public class EncodedFieldSection
|
|||
private final long _streamId;
|
||||
private final int _requiredInsertCount;
|
||||
private final int _base;
|
||||
private final QpackDecoder.Handler _handler;
|
||||
|
||||
public EncodedFieldSection(long streamId, int requiredInsertCount, int base, ByteBuffer content) throws QpackException
|
||||
public EncodedFieldSection(long streamId, QpackDecoder.Handler handler, int requiredInsertCount, int base, ByteBuffer content) throws QpackException
|
||||
{
|
||||
_streamId = streamId;
|
||||
_requiredInsertCount = requiredInsertCount;
|
||||
_base = base;
|
||||
_handler = handler;
|
||||
|
||||
while (content.hasRemaining())
|
||||
{
|
||||
|
@ -70,6 +73,11 @@ public class EncodedFieldSection
|
|||
return _streamId;
|
||||
}
|
||||
|
||||
public QpackDecoder.Handler getHandler()
|
||||
{
|
||||
return _handler;
|
||||
}
|
||||
|
||||
public int getRequiredInsertCount()
|
||||
{
|
||||
return _requiredInsertCount;
|
||||
|
@ -94,7 +102,7 @@ public class EncodedFieldSection
|
|||
byte firstByte = buffer.get(buffer.position());
|
||||
boolean dynamicTable = (firstByte & 0x40) == 0;
|
||||
_integerParser.setPrefix(6);
|
||||
int index = _integerParser.decode(buffer);
|
||||
int index = _integerParser.decodeInt(buffer);
|
||||
if (index < 0)
|
||||
throw new QpackException.CompressionException("Invalid Index");
|
||||
return new IndexedField(dynamicTable, index);
|
||||
|
@ -103,7 +111,7 @@ public class EncodedFieldSection
|
|||
private EncodedField parseIndexedFieldPostBase(ByteBuffer buffer) throws QpackException
|
||||
{
|
||||
_integerParser.setPrefix(4);
|
||||
int index = _integerParser.decode(buffer);
|
||||
int index = _integerParser.decodeInt(buffer);
|
||||
if (index < 0)
|
||||
throw new QpackException.CompressionException("Invalid Index");
|
||||
|
||||
|
@ -119,7 +127,7 @@ public class EncodedFieldSection
|
|||
boolean dynamicTable = (firstByte & 0x10) == 0;
|
||||
|
||||
_integerParser.setPrefix(4);
|
||||
int nameIndex = _integerParser.decode(buffer);
|
||||
int nameIndex = _integerParser.decodeInt(buffer);
|
||||
if (nameIndex < 0)
|
||||
throw new QpackException.CompressionException("Invalid Name Index");
|
||||
|
||||
|
@ -137,7 +145,7 @@ public class EncodedFieldSection
|
|||
boolean allowEncoding = (firstByte & 0x08) != 0;
|
||||
|
||||
_integerParser.setPrefix(3);
|
||||
int nameIndex = _integerParser.decode(buffer);
|
||||
int nameIndex = _integerParser.decodeInt(buffer);
|
||||
if (nameIndex < 0)
|
||||
throw new QpackException.CompressionException("Invalid Index");
|
||||
|
||||
|
|
|
@ -41,9 +41,9 @@ public class EncoderInstructionParser
|
|||
|
||||
public interface Handler
|
||||
{
|
||||
void onSectionAcknowledgement(int streamId) throws QpackException;
|
||||
void onSectionAcknowledgement(long streamId) throws QpackException;
|
||||
|
||||
void onStreamCancellation(int streamId) throws QpackException;
|
||||
void onStreamCancellation(long streamId) throws QpackException;
|
||||
|
||||
void onInsertCountIncrement(int increment) throws QpackException;
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ public class EncoderInstructionParser
|
|||
|
||||
private void parseSectionAcknowledgment(ByteBuffer buffer) throws QpackException
|
||||
{
|
||||
int streamId = _integerParser.decode(buffer);
|
||||
long streamId = _integerParser.decodeInt(buffer);
|
||||
if (streamId >= 0)
|
||||
{
|
||||
reset();
|
||||
|
@ -113,7 +113,7 @@ public class EncoderInstructionParser
|
|||
|
||||
private void parseStreamCancellation(ByteBuffer buffer) throws QpackException
|
||||
{
|
||||
int streamId = _integerParser.decode(buffer);
|
||||
long streamId = _integerParser.decodeLong(buffer);
|
||||
if (streamId >= 0)
|
||||
{
|
||||
reset();
|
||||
|
@ -123,7 +123,7 @@ public class EncoderInstructionParser
|
|||
|
||||
private void parseInsertCountIncrement(ByteBuffer buffer) throws QpackException
|
||||
{
|
||||
int increment = _integerParser.decode(buffer);
|
||||
int increment = _integerParser.decodeInt(buffer);
|
||||
if (increment >= 0)
|
||||
{
|
||||
reset();
|
||||
|
|
|
@ -18,7 +18,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.HuffmanEncoder;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitLongEncoder;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
|
||||
public class Entry
|
||||
|
@ -119,14 +119,14 @@ public class Entry
|
|||
int huffmanLen = HuffmanEncoder.octetsNeeded(value);
|
||||
if (huffmanLen < 0)
|
||||
throw new IllegalStateException("bad value");
|
||||
int lenLen = NBitLongEncoder.octectsNeeded(7, huffmanLen);
|
||||
int lenLen = NBitIntegerEncoder.octectsNeeded(7, huffmanLen);
|
||||
_huffmanValue = new byte[1 + lenLen + huffmanLen];
|
||||
ByteBuffer buffer = ByteBuffer.wrap(_huffmanValue);
|
||||
|
||||
// Indicate Huffman
|
||||
buffer.put((byte)0x80);
|
||||
// Add huffman length
|
||||
NBitLongEncoder.encode(buffer, 7, huffmanLen);
|
||||
NBitIntegerEncoder.encode(buffer, 7, huffmanLen);
|
||||
// Encode value
|
||||
HuffmanEncoder.encode(buffer, value);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ package org.eclipse.jetty.http3.qpack.internal.util;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class NBitLongEncoder
|
||||
public class NBitIntegerEncoder
|
||||
{
|
||||
public static int octectsNeeded(int n, long i)
|
||||
{
|
|
@ -18,8 +18,8 @@ import java.nio.ByteBuffer;
|
|||
public class NBitIntegerParser
|
||||
{
|
||||
private int _prefix;
|
||||
private int _total;
|
||||
private int _multiplier;
|
||||
private long _total;
|
||||
private long _multiplier;
|
||||
private boolean _started;
|
||||
|
||||
public void setPrefix(int prefix)
|
||||
|
@ -29,7 +29,12 @@ public class NBitIntegerParser
|
|||
_prefix = prefix;
|
||||
}
|
||||
|
||||
public int decode(ByteBuffer buffer)
|
||||
public int decodeInt(ByteBuffer buffer)
|
||||
{
|
||||
return Math.toIntExact(decodeLong(buffer));
|
||||
}
|
||||
|
||||
public long decodeLong(ByteBuffer buffer)
|
||||
{
|
||||
if (!_started)
|
||||
{
|
||||
|
@ -42,7 +47,7 @@ public class NBitIntegerParser
|
|||
_total = buffer.get() & nbits;
|
||||
if (_total < nbits)
|
||||
{
|
||||
int total = _total;
|
||||
long total = _total;
|
||||
reset();
|
||||
return total;
|
||||
}
|
||||
|
@ -55,11 +60,11 @@ public class NBitIntegerParser
|
|||
return -1;
|
||||
|
||||
int b = buffer.get() & 0xFF;
|
||||
_total += (b & 127) * _multiplier;
|
||||
_multiplier = _multiplier * 128;
|
||||
_total = Math.addExact(_total, (b & 127) * _multiplier);
|
||||
_multiplier = Math.multiplyExact(_multiplier, 128);
|
||||
if ((b & 128) == 0)
|
||||
{
|
||||
int total = _total;
|
||||
long total = _total;
|
||||
reset();
|
||||
return total;
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ public class NBitStringParser
|
|||
continue;
|
||||
|
||||
case LENGTH:
|
||||
_length = _integerParser.decode(buffer);
|
||||
_length = _integerParser.decodeInt(buffer);
|
||||
if (_length < 0)
|
||||
return null;
|
||||
_state = State.VALUE;
|
||||
|
|
|
@ -75,7 +75,7 @@ public class EncodeDecodeTest
|
|||
public void test() throws Exception
|
||||
{
|
||||
// B.1. Literal Field Line With Name Reference.
|
||||
int streamId = 0;
|
||||
long streamId = 0;
|
||||
HttpFields httpFields = HttpFields.build().add(":path", "/index.html");
|
||||
|
||||
ByteBuffer buffer = BufferUtil.allocate(1024);
|
||||
|
|
|
@ -44,35 +44,35 @@ public class EncoderInstructionParserTest
|
|||
String encoded = "84";
|
||||
ByteBuffer buffer = BufferUtil.toBuffer(TypeUtil.fromHexString(encoded));
|
||||
_instructionParser.parse(buffer);
|
||||
assertThat(_handler.sectionAcknowledgements.poll(), is(4));
|
||||
assertThat(_handler.sectionAcknowledgements.poll(), is(4L));
|
||||
assertTrue(_handler.isEmpty());
|
||||
|
||||
// 1111 1110 == FE is largest value we can do without continuation should be stream ID 126.
|
||||
encoded = "FE";
|
||||
buffer = BufferUtil.toBuffer(TypeUtil.fromHexString(encoded));
|
||||
_instructionParser.parse(buffer);
|
||||
assertThat(_handler.sectionAcknowledgements.poll(), is(126));
|
||||
assertThat(_handler.sectionAcknowledgements.poll(), is(126L));
|
||||
assertTrue(_handler.isEmpty());
|
||||
|
||||
// 1111 1111 0000 0000 == FF00 is next value, stream id 127.
|
||||
encoded = "FF00";
|
||||
buffer = BufferUtil.toBuffer(TypeUtil.fromHexString(encoded));
|
||||
_instructionParser.parse(buffer);
|
||||
assertThat(_handler.sectionAcknowledgements.poll(), is(127));
|
||||
assertThat(_handler.sectionAcknowledgements.poll(), is(127L));
|
||||
assertTrue(_handler.isEmpty());
|
||||
|
||||
// 1111 1111 0000 0001 == FF01 is next value, stream id 128.
|
||||
encoded = "FF01";
|
||||
buffer = BufferUtil.toBuffer(TypeUtil.fromHexString(encoded));
|
||||
_instructionParser.parse(buffer);
|
||||
assertThat(_handler.sectionAcknowledgements.poll(), is(128));
|
||||
assertThat(_handler.sectionAcknowledgements.poll(), is(128L));
|
||||
assertTrue(_handler.isEmpty());
|
||||
|
||||
// FFBA09 contains section ack with stream ID of 1337, this contains an octet with continuation bit.
|
||||
encoded = "FFBA09";
|
||||
buffer = BufferUtil.toBuffer(TypeUtil.fromHexString(encoded));
|
||||
_instructionParser.parse(buffer);
|
||||
assertThat(_handler.sectionAcknowledgements.poll(), is(1337));
|
||||
assertThat(_handler.sectionAcknowledgements.poll(), is(1337L));
|
||||
assertTrue(_handler.isEmpty());
|
||||
|
||||
// Test with continuation.
|
||||
|
@ -85,7 +85,7 @@ public class EncoderInstructionParserTest
|
|||
_instructionParser.parse(buffer1);
|
||||
assertTrue(_handler.isEmpty());
|
||||
_instructionParser.parse(buffer2);
|
||||
assertThat(_handler.sectionAcknowledgements.poll(), is(1337));
|
||||
assertThat(_handler.sectionAcknowledgements.poll(), is(1337L));
|
||||
assertTrue(_handler.isEmpty());
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ public class EncoderInstructionParserTest
|
|||
// Stream Cancellation (Stream=8).
|
||||
ByteBuffer buffer = QpackTestUtil.hexToBuffer("48");
|
||||
_instructionParser.parse(buffer);
|
||||
assertThat(_handler.streamCancellations.poll(), is(8));
|
||||
assertThat(_handler.streamCancellations.poll(), is(8L));
|
||||
assertTrue(_handler.isEmpty());
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@ import org.eclipse.jetty.http3.qpack.internal.parser.EncoderInstructionParser;
|
|||
|
||||
public class EncoderParserDebugHandler implements EncoderInstructionParser.Handler
|
||||
{
|
||||
public Queue<Integer> sectionAcknowledgements = new LinkedList<>();
|
||||
public Queue<Integer> streamCancellations = new LinkedList<>();
|
||||
public Queue<Long> sectionAcknowledgements = new LinkedList<>();
|
||||
public Queue<Long> streamCancellations = new LinkedList<>();
|
||||
public Queue<Integer> insertCountIncrements = new LinkedList<>();
|
||||
|
||||
private final QpackEncoder _encoder;
|
||||
|
@ -37,7 +37,7 @@ public class EncoderParserDebugHandler implements EncoderInstructionParser.Handl
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onSectionAcknowledgement(int streamId) throws QpackException
|
||||
public void onSectionAcknowledgement(long streamId) throws QpackException
|
||||
{
|
||||
sectionAcknowledgements.add(streamId);
|
||||
if (_encoder != null)
|
||||
|
@ -45,7 +45,7 @@ public class EncoderParserDebugHandler implements EncoderInstructionParser.Handl
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onStreamCancellation(int streamId) throws QpackException
|
||||
public void onStreamCancellation(long streamId) throws QpackException
|
||||
{
|
||||
streamCancellations.add(streamId);
|
||||
if (_encoder != null)
|
||||
|
|
|
@ -50,10 +50,6 @@ public class EvictionTest
|
|||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Set the instruction bytes to be passed on to the remote Encoder/Decoder through the handler directly.
|
||||
_encoderHandler.setDecoder(_decoder);
|
||||
_decoderHandler.setEncoder(_encoder);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -65,10 +61,14 @@ public class EvictionTest
|
|||
for (int i = 0; i < 10000; i++)
|
||||
{
|
||||
HttpFields httpFields = newRandomFields(5);
|
||||
int streamId = getPositiveInt(10);
|
||||
long streamId = getPositiveInt(10);
|
||||
|
||||
_encoder.encode(encodedFields, streamId, new MetaData(HttpVersion.HTTP_3, httpFields));
|
||||
_decoder.parseInstructionBuffer(_encoderHandler.getInstructionBuffer());
|
||||
|
||||
_decoder.decode(streamId, encodedFields, _decoderHandler);
|
||||
_encoder.parseInstructionBuffer(_decoderHandler.getInstructionBuffer());
|
||||
|
||||
MetaData result = _decoderHandler.getMetaData();
|
||||
assertNotNull(result);
|
||||
|
||||
|
|
|
@ -38,10 +38,10 @@ public class NBitIntegerParserTest
|
|||
ByteBuffer buffer2 = BufferUtil.toBuffer(bytes, 2, 1);
|
||||
|
||||
parser.setPrefix(7);
|
||||
int value = parser.decode(buffer1);
|
||||
int value = parser.decodeInt(buffer1);
|
||||
assertThat(value, is(-1));
|
||||
|
||||
value = parser.decode(buffer2);
|
||||
value = parser.decodeInt(buffer2);
|
||||
assertThat(value, is(1337));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ package org.eclipse.jetty.http3.qpack;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerEncoder;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitIntegerParser;
|
||||
import org.eclipse.jetty.http3.qpack.internal.util.NBitLongEncoder;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -31,17 +31,17 @@ public class NBitIntegerTest
|
|||
@Test
|
||||
public void testOctetsNeeded()
|
||||
{
|
||||
assertEquals(0, NBitLongEncoder.octectsNeeded(5, 10));
|
||||
assertEquals(2, NBitLongEncoder.octectsNeeded(5, 1337));
|
||||
assertEquals(1, NBitLongEncoder.octectsNeeded(8, 42));
|
||||
assertEquals(3, NBitLongEncoder.octectsNeeded(8, 1337));
|
||||
assertEquals(0, NBitIntegerEncoder.octectsNeeded(5, 10));
|
||||
assertEquals(2, NBitIntegerEncoder.octectsNeeded(5, 1337));
|
||||
assertEquals(1, NBitIntegerEncoder.octectsNeeded(8, 42));
|
||||
assertEquals(3, NBitIntegerEncoder.octectsNeeded(8, 1337));
|
||||
|
||||
assertEquals(0, NBitLongEncoder.octectsNeeded(6, 62));
|
||||
assertEquals(1, NBitLongEncoder.octectsNeeded(6, 63));
|
||||
assertEquals(1, NBitLongEncoder.octectsNeeded(6, 64));
|
||||
assertEquals(2, NBitLongEncoder.octectsNeeded(6, 63 + 0x00 + 0x80 * 0x01));
|
||||
assertEquals(3, NBitLongEncoder.octectsNeeded(6, 63 + 0x00 + 0x80 * 0x80));
|
||||
assertEquals(4, NBitLongEncoder.octectsNeeded(6, 63 + 0x00 + 0x80 * 0x80 * 0x80));
|
||||
assertEquals(0, NBitIntegerEncoder.octectsNeeded(6, 62));
|
||||
assertEquals(1, NBitIntegerEncoder.octectsNeeded(6, 63));
|
||||
assertEquals(1, NBitIntegerEncoder.octectsNeeded(6, 64));
|
||||
assertEquals(2, NBitIntegerEncoder.octectsNeeded(6, 63 + 0x00 + 0x80 * 0x01));
|
||||
assertEquals(3, NBitIntegerEncoder.octectsNeeded(6, 63 + 0x00 + 0x80 * 0x80));
|
||||
assertEquals(4, NBitIntegerEncoder.octectsNeeded(6, 63 + 0x00 + 0x80 * 0x80 * 0x80));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -82,12 +82,12 @@ public class NBitIntegerTest
|
|||
int p = BufferUtil.flipToFill(buf);
|
||||
if (n < 8)
|
||||
buf.put((byte)0x00);
|
||||
NBitLongEncoder.encode(buf, n, i);
|
||||
NBitIntegerEncoder.encode(buf, n, i);
|
||||
BufferUtil.flipToFlush(buf, p);
|
||||
String r = TypeUtil.toHexString(BufferUtil.toArray(buf));
|
||||
assertEquals(expected, r);
|
||||
|
||||
assertEquals(expected.length() / 2, (n < 8 ? 1 : 0) + NBitLongEncoder.octectsNeeded(n, i));
|
||||
assertEquals(expected.length() / 2, (n < 8 ? 1 : 0) + NBitIntegerEncoder.octectsNeeded(n, i));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -126,7 +126,7 @@ public class NBitIntegerTest
|
|||
{
|
||||
ByteBuffer buf = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
_parser.setPrefix(n);
|
||||
assertEquals(expected, _parser.decode(buf));
|
||||
assertEquals(expected, _parser.decodeInt(buf));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -136,7 +136,7 @@ public class NBitIntegerTest
|
|||
int p = BufferUtil.flipToFill(buf);
|
||||
buf.put((byte)0x77);
|
||||
buf.put((byte)0xFF);
|
||||
NBitLongEncoder.encode(buf, 5, 10);
|
||||
NBitIntegerEncoder.encode(buf, 5, 10);
|
||||
BufferUtil.flipToFlush(buf, p);
|
||||
|
||||
String r = TypeUtil.toHexString(BufferUtil.toArray(buf));
|
||||
|
@ -150,7 +150,7 @@ public class NBitIntegerTest
|
|||
ByteBuffer buf = ByteBuffer.wrap(TypeUtil.fromHexString("77EaFF"));
|
||||
buf.position(1);
|
||||
_parser.setPrefix(5);
|
||||
assertEquals(10, _parser.decode(buf));
|
||||
assertEquals(10, _parser.decodeInt(buf));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -160,7 +160,7 @@ public class NBitIntegerTest
|
|||
int p = BufferUtil.flipToFill(buf);
|
||||
buf.put((byte)0x88);
|
||||
buf.put((byte)0x00);
|
||||
NBitLongEncoder.encode(buf, 5, 1337);
|
||||
NBitIntegerEncoder.encode(buf, 5, 1337);
|
||||
BufferUtil.flipToFlush(buf, p);
|
||||
|
||||
String r = TypeUtil.toHexString(BufferUtil.toArray(buf));
|
||||
|
@ -174,7 +174,7 @@ public class NBitIntegerTest
|
|||
ByteBuffer buf = ByteBuffer.wrap(TypeUtil.fromHexString("881f9a0aff"));
|
||||
buf.position(1);
|
||||
_parser.setPrefix(5);
|
||||
assertEquals(1337, _parser.decode(buf));
|
||||
assertEquals(1337, _parser.decodeInt(buf));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -184,7 +184,7 @@ public class NBitIntegerTest
|
|||
int p = BufferUtil.flipToFill(buf);
|
||||
buf.put((byte)0x88);
|
||||
buf.put((byte)0xFF);
|
||||
NBitLongEncoder.encode(buf, 8, 42);
|
||||
NBitIntegerEncoder.encode(buf, 8, 42);
|
||||
BufferUtil.flipToFlush(buf, p);
|
||||
|
||||
String r = TypeUtil.toHexString(BufferUtil.toArray(buf));
|
||||
|
@ -198,6 +198,6 @@ public class NBitIntegerTest
|
|||
ByteBuffer buf = ByteBuffer.wrap(TypeUtil.fromHexString("882aFf"));
|
||||
buf.position(1);
|
||||
_parser.setPrefix(8);
|
||||
assertEquals(42, _parser.decode(buf));
|
||||
assertEquals(42, _parser.decodeInt(buf));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,10 +35,15 @@ public class QpackTestUtil
|
|||
|
||||
public static ByteBuffer toBuffer(List<Instruction> instructions)
|
||||
{
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(new NullByteBufferPool());
|
||||
NullByteBufferPool bufferPool = new NullByteBufferPool();
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(bufferPool);
|
||||
instructions.forEach(i -> i.encode(lease));
|
||||
assertThat(lease.getSize(), is(instructions.size()));
|
||||
return lease.getByteBuffers().get(0);
|
||||
ByteBuffer combinedBuffer = BufferUtil.allocate(Math.toIntExact(lease.getTotalLength()), false);
|
||||
BufferUtil.clearToFill(combinedBuffer);
|
||||
lease.getByteBuffers().forEach(combinedBuffer::put);
|
||||
BufferUtil.flipToFlush(combinedBuffer, 0);
|
||||
return combinedBuffer;
|
||||
}
|
||||
|
||||
public static ByteBuffer hexToBuffer(String hexString)
|
||||
|
|
|
@ -13,22 +13,16 @@
|
|||
|
||||
package org.eclipse.jetty.http3.qpack;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
|
||||
public class TestDecoderHandler implements QpackDecoder.Handler, Instruction.Handler
|
||||
{
|
||||
private final Queue<MetaData> _metadataList = new LinkedList<>();
|
||||
private final Queue<Instruction> _instructionList = new LinkedList<>();
|
||||
private QpackEncoder _encoder;
|
||||
|
||||
public void setEncoder(QpackEncoder encoder)
|
||||
{
|
||||
_encoder = encoder;
|
||||
}
|
||||
private final LinkedList<MetaData> _metadataList = new LinkedList<>();
|
||||
private final LinkedList<Instruction> _instructionList = new LinkedList<>();
|
||||
|
||||
@Override
|
||||
public void onMetaData(long streamId, MetaData metadata)
|
||||
|
@ -37,11 +31,16 @@ public class TestDecoderHandler implements QpackDecoder.Handler, Instruction.Han
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onInstructions(List<Instruction> instructions) throws QpackException
|
||||
public void onInstructions(List<Instruction> instructions)
|
||||
{
|
||||
_instructionList.addAll(instructions);
|
||||
if (_encoder != null)
|
||||
_encoder.parseInstruction(QpackTestUtil.toBuffer(instructions));
|
||||
}
|
||||
|
||||
public ByteBuffer getInstructionBuffer()
|
||||
{
|
||||
ByteBuffer byteBuffer = QpackTestUtil.toBuffer(_instructionList);
|
||||
_instructionList.clear();
|
||||
return byteBuffer;
|
||||
}
|
||||
|
||||
public MetaData getMetaData()
|
||||
|
|
|
@ -13,26 +13,25 @@
|
|||
|
||||
package org.eclipse.jetty.http3.qpack;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
|
||||
public class TestEncoderHandler implements Instruction.Handler
|
||||
{
|
||||
private final Queue<Instruction> _instructionList = new LinkedList<>();
|
||||
private QpackDecoder _decoder;
|
||||
|
||||
public void setDecoder(QpackDecoder decoder)
|
||||
{
|
||||
_decoder = decoder;
|
||||
}
|
||||
private final LinkedList<Instruction> _instructionList = new LinkedList<>();
|
||||
|
||||
@Override
|
||||
public void onInstructions(List<Instruction> instructions) throws QpackException
|
||||
public void onInstructions(List<Instruction> instructions)
|
||||
{
|
||||
_instructionList.addAll(instructions);
|
||||
if (_decoder != null)
|
||||
_decoder.parseInstruction(QpackTestUtil.toBuffer(instructions));
|
||||
}
|
||||
|
||||
public ByteBuffer getInstructionBuffer()
|
||||
{
|
||||
ByteBuffer byteBuffer = QpackTestUtil.toBuffer(_instructionList);
|
||||
_instructionList.clear();
|
||||
return byteBuffer;
|
||||
}
|
||||
|
||||
public Instruction getInstruction()
|
||||
|
|
|
@ -171,7 +171,7 @@ public abstract class QuicSession
|
|||
// the remote address may change so store it again.
|
||||
this.remoteAddress = remoteAddress;
|
||||
|
||||
quicheConnection.feedCipherText(cipherBufferIn);
|
||||
quicheConnection.feedCipherText(cipherBufferIn, remoteAddress);
|
||||
|
||||
if (quicheConnection.isConnectionEstablished())
|
||||
{
|
||||
|
|
|
@ -15,6 +15,7 @@ package org.eclipse.jetty.quic.quiche;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.SecureRandom;
|
||||
|
@ -24,6 +25,7 @@ import java.util.List;
|
|||
import org.eclipse.jetty.quic.quiche.ffi.LibQuiche;
|
||||
import org.eclipse.jetty.quic.quiche.ffi.bool_pointer;
|
||||
import org.eclipse.jetty.quic.quiche.ffi.char_pointer;
|
||||
import org.eclipse.jetty.quic.quiche.ffi.netinet_h;
|
||||
import org.eclipse.jetty.quic.quiche.ffi.size_t;
|
||||
import org.eclipse.jetty.quic.quiche.ffi.size_t_pointer;
|
||||
import org.eclipse.jetty.quic.quiche.ffi.ssize_t;
|
||||
|
@ -66,7 +68,9 @@ public class QuicheConnection
|
|||
byte[] scid = new byte[connectionIdLength];
|
||||
SECURE_RANDOM.nextBytes(scid);
|
||||
LibQuiche.quiche_config libQuicheConfig = buildConfig(quicheConfig);
|
||||
LibQuiche.quiche_conn quicheConn = LibQuiche.INSTANCE.quiche_connect(peer.getHostName(), scid, new size_t(scid.length), libQuicheConfig);
|
||||
|
||||
netinet_h.sockaddr_in to = netinet_h.to_sock_addr(peer);
|
||||
LibQuiche.quiche_conn quicheConn = LibQuiche.INSTANCE.quiche_connect(peer.getHostName(), scid, new size_t(scid.length), to, new size_t(to.size()), libQuicheConfig);
|
||||
return new QuicheConnection(quicheConn, libQuicheConfig);
|
||||
}
|
||||
|
||||
|
@ -245,7 +249,7 @@ public class QuicheConnection
|
|||
* Fully consumes the {@code packetRead} buffer if the connection was accepted.
|
||||
* @return an established connection if accept succeeded, null if accept failed and negotiation should be tried.
|
||||
*/
|
||||
public static QuicheConnection tryAccept(QuicheConfig quicheConfig, TokenValidator tokenValidator, ByteBuffer packetRead) throws IOException
|
||||
public static QuicheConnection tryAccept(QuicheConfig quicheConfig, TokenValidator tokenValidator, ByteBuffer packetRead, SocketAddress peer) throws IOException
|
||||
{
|
||||
uint8_t_pointer type = new uint8_t_pointer();
|
||||
uint32_t_pointer version = new uint32_t_pointer();
|
||||
|
@ -297,7 +301,9 @@ public class QuicheConnection
|
|||
|
||||
LOG.debug(" connection creation...");
|
||||
LibQuiche.quiche_config libQuicheConfig = buildConfig(quicheConfig);
|
||||
LibQuiche.quiche_conn quicheConn = LibQuiche.INSTANCE.quiche_accept(dcid, dcid_len.getPointee(), odcid, new size_t(odcid.length), libQuicheConfig);
|
||||
|
||||
netinet_h.sockaddr_in from = netinet_h.to_sock_addr(peer);
|
||||
LibQuiche.quiche_conn quicheConn = LibQuiche.INSTANCE.quiche_accept(dcid, dcid_len.getPointee(), odcid, new size_t(odcid.length), from, new size_t(from.size()), libQuicheConfig);
|
||||
if (quicheConn == null)
|
||||
{
|
||||
LibQuiche.INSTANCE.quiche_config_free(libQuicheConfig);
|
||||
|
@ -309,7 +315,7 @@ public class QuicheConnection
|
|||
LOG.debug("accepted, immediately receiving the same packet - remaining in buffer: {}", packetRead.remaining());
|
||||
while (packetRead.hasRemaining())
|
||||
{
|
||||
quicheConnection.feedCipherText(packetRead);
|
||||
quicheConnection.feedCipherText(packetRead, peer);
|
||||
}
|
||||
return quicheConnection;
|
||||
}
|
||||
|
@ -345,15 +351,21 @@ public class QuicheConnection
|
|||
/**
|
||||
* Read the buffer of cipher text coming from the network.
|
||||
* @param buffer the buffer to read.
|
||||
* @param peer
|
||||
* @return how many bytes were consumed.
|
||||
* @throws IOException
|
||||
*/
|
||||
public synchronized int feedCipherText(ByteBuffer buffer) throws IOException
|
||||
public synchronized int feedCipherText(ByteBuffer buffer, SocketAddress peer) throws IOException
|
||||
{
|
||||
if (quicheConn == null)
|
||||
throw new IOException("Cannot receive when not connected");
|
||||
|
||||
int received = libQuiche().quiche_conn_recv(quicheConn, buffer, new size_t(buffer.remaining())).intValue();
|
||||
LibQuiche.quiche_recv_info info = new LibQuiche.quiche_recv_info();
|
||||
netinet_h.sockaddr_in from = netinet_h.to_sock_addr(peer);
|
||||
info.from = netinet_h.sockaddr_in.byReference(from);
|
||||
info.from_len = new size_t(from.size());
|
||||
|
||||
int received = libQuiche().quiche_conn_recv(quicheConn, buffer, new size_t(buffer.remaining()), info).intValue();
|
||||
if (received < 0)
|
||||
throw new IOException("Quiche failed to receive packet; err=" + LibQuiche.quiche_error.errToString(received));
|
||||
buffer.position(buffer.position() + received);
|
||||
|
@ -370,7 +382,12 @@ public class QuicheConnection
|
|||
{
|
||||
if (quicheConn == null)
|
||||
throw new IOException("Cannot send when not connected");
|
||||
int written = libQuiche().quiche_conn_send(quicheConn, buffer, new size_t(buffer.remaining())).intValue();
|
||||
|
||||
LibQuiche.quiche_send_info quiche_send_info = new LibQuiche.quiche_send_info();
|
||||
quiche_send_info.to = new netinet_h.sockaddr_storage();
|
||||
quiche_send_info.to_len = new size_t(quiche_send_info.to.size());
|
||||
|
||||
int written = libQuiche().quiche_conn_send(quicheConn, buffer, new size_t(buffer.remaining()), quiche_send_info).intValue();
|
||||
if (written == LibQuiche.quiche_error.QUICHE_ERR_DONE)
|
||||
return 0;
|
||||
if (written < 0L)
|
||||
|
|
|
@ -28,7 +28,7 @@ public interface LibQuiche extends Library
|
|||
{
|
||||
// This interface is a translation of the quiche.h header of a specific version.
|
||||
// It needs to be reviewed each time the native lib version changes.
|
||||
String EXPECTED_QUICHE_VERSION = "0.7.0";
|
||||
String EXPECTED_QUICHE_VERSION = "0.9.0";
|
||||
|
||||
// load the native lib
|
||||
LibQuiche INSTANCE = Native.load("quiche", LibQuiche.class);
|
||||
|
@ -56,7 +56,7 @@ public interface LibQuiche extends Library
|
|||
// QUIC transport API.
|
||||
|
||||
// The current QUIC wire version.
|
||||
int QUICHE_PROTOCOL_VERSION = 0xff00001d;
|
||||
int QUICHE_PROTOCOL_VERSION = 0x00000001;
|
||||
|
||||
// The maximum length of a connection ID.
|
||||
int QUICHE_MAX_CONN_ID_LEN = 20;
|
||||
|
@ -251,7 +251,7 @@ public interface LibQuiche extends Library
|
|||
int quiche_enable_debug_logging(LoggingCallback cb, Pointer argp);
|
||||
|
||||
// Creates a new client-side connection.
|
||||
quiche_conn quiche_connect(String server_name, byte[] scid, size_t scid_len, quiche_config config);
|
||||
quiche_conn quiche_connect(String server_name, byte[] scid, size_t scid_len, netinet_h.sockaddr_in to, size_t to_len, quiche_config config);
|
||||
|
||||
interface packet_type
|
||||
{
|
||||
|
@ -304,7 +304,7 @@ public interface LibQuiche extends Library
|
|||
uint32_t version, ByteBuffer out, size_t out_len);
|
||||
|
||||
// Creates a new server-side connection.
|
||||
quiche_conn quiche_accept(byte[] scid, size_t scid_len, byte[] odcid, size_t odcid_len, quiche_config config);
|
||||
quiche_conn quiche_accept(byte[] scid, size_t scid_len, byte[] odcid, size_t odcid_len, netinet_h.sockaddr_in from, size_t from_len, quiche_config config);
|
||||
|
||||
// Returns the amount of time until the next timeout event, in milliseconds.
|
||||
uint64_t quiche_conn_timeout_as_millis(quiche_conn conn);
|
||||
|
@ -315,11 +315,33 @@ public interface LibQuiche extends Library
|
|||
// Collects and returns statistics about the connection.
|
||||
void quiche_conn_stats(quiche_conn conn, quiche_stats out);
|
||||
|
||||
@Structure.FieldOrder({"to", "to_len", "at"})
|
||||
class quiche_send_info extends Structure
|
||||
{
|
||||
public netinet_h.sockaddr_storage to;
|
||||
public size_t to_len;
|
||||
public timespec at;
|
||||
}
|
||||
|
||||
@Structure.FieldOrder({"tv_sec", "tv_nsec"})
|
||||
class timespec extends Structure
|
||||
{
|
||||
public uint64_t tv_sec;
|
||||
public long tv_nsec;
|
||||
}
|
||||
|
||||
// Writes a single QUIC packet to be sent to the peer.
|
||||
ssize_t quiche_conn_send(quiche_conn conn, ByteBuffer out, size_t out_len);
|
||||
ssize_t quiche_conn_send(quiche_conn conn, ByteBuffer out, size_t out_len, quiche_send_info out_info);
|
||||
|
||||
@Structure.FieldOrder({"from", "from_len"})
|
||||
class quiche_recv_info extends Structure
|
||||
{
|
||||
public netinet_h.sockaddr_in.ByReference from;
|
||||
public size_t from_len;
|
||||
}
|
||||
|
||||
// Processes QUIC packets received from the peer.
|
||||
ssize_t quiche_conn_recv(quiche_conn conn, ByteBuffer buf, size_t buf_len);
|
||||
ssize_t quiche_conn_recv(quiche_conn conn, ByteBuffer buf, size_t buf_len, quiche_recv_info info);
|
||||
|
||||
// Returns the negotiated ALPN protocol.
|
||||
void quiche_conn_application_proto(quiche_conn conn, char_pointer out, size_t_pointer out_len);
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.quic.quiche.ffi;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
import com.sun.jna.Structure;
|
||||
|
||||
public interface netinet_h
|
||||
{
|
||||
uint16_t AF_INET = new uint16_t(2);
|
||||
|
||||
static sockaddr_in to_sock_addr(SocketAddress socketAddress)
|
||||
{
|
||||
if (!(socketAddress instanceof InetSocketAddress))
|
||||
throw new IllegalArgumentException("Expected InetSocketAddress instance, got: " + socketAddress);
|
||||
InetSocketAddress inetSocketAddress = (InetSocketAddress)socketAddress;
|
||||
InetAddress address = inetSocketAddress.getAddress();
|
||||
if (!(address instanceof Inet4Address))
|
||||
throw new UnsupportedOperationException("TODO: only ipv4 is supported for now");
|
||||
|
||||
sockaddr_in sa = new sockaddr_in();
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_addr = new uint32_t(ByteBuffer.wrap(address.getAddress()).getInt()); // TODO: is the endianness correct?
|
||||
sa.sin_port = new uint16_t(inetSocketAddress.getPort());
|
||||
return sa;
|
||||
}
|
||||
|
||||
@Structure.FieldOrder({"sa_family", "sa_data"})
|
||||
class sockaddr extends Structure
|
||||
{
|
||||
public uint16_t sa_family;
|
||||
public byte[] sa_data = new byte[14]; // 14 bytes of protocol address
|
||||
}
|
||||
|
||||
@Structure.FieldOrder({"sin_family", "sin_port", "sin_addr", "sin_zero"})
|
||||
class sockaddr_in extends Structure
|
||||
{
|
||||
public uint16_t sin_family;
|
||||
public uint16_t sin_port;
|
||||
public uint32_t sin_addr;
|
||||
public byte[] sin_zero = new byte[8]; // padding to have the same size as sockaddr
|
||||
|
||||
public sockaddr_in()
|
||||
{
|
||||
}
|
||||
|
||||
private sockaddr_in(Pointer p)
|
||||
{
|
||||
super(p);
|
||||
read();
|
||||
}
|
||||
|
||||
public static ByReference byReference(sockaddr_in inet)
|
||||
{
|
||||
inet.write();
|
||||
return new ByReference(inet.getPointer());
|
||||
}
|
||||
|
||||
public static class ByReference extends sockaddr_in implements Structure.ByReference
|
||||
{
|
||||
private ByReference(Pointer p)
|
||||
{
|
||||
super(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Structure.FieldOrder({"sin6_family", "sin6_port", "sin6_flowinfo", "s6_addr", "sin6_scope_id"})
|
||||
class sockaddr_in6 extends Structure
|
||||
{
|
||||
public uint16_t sin6_family;
|
||||
public uint16_t sin6_port;
|
||||
public uint32_t sin6_flowinfo;
|
||||
public byte[] s6_addr = new byte[16];
|
||||
public uint32_t sin6_scope_id;
|
||||
}
|
||||
|
||||
@Structure.FieldOrder({"ss_family", "ss_zero"})
|
||||
class sockaddr_storage extends Structure
|
||||
{
|
||||
public uint16_t ss_family;
|
||||
public byte[] ss_zero = new byte[126]; // padding
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.quic.quiche.ffi;
|
||||
|
||||
import com.sun.jna.IntegerType;
|
||||
|
||||
public class uint16_t extends IntegerType
|
||||
{
|
||||
public uint16_t()
|
||||
{
|
||||
this(0);
|
||||
}
|
||||
public uint16_t(int v)
|
||||
{
|
||||
super(2, v, true);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
git clone --recursive https://github.com/cloudflare/quiche
|
||||
cd quiche
|
||||
git checkout -b tag-0.7.0 tags/0.7.0
|
||||
git checkout -b tag-0.9.0 tags/0.9.0
|
||||
cargo build --features ffi
|
||||
ls ./target/debug/libquiche.so
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ public class ServerQuicConnection extends QuicConnection
|
|||
{
|
||||
ByteBufferPool byteBufferPool = getByteBufferPool();
|
||||
// TODO make the token validator configurable
|
||||
QuicheConnection quicheConnection = QuicheConnection.tryAccept(quicheConfig, new SimpleTokenValidator((InetSocketAddress)remoteAddress), cipherBuffer);
|
||||
QuicheConnection quicheConnection = QuicheConnection.tryAccept(quicheConfig, new SimpleTokenValidator((InetSocketAddress)remoteAddress), cipherBuffer, remoteAddress);
|
||||
if (quicheConnection == null)
|
||||
{
|
||||
// TODO make the buffer size configurable
|
||||
|
|
Loading…
Reference in New Issue