Add basic implementation for the QpackDecoder.
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
parent
9d9e13cf3a
commit
08b612feca
|
@ -38,11 +38,22 @@ public class QpackContext
|
|||
|
||||
QpackContext(int maxDynamicTableSize)
|
||||
{
|
||||
_dynamicTable = new DynamicTable(maxDynamicTableSize);
|
||||
_dynamicTable = new DynamicTable();
|
||||
_dynamicTable.setCapacity(maxDynamicTableSize);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug(String.format("HdrTbl[%x] created max=%d", hashCode(), maxDynamicTableSize));
|
||||
}
|
||||
|
||||
public DynamicTable getDynamicTable()
|
||||
{
|
||||
return _dynamicTable;
|
||||
}
|
||||
|
||||
public StaticTable getStaticTable()
|
||||
{
|
||||
return __staticTable;
|
||||
}
|
||||
|
||||
public void resize(int newMaxDynamicTableSize)
|
||||
{
|
||||
_dynamicTable.setCapacity(newMaxDynamicTableSize);
|
||||
|
|
|
@ -14,14 +14,24 @@
|
|||
package org.eclipse.jetty.http3.qpack;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpTokens;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http3.qpack.generator.DuplicateInstruction;
|
||||
import org.eclipse.jetty.http3.qpack.generator.IndexedNameEntryInstruction;
|
||||
import org.eclipse.jetty.http3.qpack.generator.InsertCountIncrementInstruction;
|
||||
import org.eclipse.jetty.http3.qpack.generator.Instruction;
|
||||
import org.eclipse.jetty.http3.qpack.generator.LiteralNameEntryInstruction;
|
||||
import org.eclipse.jetty.http3.qpack.generator.SectionAcknowledgmentInstruction;
|
||||
import org.eclipse.jetty.http3.qpack.generator.SetCapacityInstruction;
|
||||
import org.eclipse.jetty.http3.qpack.parser.EncodedFieldSection;
|
||||
import org.eclipse.jetty.http3.qpack.parser.NBitIntegerParser;
|
||||
import org.eclipse.jetty.http3.qpack.table.DynamicTable;
|
||||
import org.eclipse.jetty.http3.qpack.table.Entry;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.http3.qpack.table.StaticTable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -35,19 +45,22 @@ public class QpackDecoder
|
|||
public static final HttpField.LongValueHttpField CONTENT_LENGTH_0 =
|
||||
new HttpField.LongValueHttpField(HttpHeader.CONTENT_LENGTH, 0L);
|
||||
|
||||
private final Handler _handler;
|
||||
private final QpackContext _context;
|
||||
private final MetaDataBuilder _builder;
|
||||
private int _localMaxDynamicTableSize;
|
||||
|
||||
private final List<EncodedFieldSection> _encodedFieldSections = new ArrayList<>();
|
||||
private final NBitIntegerParser _integerDecoder = new NBitIntegerParser();
|
||||
|
||||
/**
|
||||
* @param localMaxDynamicTableSize The maximum allowed size of the local dynamic header field table.
|
||||
* @param maxHeaderSize The maximum allowed size of a headers block, expressed as total of all name and value characters, plus 32 per field
|
||||
*/
|
||||
public QpackDecoder(int localMaxDynamicTableSize, int maxHeaderSize)
|
||||
public QpackDecoder(Handler handler, int localMaxDynamicTableSize, int maxHeaderSize)
|
||||
{
|
||||
_context = new QpackContext(localMaxDynamicTableSize);
|
||||
_localMaxDynamicTableSize = localMaxDynamicTableSize;
|
||||
_builder = new MetaDataBuilder(maxHeaderSize);
|
||||
_handler = handler;
|
||||
}
|
||||
|
||||
public QpackContext getQpackContext()
|
||||
|
@ -55,11 +68,6 @@ public class QpackDecoder
|
|||
return _context;
|
||||
}
|
||||
|
||||
public void setLocalMaxDynamicTableSize(int localMaxdynamciTableSize)
|
||||
{
|
||||
_localMaxDynamicTableSize = localMaxdynamciTableSize;
|
||||
}
|
||||
|
||||
public interface Handler
|
||||
{
|
||||
void onMetadata(MetaData metaData);
|
||||
|
@ -67,7 +75,7 @@ public class QpackDecoder
|
|||
void onInstruction(Instruction instruction);
|
||||
}
|
||||
|
||||
public MetaData decode(ByteBuffer buffer) throws QpackException.SessionException, QpackException.StreamException
|
||||
public void decode(ByteBuffer buffer) throws QpackException
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug(String.format("CtxTbl[%x] decoding %d octets", _context.hashCode(), buffer.remaining()));
|
||||
|
@ -76,213 +84,117 @@ public class QpackDecoder
|
|||
if (buffer.remaining() > _builder.getMaxSize())
|
||||
throw new QpackException.SessionException("431 Request Header Fields too large");
|
||||
|
||||
boolean emitted = false;
|
||||
int encodedInsertCount = _integerDecoder.decode(buffer);
|
||||
if (encodedInsertCount < 0)
|
||||
throw new QpackException.CompressionException("Could not parse Required Insert Count");
|
||||
|
||||
while (buffer.hasRemaining())
|
||||
boolean signBit = (buffer.get(buffer.position()) & 0x80) != 0;
|
||||
int deltaBase = _integerDecoder.decode(buffer);
|
||||
if (deltaBase < 0)
|
||||
throw new QpackException.CompressionException("Could not parse Delta Base");
|
||||
|
||||
// Decode the Required Insert Count using the DynamicTable state.
|
||||
DynamicTable dynamicTable = _context.getDynamicTable();
|
||||
int insertCount = dynamicTable.getInsertCount();
|
||||
int maxDynamicTableSize = dynamicTable.getMaxSize();
|
||||
int requiredInsertCount = decodeInsertCount(encodedInsertCount, insertCount, maxDynamicTableSize);
|
||||
|
||||
int base = signBit ? requiredInsertCount - deltaBase - 1 : requiredInsertCount + deltaBase;
|
||||
EncodedFieldSection encodedFieldSection = new EncodedFieldSection(requiredInsertCount, base);
|
||||
encodedFieldSection.parse(buffer);
|
||||
|
||||
int streamId = -1;
|
||||
|
||||
if (encodedFieldSection.getRequiredInsertCount() >= insertCount)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("decode {}", BufferUtil.toHexString(buffer));
|
||||
|
||||
byte b = buffer.get();
|
||||
if (b < 0)
|
||||
{
|
||||
// 7.1 indexed if the high bit is set
|
||||
int index = NBitInteger.decode(buffer, 7);
|
||||
Entry entry = _context.get(index);
|
||||
if (entry == null)
|
||||
throw new QpackException.SessionException("Unknown index %d", index);
|
||||
|
||||
if (entry.isStatic())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("decode IdxStatic {}", entry);
|
||||
// emit field
|
||||
emitted = true;
|
||||
_builder.emit(entry.getHttpField());
|
||||
|
||||
// TODO copy and add to reference set if there is room
|
||||
// _context.add(entry.getHttpField());
|
||||
MetaData metadata = encodedFieldSection.decode(_context, _builder);
|
||||
_handler.onMetadata(metadata);
|
||||
_handler.onInstruction(new SectionAcknowledgmentInstruction(streamId));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("decode Idx {}", entry);
|
||||
// emit
|
||||
emitted = true;
|
||||
_builder.emit(entry.getHttpField());
|
||||
_encodedFieldSections.add(encodedFieldSection);
|
||||
}
|
||||
}
|
||||
|
||||
public void onInstruction(Instruction instruction) throws QpackException
|
||||
{
|
||||
StaticTable staticTable = _context.getStaticTable();
|
||||
DynamicTable dynamicTable = _context.getDynamicTable();
|
||||
if (instruction instanceof SetCapacityInstruction)
|
||||
{
|
||||
int capacity = ((SetCapacityInstruction)instruction).getCapacity();
|
||||
dynamicTable.setCapacity(capacity);
|
||||
}
|
||||
else if (instruction instanceof DuplicateInstruction)
|
||||
{
|
||||
DuplicateInstruction duplicate = (DuplicateInstruction)instruction;
|
||||
Entry entry = dynamicTable.get(duplicate.getIndex());
|
||||
|
||||
// Add the new Entry to the DynamicTable.
|
||||
if (dynamicTable.add(entry) == null)
|
||||
throw new QpackException.StreamException("No space in DynamicTable");
|
||||
_handler.onInstruction(new InsertCountIncrementInstruction(1));
|
||||
}
|
||||
else if (instruction instanceof IndexedNameEntryInstruction)
|
||||
{
|
||||
IndexedNameEntryInstruction nameEntryInstruction = (IndexedNameEntryInstruction)instruction;
|
||||
int index = nameEntryInstruction.getIndex();
|
||||
String value = nameEntryInstruction.getValue();
|
||||
Entry referencedEntry = nameEntryInstruction.isDynamic() ? dynamicTable.get(index) : staticTable.get(index);
|
||||
|
||||
// Add the new Entry to the DynamicTable.
|
||||
Entry entry = new Entry(new HttpField(referencedEntry.getHttpField().getHeader(), referencedEntry.getHttpField().getName(), value));
|
||||
if (dynamicTable.add(entry) == null)
|
||||
throw new QpackException.StreamException("No space in DynamicTable");
|
||||
_handler.onInstruction(new InsertCountIncrementInstruction(1));
|
||||
}
|
||||
else if (instruction instanceof LiteralNameEntryInstruction)
|
||||
{
|
||||
LiteralNameEntryInstruction literalEntryInstruction = (LiteralNameEntryInstruction)instruction;
|
||||
String name = literalEntryInstruction.getName();
|
||||
String value = literalEntryInstruction.getValue();
|
||||
Entry entry = new Entry(new HttpField(name, value));
|
||||
|
||||
// Add the new Entry to the DynamicTable.
|
||||
if (dynamicTable.add(entry) == null)
|
||||
throw new QpackException.StreamException("No space in DynamicTable");
|
||||
_handler.onInstruction(new InsertCountIncrementInstruction(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
// look at the first nibble in detail
|
||||
byte f = (byte)((b & 0xF0) >> 4);
|
||||
String name;
|
||||
HttpHeader header;
|
||||
String value;
|
||||
throw new IllegalStateException("Invalid Encoder Instruction");
|
||||
}
|
||||
}
|
||||
|
||||
boolean indexed;
|
||||
int nameIndex;
|
||||
|
||||
switch (f)
|
||||
private static int decodeInsertCount(int encInsertCount, int totalNumInserts, int maxTableCapacity) throws QpackException
|
||||
{
|
||||
case 2: // 7.3
|
||||
case 3: // 7.3
|
||||
// change table size
|
||||
int size = NBitInteger.decode(buffer, 5);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("decode resize={}", size);
|
||||
if (size > _localMaxDynamicTableSize)
|
||||
throw new IllegalArgumentException();
|
||||
if (emitted)
|
||||
throw new QpackException.CompressionException("Dynamic table resize after fields");
|
||||
_context.resize(size);
|
||||
continue;
|
||||
if (encInsertCount == 0)
|
||||
return 0;
|
||||
|
||||
case 0: // 7.2.2
|
||||
case 1: // 7.2.3
|
||||
indexed = false;
|
||||
nameIndex = NBitInteger.decode(buffer, 4);
|
||||
break;
|
||||
int maxEntries = maxTableCapacity / 32;
|
||||
int fullRange = 2 * maxEntries;
|
||||
if (encInsertCount > fullRange)
|
||||
throw new QpackException.CompressionException("encInsertCount > fullRange");
|
||||
|
||||
case 4: // 7.2.1
|
||||
case 5: // 7.2.1
|
||||
case 6: // 7.2.1
|
||||
case 7: // 7.2.1
|
||||
indexed = true;
|
||||
nameIndex = NBitInteger.decode(buffer, 6);
|
||||
break;
|
||||
// MaxWrapped is the largest possible value of ReqInsertCount that is 0 mod 2 * MaxEntries.
|
||||
int maxValue = totalNumInserts + maxEntries;
|
||||
int maxWrapped = (maxValue / fullRange) * fullRange;
|
||||
int reqInsertCount = maxWrapped + encInsertCount - 1;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
boolean huffmanName = false;
|
||||
|
||||
// decode the name
|
||||
if (nameIndex > 0)
|
||||
// If reqInsertCount exceeds maxValue, the Encoder's value must have wrapped one fewer time.
|
||||
if (reqInsertCount > maxValue)
|
||||
{
|
||||
Entry nameEntry = _context.get(nameIndex);
|
||||
name = nameEntry.getHttpField().getName();
|
||||
header = nameEntry.getHttpField().getHeader();
|
||||
}
|
||||
else
|
||||
{
|
||||
huffmanName = (buffer.get() & 0x80) == 0x80;
|
||||
int length = NBitInteger.decode(buffer, 7);
|
||||
_builder.checkSize(length, huffmanName);
|
||||
if (huffmanName)
|
||||
name = Huffman.decode(buffer, length);
|
||||
else
|
||||
name = toASCIIString(buffer, length);
|
||||
check:
|
||||
for (int i = name.length(); i-- > 0; )
|
||||
{
|
||||
char c = name.charAt(i);
|
||||
if (c > 0xff)
|
||||
{
|
||||
_builder.streamException("Illegal header name %s", name);
|
||||
break;
|
||||
}
|
||||
HttpTokens.Token token = HttpTokens.TOKENS[0xFF & c];
|
||||
switch (token.getType())
|
||||
{
|
||||
case ALPHA:
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
{
|
||||
_builder.streamException("Uppercase header name %s", name);
|
||||
break check;
|
||||
}
|
||||
break;
|
||||
|
||||
case COLON:
|
||||
case TCHAR:
|
||||
case DIGIT:
|
||||
break;
|
||||
|
||||
default:
|
||||
_builder.streamException("Illegal header name %s", name);
|
||||
break check;
|
||||
}
|
||||
}
|
||||
header = HttpHeader.CACHE.get(name);
|
||||
if (reqInsertCount <= fullRange)
|
||||
throw new QpackException.CompressionException("reqInsertCount <= fullRange");
|
||||
reqInsertCount -= fullRange;
|
||||
}
|
||||
|
||||
// decode the value
|
||||
boolean huffmanValue = (buffer.get() & 0x80) == 0x80;
|
||||
int length = NBitInteger.decode(buffer, 7);
|
||||
_builder.checkSize(length, huffmanValue);
|
||||
if (huffmanValue)
|
||||
value = Huffman.decode(buffer, length);
|
||||
else
|
||||
value = toASCIIString(buffer, length);
|
||||
// Value of 0 must be encoded as 0.
|
||||
if (reqInsertCount == 0)
|
||||
throw new QpackException.CompressionException("reqInsertCount == 0");
|
||||
|
||||
// Make the new field
|
||||
HttpField field;
|
||||
if (header == null)
|
||||
{
|
||||
// just make a normal field and bypass header name lookup
|
||||
field = new HttpField(null, name, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// might be worthwhile to create a value HttpField if it is indexed
|
||||
// and/or of a type that may be looked up multiple times.
|
||||
switch (header)
|
||||
{
|
||||
case C_STATUS:
|
||||
if (indexed)
|
||||
field = new HttpField.IntValueHttpField(header, name, value);
|
||||
else
|
||||
field = new HttpField(header, name, value);
|
||||
break;
|
||||
|
||||
case C_AUTHORITY:
|
||||
field = new AuthorityHttpField(value);
|
||||
break;
|
||||
|
||||
case CONTENT_LENGTH:
|
||||
if ("0".equals(value))
|
||||
field = CONTENT_LENGTH_0;
|
||||
else
|
||||
field = new HttpField.LongValueHttpField(header, name, value);
|
||||
break;
|
||||
|
||||
default:
|
||||
field = new HttpField(header, name, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("decoded '{}' by {}/{}/{}",
|
||||
field,
|
||||
nameIndex > 0 ? "IdxName" : (huffmanName ? "HuffName" : "LitName"),
|
||||
huffmanValue ? "HuffVal" : "LitVal",
|
||||
indexed ? "Idx" : "");
|
||||
}
|
||||
|
||||
// emit the field
|
||||
emitted = true;
|
||||
_builder.emit(field);
|
||||
|
||||
// if indexed add to dynamic table
|
||||
if (indexed)
|
||||
_context.add(field);
|
||||
}
|
||||
}
|
||||
|
||||
return _builder.build();
|
||||
}
|
||||
|
||||
public static String toASCIIString(ByteBuffer buffer, int length)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder(length);
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
builder.append((char)(0x7F & buffer.get()));
|
||||
}
|
||||
return builder.toString();
|
||||
return reqInsertCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -30,7 +30,7 @@ public abstract class QpackException extends Exception
|
|||
*/
|
||||
public static class StreamException extends QpackException
|
||||
{
|
||||
StreamException(String messageFormat, Object... args)
|
||||
public StreamException(String messageFormat, Object... args)
|
||||
{
|
||||
super(messageFormat, args);
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ public abstract class QpackException extends Exception
|
|||
*/
|
||||
public static class SessionException extends QpackException
|
||||
{
|
||||
SessionException(String messageFormat, Object... args)
|
||||
public SessionException(String messageFormat, Object... args)
|
||||
{
|
||||
super(messageFormat, args);
|
||||
}
|
||||
|
|
|
@ -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.qpack.generator;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -15,6 +28,11 @@ public class DuplicateInstruction implements Instruction
|
|||
_index = index;
|
||||
}
|
||||
|
||||
public int getIndex()
|
||||
{
|
||||
return _index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBufferPool.Lease lease)
|
||||
{
|
||||
|
|
|
@ -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.qpack.generator;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -22,6 +35,21 @@ public class IndexedNameEntryInstruction implements Instruction
|
|||
_value = value;
|
||||
}
|
||||
|
||||
public boolean isDynamic()
|
||||
{
|
||||
return _dynamic;
|
||||
}
|
||||
|
||||
public int getIndex()
|
||||
{
|
||||
return _index;
|
||||
}
|
||||
|
||||
public String getValue()
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBufferPool.Lease lease)
|
||||
{
|
||||
|
|
|
@ -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.qpack.generator;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
|
|
@ -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.qpack.generator;
|
||||
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
|
|
|
@ -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.qpack.generator;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -22,6 +35,16 @@ public class LiteralNameEntryInstruction implements Instruction
|
|||
_value = value;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
public String getValue()
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBufferPool.Lease lease)
|
||||
{
|
||||
|
|
|
@ -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.qpack.generator;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
|
|
@ -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.qpack.generator;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -15,6 +28,11 @@ public class SetCapacityInstruction implements Instruction
|
|||
_capacity = capacity;
|
||||
}
|
||||
|
||||
public int getCapacity()
|
||||
{
|
||||
return _capacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBufferPool.Lease lease)
|
||||
{
|
||||
|
|
|
@ -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.qpack.generator;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
|
|
@ -0,0 +1,277 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.http3.qpack.parser;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http3.qpack.MetaDataBuilder;
|
||||
import org.eclipse.jetty.http3.qpack.QpackContext;
|
||||
import org.eclipse.jetty.http3.qpack.QpackException;
|
||||
|
||||
public class EncodedFieldSection
|
||||
{
|
||||
private final NBitIntegerParser _integerParser = new NBitIntegerParser();
|
||||
private final NBitStringParser _stringParser = new NBitStringParser();
|
||||
private final List<EncodedField> _encodedFields = new ArrayList<>();
|
||||
|
||||
private final int _requiredInsertCount;
|
||||
private final int _base;
|
||||
|
||||
public EncodedFieldSection(int requiredInsertCount, int base)
|
||||
{
|
||||
_requiredInsertCount = requiredInsertCount;
|
||||
_base = base;
|
||||
}
|
||||
|
||||
public int getRequiredInsertCount()
|
||||
{
|
||||
return _requiredInsertCount;
|
||||
}
|
||||
|
||||
public void parse(ByteBuffer buffer) throws QpackException
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
EncodedField encodedField;
|
||||
byte firstByte = buffer.get(buffer.position());
|
||||
if ((firstByte & 0x80) != 0)
|
||||
encodedField = parseIndexedFieldLine(buffer);
|
||||
else if ((firstByte & 0x40) != 0)
|
||||
encodedField = parseLiteralFieldLineWithNameReference(buffer);
|
||||
else if ((firstByte & 0x20) != 0)
|
||||
encodedField = parseLiteralFieldLineWithLiteralName(buffer);
|
||||
else if ((firstByte & 0x10) != 0)
|
||||
encodedField = parseIndexFieldLineWithPostBaseIndex(buffer);
|
||||
else
|
||||
encodedField = parseLiteralFieldLineWithPostBaseNameReference(buffer);
|
||||
|
||||
_encodedFields.add(encodedField);
|
||||
}
|
||||
}
|
||||
|
||||
public MetaData decode(QpackContext context, MetaDataBuilder builder) throws QpackException
|
||||
{
|
||||
if (context.getDynamicTable().getInsertCount() < _requiredInsertCount)
|
||||
throw new IllegalStateException("Required Insert Count Not Reached");
|
||||
|
||||
for (EncodedField encodedField : _encodedFields)
|
||||
{
|
||||
builder.emit(encodedField.decode(context));
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private EncodedField parseIndexedFieldLine(ByteBuffer buffer) throws QpackException
|
||||
{
|
||||
byte firstByte = buffer.get(buffer.position());
|
||||
boolean dynamicTable = (firstByte & 0x40) == 0;
|
||||
_integerParser.setPrefix(6);
|
||||
int index = _integerParser.decode(buffer);
|
||||
if (index < 0)
|
||||
throw new QpackException.CompressionException("Invalid Index");
|
||||
return new IndexedField(dynamicTable, index);
|
||||
}
|
||||
|
||||
private EncodedField parseLiteralFieldLineWithNameReference(ByteBuffer buffer) throws QpackException
|
||||
{
|
||||
byte firstByte = buffer.get(buffer.position());
|
||||
boolean allowEncoding = (firstByte & 0x20) != 0;
|
||||
boolean dynamicTable = (firstByte & 0x10) == 0;
|
||||
|
||||
_integerParser.setPrefix(4);
|
||||
int nameIndex = _integerParser.decode(buffer);
|
||||
if (nameIndex < 0)
|
||||
throw new QpackException.CompressionException("Invalid Name Index");
|
||||
|
||||
_stringParser.setPrefix(8);
|
||||
String value = _stringParser.decode(buffer);
|
||||
if (value == null)
|
||||
throw new QpackException.CompressionException("Value");
|
||||
|
||||
return new IndexedNameField(allowEncoding, dynamicTable, nameIndex, value);
|
||||
}
|
||||
|
||||
private EncodedField parseLiteralFieldLineWithLiteralName(ByteBuffer buffer) throws QpackException
|
||||
{
|
||||
byte firstByte = buffer.get(buffer.position());
|
||||
boolean allowEncoding = (firstByte & 0x10) != 0;
|
||||
|
||||
_stringParser.setPrefix(3);
|
||||
String name = _stringParser.decode(buffer);
|
||||
if (name == null)
|
||||
throw new QpackException.CompressionException("Invalid Name");
|
||||
|
||||
_stringParser.setPrefix(8);
|
||||
String value = _stringParser.decode(buffer);
|
||||
if (value == null)
|
||||
throw new QpackException.CompressionException("Invalid Value");
|
||||
|
||||
return new LiteralField(allowEncoding, name, value);
|
||||
}
|
||||
|
||||
private EncodedField parseLiteralFieldLineWithPostBaseNameReference(ByteBuffer buffer) throws QpackException
|
||||
{
|
||||
byte firstByte = buffer.get(buffer.position());
|
||||
boolean allowEncoding = (firstByte & 0x08) != 0;
|
||||
|
||||
_integerParser.setPrefix(3);
|
||||
int nameIndex = _integerParser.decode(buffer);
|
||||
if (nameIndex < 0)
|
||||
throw new QpackException.CompressionException("Invalid Index");
|
||||
|
||||
_stringParser.setPrefix(8);
|
||||
String value = _stringParser.decode(buffer);
|
||||
if (value == null)
|
||||
throw new QpackException.CompressionException("Invalid Value");
|
||||
|
||||
return new PostBaseIndexedNameField(allowEncoding, nameIndex, value);
|
||||
}
|
||||
|
||||
private EncodedField parseIndexFieldLineWithPostBaseIndex(ByteBuffer buffer) throws QpackException
|
||||
{
|
||||
_integerParser.setPrefix(4);
|
||||
int index = _integerParser.decode(buffer);
|
||||
if (index < 0)
|
||||
throw new QpackException.CompressionException("Invalid Index");
|
||||
|
||||
return new PostBaseIndexedField(index);
|
||||
}
|
||||
|
||||
public interface EncodedField
|
||||
{
|
||||
HttpField decode(QpackContext context) throws QpackException;
|
||||
}
|
||||
|
||||
private class LiteralField implements EncodedField
|
||||
{
|
||||
private final boolean _allowEncoding;
|
||||
private final String _name;
|
||||
private final String _value;
|
||||
|
||||
public LiteralField(boolean allowEncoding, String name, String value)
|
||||
{
|
||||
_allowEncoding = allowEncoding;
|
||||
_name = name;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField decode(QpackContext context)
|
||||
{
|
||||
return new HttpField(_name, _value);
|
||||
}
|
||||
}
|
||||
|
||||
private class IndexedField implements EncodedField
|
||||
{
|
||||
private final boolean _dynamicTable;
|
||||
private final int _index;
|
||||
|
||||
public IndexedField(boolean dynamicTable, int index)
|
||||
{
|
||||
_dynamicTable = dynamicTable;
|
||||
_index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField decode(QpackContext context) throws QpackException
|
||||
{
|
||||
if (_dynamicTable)
|
||||
return context.getDynamicTable().getAbsolute(_base + _index + 1).getHttpField();
|
||||
else
|
||||
return context.getStaticTable().get(_index).getHttpField();
|
||||
}
|
||||
}
|
||||
|
||||
private class PostBaseIndexedField implements EncodedField
|
||||
{
|
||||
private final int _index;
|
||||
|
||||
public PostBaseIndexedField(int index)
|
||||
{
|
||||
_index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField decode(QpackContext context) throws QpackException
|
||||
{
|
||||
return context.getDynamicTable().getAbsolute(_base - _index).getHttpField();
|
||||
}
|
||||
}
|
||||
|
||||
private class IndexedNameField implements EncodedField
|
||||
{
|
||||
private final boolean _allowEncoding;
|
||||
private final boolean _dynamicTable;
|
||||
private final int _nameIndex;
|
||||
private final String _value;
|
||||
|
||||
public IndexedNameField(boolean allowEncoding, boolean dynamicTable, int nameIndex, String value)
|
||||
{
|
||||
_allowEncoding = allowEncoding;
|
||||
_dynamicTable = dynamicTable;
|
||||
_nameIndex = nameIndex;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField decode(QpackContext context) throws QpackException
|
||||
{
|
||||
HttpField field;
|
||||
if (_dynamicTable)
|
||||
field = context.getDynamicTable().getAbsolute(_base + _nameIndex + 1).getHttpField();
|
||||
else
|
||||
field = context.getStaticTable().get(_nameIndex).getHttpField();
|
||||
|
||||
return new HttpField(field.getHeader(), field.getName(), _value);
|
||||
}
|
||||
}
|
||||
|
||||
private class PostBaseIndexedNameField implements EncodedField
|
||||
{
|
||||
private final boolean _allowEncoding;
|
||||
private final int _nameIndex;
|
||||
private final String _value;
|
||||
|
||||
public PostBaseIndexedNameField(boolean allowEncoding, int nameIndex, String value)
|
||||
{
|
||||
_allowEncoding = allowEncoding;
|
||||
_nameIndex = nameIndex;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField decode(QpackContext context) throws QpackException
|
||||
{
|
||||
HttpField field = context.getDynamicTable().getAbsolute(_base - _nameIndex).getHttpField();
|
||||
return new HttpField(field.getHeader(), field.getName(), _value);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move to QpackEncoder.
|
||||
@SuppressWarnings("unused")
|
||||
public static int encodeInsertCount(int reqInsertCount, int maxTableCapacity)
|
||||
{
|
||||
if (reqInsertCount == 0)
|
||||
return 0;
|
||||
|
||||
int maxEntries = maxTableCapacity / 32;
|
||||
return (reqInsertCount % (2 * maxEntries)) + 1;
|
||||
}
|
||||
}
|
|
@ -13,84 +13,79 @@
|
|||
|
||||
package org.eclipse.jetty.http3.qpack.table;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http3.qpack.QpackContext;
|
||||
import org.eclipse.jetty.http3.qpack.QpackException;
|
||||
|
||||
public class DynamicTable
|
||||
{
|
||||
public static final int FIRST_INDEX = StaticTable.STATIC_SIZE + 1;
|
||||
private int _maxDynamicTableSizeInBytes;
|
||||
private int _dynamicTableSizeInBytes;
|
||||
private int _maxCapacity;
|
||||
private int _capacity;
|
||||
private int _absoluteIndex;
|
||||
|
||||
private final Map<HttpField, Entry> _fieldMap = new HashMap<>();
|
||||
private final Map<String, Entry> _nameMap = new HashMap<>();
|
||||
private final int _growby;
|
||||
private final List<Entry> _entries = new ArrayList<>();
|
||||
|
||||
private Entry[] _entries;
|
||||
private int _numEntries;
|
||||
private int _offset;
|
||||
private final Map<Integer, StreamInfo> streamInfoMap = new HashMap<>();
|
||||
|
||||
public DynamicTable(int maxSize)
|
||||
public static class StreamInfo
|
||||
{
|
||||
private int streamId;
|
||||
private final List<Integer> referencedEntries = new ArrayList<>();
|
||||
private int potentiallyBlockedStreams = 0;
|
||||
}
|
||||
|
||||
public DynamicTable()
|
||||
{
|
||||
_maxDynamicTableSizeInBytes = maxSize;
|
||||
int initCapacity = 10 + maxSize / (32 + 10 + 10);
|
||||
_entries = new Entry[initCapacity];
|
||||
_growby = initCapacity;
|
||||
}
|
||||
|
||||
public Entry add(Entry entry)
|
||||
{
|
||||
evict();
|
||||
|
||||
int size = entry.getSize();
|
||||
if (size > _maxDynamicTableSizeInBytes)
|
||||
if (size + _capacity > _maxCapacity)
|
||||
{
|
||||
if (QpackContext.LOG.isDebugEnabled())
|
||||
QpackContext.LOG.debug(String.format("HdrTbl[%x] !added size %d>%d", hashCode(), size, _maxDynamicTableSizeInBytes));
|
||||
evictAll();
|
||||
QpackContext.LOG.debug(String.format("HdrTbl[%x] !added size %d>%d", hashCode(), size, _maxCapacity));
|
||||
return null;
|
||||
}
|
||||
_dynamicTableSizeInBytes += size;
|
||||
|
||||
if (_numEntries == _entries.length)
|
||||
{
|
||||
Entry[] entries = new Entry[_entries.length + _growby];
|
||||
for (int i = 0; i < _numEntries; i++)
|
||||
{
|
||||
int slot = (_offset + i) % _entries.length;
|
||||
entries[i] = _entries[slot];
|
||||
entries[i].setIndex(i);
|
||||
}
|
||||
_entries = entries;
|
||||
_offset = 0;
|
||||
}
|
||||
int slot = (_numEntries++ + _offset) % _entries.length;
|
||||
_entries[slot] = entry;
|
||||
entry.setIndex(slot);
|
||||
_capacity += size;
|
||||
|
||||
// Set the Entries absolute index which will never change.
|
||||
entry.setIndex(_absoluteIndex++);
|
||||
_entries.add(0, entry);
|
||||
_fieldMap.put(entry.getHttpField(), entry);
|
||||
_nameMap.put(entry.getHttpField().getLowerCaseName(), entry);
|
||||
|
||||
if (QpackContext.LOG.isDebugEnabled())
|
||||
QpackContext.LOG.debug(String.format("HdrTbl[%x] added %s", hashCode(), entry));
|
||||
evict();
|
||||
return entry;
|
||||
}
|
||||
|
||||
public int index(Entry entry)
|
||||
{
|
||||
return StaticTable.STATIC_SIZE + _numEntries - (entry.getIndex() - _offset + _entries.length) % _entries.length;
|
||||
// TODO: should we improve efficiency of this by storing in the entry itself.
|
||||
return _entries.indexOf(entry);
|
||||
}
|
||||
|
||||
public Entry getAbsolute(int absoluteIndex) throws QpackException
|
||||
{
|
||||
if (absoluteIndex < 0)
|
||||
throw new QpackException.CompressionException("Invalid Index");
|
||||
return _entries.stream().filter(e -> e.getIndex() == absoluteIndex).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public Entry get(int index)
|
||||
{
|
||||
int d = index - StaticTable.STATIC_SIZE - 1;
|
||||
if (d < 0 || d >= _numEntries)
|
||||
return null;
|
||||
int slot = (_offset + _numEntries - d - 1) % _entries.length;
|
||||
return _entries[slot];
|
||||
return _entries.get(index);
|
||||
}
|
||||
|
||||
public Entry get(String name)
|
||||
|
@ -105,66 +100,91 @@ public class DynamicTable
|
|||
|
||||
public int getSize()
|
||||
{
|
||||
return _dynamicTableSizeInBytes;
|
||||
return _capacity;
|
||||
}
|
||||
|
||||
public int getMaxSize()
|
||||
{
|
||||
return _maxDynamicTableSizeInBytes;
|
||||
return _maxCapacity;
|
||||
}
|
||||
|
||||
public int getNumEntries()
|
||||
{
|
||||
return _numEntries;
|
||||
return _entries.size();
|
||||
}
|
||||
|
||||
public int getInsertCount()
|
||||
{
|
||||
return _absoluteIndex;
|
||||
}
|
||||
|
||||
public void setCapacity(int capacity)
|
||||
{
|
||||
if (QpackContext.LOG.isDebugEnabled())
|
||||
QpackContext.LOG.debug(String.format("HdrTbl[%x] resized max=%d->%d", hashCode(), _maxDynamicTableSizeInBytes, capacity));
|
||||
_maxDynamicTableSizeInBytes = capacity;
|
||||
QpackContext.LOG.debug(String.format("HdrTbl[%x] resized max=%d->%d", hashCode(), _maxCapacity, capacity));
|
||||
_maxCapacity = capacity;
|
||||
evict();
|
||||
}
|
||||
|
||||
private boolean canReference(Entry entry)
|
||||
{
|
||||
int evictionThreshold = getEvictionThreshold();
|
||||
int lowestReferencableIndex = -1;
|
||||
int remainingCapacity = _capacity;
|
||||
for (int i = 0; i < _entries.size(); i++)
|
||||
{
|
||||
if (remainingCapacity <= evictionThreshold)
|
||||
{
|
||||
lowestReferencableIndex = i;
|
||||
break;
|
||||
}
|
||||
|
||||
remainingCapacity -= _entries.get(i).getSize();
|
||||
}
|
||||
|
||||
return index(entry) >= lowestReferencableIndex;
|
||||
}
|
||||
|
||||
private void evict()
|
||||
{
|
||||
while (_dynamicTableSizeInBytes > _maxDynamicTableSizeInBytes)
|
||||
int evictionThreshold = getEvictionThreshold();
|
||||
for (Entry e : _entries)
|
||||
{
|
||||
Entry entry = _entries[_offset];
|
||||
_entries[_offset] = null;
|
||||
_offset = (_offset + 1) % _entries.length;
|
||||
_numEntries--;
|
||||
// We only evict when the table is getting full.
|
||||
if (_capacity < evictionThreshold)
|
||||
return;
|
||||
|
||||
// We can only evict if there are no references outstanding to this entry.
|
||||
if (e.getReferenceCount() != 0)
|
||||
return;
|
||||
|
||||
// Remove this entry.
|
||||
if (QpackContext.LOG.isDebugEnabled())
|
||||
QpackContext.LOG.debug(String.format("HdrTbl[%x] evict %s", hashCode(), entry));
|
||||
_dynamicTableSizeInBytes -= entry.getSize();
|
||||
entry.setIndex(-1);
|
||||
_fieldMap.remove(entry.getHttpField());
|
||||
String lc = entry.getHttpField().getLowerCaseName();
|
||||
if (entry == _nameMap.get(lc))
|
||||
_nameMap.remove(lc);
|
||||
}
|
||||
if (QpackContext.LOG.isDebugEnabled())
|
||||
QpackContext.LOG.debug(String.format("HdrTbl[%x] entries=%d, size=%d, max=%d", hashCode(), getNumEntries(), _dynamicTableSizeInBytes, _maxDynamicTableSizeInBytes));
|
||||
QpackContext.LOG.debug(String.format("HdrTbl[%x] evict %s", hashCode(), e));
|
||||
|
||||
Entry removedEntry = _entries.remove(0);
|
||||
if (removedEntry != e)
|
||||
throw new IllegalStateException("Corruption in DynamicTable");
|
||||
_fieldMap.remove(e.getHttpField());
|
||||
String name = e.getHttpField().getLowerCaseName();
|
||||
if (e == _nameMap.get(name))
|
||||
_nameMap.remove(name);
|
||||
|
||||
_capacity -= e.getSize();
|
||||
}
|
||||
|
||||
private void evictAll()
|
||||
{
|
||||
if (QpackContext.LOG.isDebugEnabled())
|
||||
QpackContext.LOG.debug(String.format("HdrTbl[%x] evictAll", hashCode()));
|
||||
if (getNumEntries() > 0)
|
||||
{
|
||||
_fieldMap.clear();
|
||||
_nameMap.clear();
|
||||
_offset = 0;
|
||||
_numEntries = 0;
|
||||
_dynamicTableSizeInBytes = 0;
|
||||
Arrays.fill(_entries, null);
|
||||
QpackContext.LOG.debug(String.format("HdrTbl[%x] entries=%d, size=%d, max=%d", hashCode(), getNumEntries(), _capacity, _maxCapacity));
|
||||
}
|
||||
|
||||
private int getEvictionThreshold()
|
||||
{
|
||||
return _maxCapacity * 3 / 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s@%x{entries=%d,size=%d,max=%d}", getClass().getSimpleName(), hashCode(), getNumEntries(), _dynamicTableSizeInBytes, _maxDynamicTableSizeInBytes);
|
||||
return String.format("%s@%x{entries=%d,size=%d,max=%d}", getClass().getSimpleName(), hashCode(), getNumEntries(), _capacity, _maxCapacity);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,12 +13,15 @@
|
|||
|
||||
package org.eclipse.jetty.http3.qpack.table;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
|
||||
public class Entry
|
||||
{
|
||||
final HttpField _field;
|
||||
private int _slot; // The index within it's array
|
||||
private final HttpField _field;
|
||||
private int _absoluteIndex;
|
||||
private AtomicInteger _referenceCount = new AtomicInteger(0);
|
||||
|
||||
public Entry()
|
||||
{
|
||||
|
@ -33,7 +36,7 @@ public class Entry
|
|||
public Entry(int index, HttpField field)
|
||||
{
|
||||
_field = field;
|
||||
_slot = index;
|
||||
_absoluteIndex = index;
|
||||
}
|
||||
|
||||
public int getSize()
|
||||
|
@ -44,12 +47,12 @@ public class Entry
|
|||
|
||||
public void setIndex(int index)
|
||||
{
|
||||
_slot = index;
|
||||
_absoluteIndex = index;
|
||||
}
|
||||
|
||||
public int getIndex()
|
||||
{
|
||||
return _slot;
|
||||
return _absoluteIndex;
|
||||
}
|
||||
|
||||
public HttpField getHttpField()
|
||||
|
@ -57,6 +60,11 @@ public class Entry
|
|||
return _field;
|
||||
}
|
||||
|
||||
public int getReferenceCount()
|
||||
{
|
||||
return _referenceCount.get();
|
||||
}
|
||||
|
||||
public boolean isStatic()
|
||||
{
|
||||
return false;
|
||||
|
@ -70,6 +78,6 @@ public class Entry
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("{%s,%d,%s,%x}", isStatic() ? "S" : "D", _slot, _field, hashCode());
|
||||
return String.format("{%s,%d,%s,%x}", isStatic() ? "S" : "D", _absoluteIndex, _field, hashCode());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,13 +150,13 @@ public class StaticTable
|
|||
|
||||
_staticTable[i] = entry;
|
||||
|
||||
if (entry._field.getValue() != null)
|
||||
_staticFieldMap.put(entry._field, entry);
|
||||
if (entry.getHttpField().getValue() != null)
|
||||
_staticFieldMap.put(entry.getHttpField(), entry);
|
||||
|
||||
if (!added.contains(entry._field.getName()))
|
||||
if (!added.contains(entry.getHttpField().getName()))
|
||||
{
|
||||
added.add(entry._field.getName());
|
||||
staticNameMapBuilder.with(entry._field.getName(), entry);
|
||||
added.add(entry.getHttpField().getName());
|
||||
staticNameMapBuilder.with(entry.getHttpField().getName(), entry);
|
||||
}
|
||||
}
|
||||
_staticNameMap = staticNameMapBuilder.build();
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.http3.qpack;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http3.qpack.generator.Instruction;
|
||||
|
||||
public class DecoderTestHandler implements QpackDecoder.Handler
|
||||
{
|
||||
private final Queue<MetaData> _metadataList = new LinkedList<>();
|
||||
private final Queue<Instruction> _instructionList = new LinkedList<>();
|
||||
|
||||
@Override
|
||||
public void onMetadata(MetaData metaData)
|
||||
{
|
||||
_metadataList.add(metaData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInstruction(Instruction instruction)
|
||||
{
|
||||
_instructionList.add(instruction);
|
||||
}
|
||||
|
||||
public MetaData getMetaData()
|
||||
{
|
||||
return _metadataList.poll();
|
||||
}
|
||||
|
||||
public Instruction getInstruction()
|
||||
{
|
||||
return _instructionList.poll();
|
||||
}
|
||||
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return _metadataList.isEmpty() && _instructionList.isEmpty();
|
||||
}
|
||||
}
|
|
@ -54,16 +54,19 @@ public class QpackDecoderTest
|
|||
+-------------------------------+
|
||||
*/
|
||||
|
||||
private final DecoderTestHandler handler = new DecoderTestHandler();
|
||||
|
||||
@Test
|
||||
public void testDecodeD3() throws Exception
|
||||
{
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
|
||||
// First request
|
||||
String encoded = "828684410f7777772e6578616d706c652e636f6d";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
|
||||
MetaData.Request request = (MetaData.Request)decoder.decode(buffer);
|
||||
decoder.decode(buffer);
|
||||
MetaData.Request request = (MetaData.Request)handler.getMetaData();
|
||||
|
||||
assertEquals("GET", request.getMethod());
|
||||
assertEquals(HttpScheme.HTTP.asString(), request.getURI().getScheme());
|
||||
|
@ -75,7 +78,8 @@ public class QpackDecoderTest
|
|||
encoded = "828684be58086e6f2d6361636865";
|
||||
buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
|
||||
request = (MetaData.Request)decoder.decode(buffer);
|
||||
decoder.decode(buffer);
|
||||
request = (MetaData.Request)handler.getMetaData();
|
||||
|
||||
assertEquals("GET", request.getMethod());
|
||||
assertEquals(HttpScheme.HTTP.asString(), request.getURI().getScheme());
|
||||
|
@ -90,7 +94,8 @@ public class QpackDecoderTest
|
|||
encoded = "828785bf400a637573746f6d2d6b65790c637573746f6d2d76616c7565";
|
||||
buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
|
||||
request = (MetaData.Request)decoder.decode(buffer);
|
||||
decoder.decode(buffer);
|
||||
request = (MetaData.Request)handler.getMetaData();
|
||||
|
||||
assertEquals("GET", request.getMethod());
|
||||
assertEquals(HttpScheme.HTTPS.asString(), request.getURI().getScheme());
|
||||
|
@ -105,13 +110,14 @@ public class QpackDecoderTest
|
|||
@Test
|
||||
public void testDecodeD4() throws Exception
|
||||
{
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
|
||||
// First request
|
||||
String encoded = "828684418cf1e3c2e5f23a6ba0ab90f4ff";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
|
||||
MetaData.Request request = (MetaData.Request)decoder.decode(buffer);
|
||||
decoder.decode(buffer);
|
||||
MetaData.Request request = (MetaData.Request)handler.getMetaData();
|
||||
|
||||
assertEquals("GET", request.getMethod());
|
||||
assertEquals(HttpScheme.HTTP.asString(), request.getURI().getScheme());
|
||||
|
@ -123,7 +129,8 @@ public class QpackDecoderTest
|
|||
encoded = "828684be5886a8eb10649cbf";
|
||||
buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
|
||||
request = (MetaData.Request)decoder.decode(buffer);
|
||||
decoder.decode(buffer);
|
||||
request = (MetaData.Request)handler.getMetaData();
|
||||
|
||||
assertEquals("GET", request.getMethod());
|
||||
assertEquals(HttpScheme.HTTP.asString(), request.getURI().getScheme());
|
||||
|
@ -140,14 +147,15 @@ public class QpackDecoderTest
|
|||
{
|
||||
String value = "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==";
|
||||
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
String encoded = "8682418cF1E3C2E5F23a6bA0Ab90F4Ff841f0822426173696320515778685a475270626a70766347567549484e6c633246745a513d3d";
|
||||
byte[] bytes = TypeUtil.fromHexString(encoded);
|
||||
byte[] array = new byte[bytes.length + 1];
|
||||
System.arraycopy(bytes, 0, array, 1, bytes.length);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(array, 1, bytes.length).slice();
|
||||
|
||||
MetaData.Request request = (MetaData.Request)decoder.decode(buffer);
|
||||
decoder.decode(buffer);
|
||||
MetaData.Request request = (MetaData.Request)handler.getMetaData();
|
||||
|
||||
assertEquals("GET", request.getMethod());
|
||||
assertEquals(HttpScheme.HTTP.asString(), request.getURI().getScheme());
|
||||
|
@ -162,7 +170,7 @@ public class QpackDecoderTest
|
|||
@Test
|
||||
public void testDecodeHuffmanWithArrayOffset() throws Exception
|
||||
{
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
|
||||
String encoded = "8286418cf1e3c2e5f23a6ba0ab90f4ff84";
|
||||
byte[] bytes = TypeUtil.fromHexString(encoded);
|
||||
|
@ -170,7 +178,8 @@ public class QpackDecoderTest
|
|||
System.arraycopy(bytes, 0, array, 1, bytes.length);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(array, 1, bytes.length).slice();
|
||||
|
||||
MetaData.Request request = (MetaData.Request)decoder.decode(buffer);
|
||||
decoder.decode(buffer);
|
||||
MetaData.Request request = (MetaData.Request)handler.getMetaData();
|
||||
|
||||
assertEquals("GET", request.getMethod());
|
||||
assertEquals(HttpScheme.HTTP.asString(), request.getURI().getScheme());
|
||||
|
@ -186,8 +195,9 @@ public class QpackDecoderTest
|
|||
String encoded = "886196C361Be940b6a65B6850400B8A00571972e080a62D1Bf5f87497cA589D34d1f9a0f0d0234327690Aa69D29aFcA954D3A5358980Ae112e0f7c880aE152A9A74a6bF3";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
MetaData.Response response = (MetaData.Response)decoder.decode(buffer);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
decoder.decode(buffer);
|
||||
MetaData.Response response = (MetaData.Response)handler.getMetaData();
|
||||
|
||||
assertThat(response.getStatus(), is(200));
|
||||
assertThat(response.getFields().size(), is(6));
|
||||
|
@ -204,8 +214,9 @@ public class QpackDecoderTest
|
|||
{
|
||||
String encoded = "203f136687A0E41d139d090760881c6490B2Cd39Ba7f";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
MetaData metaData = decoder.decode(buffer);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
decoder.decode(buffer);
|
||||
MetaData metaData = handler.getMetaData();
|
||||
assertThat(metaData.getFields().get(HttpHeader.HOST), is("localhost0"));
|
||||
assertThat(metaData.getFields().get(HttpHeader.COOKIE), is("abcdefghij"));
|
||||
assertThat(decoder.getQpackContext().getMaxDynamicTableSize(), is(50));
|
||||
|
@ -226,7 +237,7 @@ public class QpackDecoderTest
|
|||
|
||||
String encoded = "203f136687A0E41d139d090760881c6490B2Cd39Ba7f20";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
try
|
||||
{
|
||||
decoder.decode(buffer);
|
||||
|
@ -244,8 +255,9 @@ public class QpackDecoderTest
|
|||
String encoded = "3f610f17FfEc02Df3990A190A0D4Ee5b3d2940Ec98Aa4a62D127D29e273a0aA20dEcAa190a503b262d8a2671D4A2672a927aA874988a2471D05510750c951139EdA2452a3a548cAa1aA90bE4B228342864A9E0D450A5474a92992a1aA513395448E3A0Aa17B96cFe3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f14E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F353F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F54f";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
|
||||
QpackDecoder decoder = new QpackDecoder(128, 8192);
|
||||
MetaData metaData = decoder.decode(buffer);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 128, 8192);
|
||||
decoder.decode(buffer);
|
||||
MetaData metaData = handler.getMetaData();
|
||||
|
||||
assertThat(decoder.getQpackContext().getDynamicTableSize(), is(0));
|
||||
assertThat(metaData.getFields().get("host"), Matchers.startsWith("This is a very large field"));
|
||||
|
@ -257,7 +269,7 @@ public class QpackDecoderTest
|
|||
String encoded = "BE";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
|
||||
QpackDecoder decoder = new QpackDecoder(128, 8192);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 128, 8192);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -442,12 +454,13 @@ public class QpackDecoderTest
|
|||
@Test
|
||||
public void testHuffmanEncodedStandard() throws Exception
|
||||
{
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
|
||||
String encoded = "82868441" + "83" + "49509F";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
|
||||
MetaData.Request request = (MetaData.Request)decoder.decode(buffer);
|
||||
decoder.decode(buffer);
|
||||
MetaData.Request request = (MetaData.Request)handler.getMetaData();
|
||||
|
||||
assertEquals("GET", request.getMethod());
|
||||
assertEquals(HttpScheme.HTTP.asString(), request.getURI().getScheme());
|
||||
|
@ -460,7 +473,7 @@ public class QpackDecoderTest
|
|||
@Test
|
||||
public void testHuffmanEncodedExtraPadding()
|
||||
{
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
|
||||
String encoded = "82868441" + "84" + "49509FFF";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
|
@ -472,7 +485,7 @@ public class QpackDecoderTest
|
|||
@Test
|
||||
public void testHuffmanEncodedZeroPadding()
|
||||
{
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
|
||||
String encoded = "82868441" + "83" + "495090";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
|
@ -485,7 +498,7 @@ public class QpackDecoderTest
|
|||
@Test
|
||||
public void testHuffmanEncodedWithEOS()
|
||||
{
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
|
||||
String encoded = "82868441" + "87" + "497FFFFFFF427F";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
|
@ -497,7 +510,7 @@ public class QpackDecoderTest
|
|||
@Test
|
||||
public void testHuffmanEncodedOneIncompleteOctet()
|
||||
{
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
|
||||
String encoded = "82868441" + "81" + "FE";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
|
@ -509,7 +522,7 @@ public class QpackDecoderTest
|
|||
@Test
|
||||
public void testHuffmanEncodedTwoIncompleteOctet()
|
||||
{
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
|
||||
String encoded = "82868441" + "82" + "FFFE";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
|
@ -521,7 +534,7 @@ public class QpackDecoderTest
|
|||
@Test
|
||||
public void testZeroLengthName()
|
||||
{
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
|
||||
String encoded = "00000130";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
|
@ -532,11 +545,12 @@ public class QpackDecoderTest
|
|||
@Test
|
||||
public void testZeroLengthValue() throws Exception
|
||||
{
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
|
||||
String encoded = "00016800";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
MetaData metaData = decoder.decode(buffer);
|
||||
decoder.decode(buffer);
|
||||
MetaData metaData = handler.getMetaData();
|
||||
assertThat(metaData.getFields().size(), is(1));
|
||||
assertThat(metaData.getFields().get("h"), is(""));
|
||||
}
|
||||
|
@ -544,7 +558,7 @@ public class QpackDecoderTest
|
|||
@Test
|
||||
public void testUpperCaseName()
|
||||
{
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
|
||||
String encoded = "0001480130";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
|
@ -555,7 +569,7 @@ public class QpackDecoderTest
|
|||
@Test
|
||||
public void testWhiteSpaceName()
|
||||
{
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
|
||||
String encoded = "0001200130";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
|
|
|
@ -41,11 +41,13 @@ public class QpackTest
|
|||
static final HttpField XPowerJetty = new PreEncodedHttpField(HttpHeader.X_POWERED_BY, "jetty");
|
||||
static final HttpField Date = new PreEncodedHttpField(HttpHeader.DATE, DateGenerator.formatDate(TimeUnit.NANOSECONDS.toMillis(System.nanoTime())));
|
||||
|
||||
private final DecoderTestHandler handler = new DecoderTestHandler();
|
||||
|
||||
@Test
|
||||
public void encodeDecodeResponseTest() throws Exception
|
||||
{
|
||||
QpackEncoder encoder = new QpackEncoder();
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
ByteBuffer buffer = BufferUtil.allocateDirect(16 * 1024);
|
||||
|
||||
HttpFields.Mutable fields0 = HttpFields.build()
|
||||
|
@ -62,7 +64,8 @@ public class QpackTest
|
|||
BufferUtil.clearToFill(buffer);
|
||||
encoder.encode(buffer, original0);
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
Response decoded0 = (Response)decoder.decode(buffer);
|
||||
decoder.decode(buffer);
|
||||
Response decoded0 = (Response)handler.getMetaData();
|
||||
|
||||
Response nullToEmpty = new MetaData.Response(HttpVersion.HTTP_2, 200,
|
||||
fields0.put(new HttpField(HttpHeader.CONTENT_ENCODING, "")));
|
||||
|
@ -72,7 +75,8 @@ public class QpackTest
|
|||
BufferUtil.clearToFill(buffer);
|
||||
encoder.encode(buffer, original0);
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
Response decoded0b = (Response)decoder.decode(buffer);
|
||||
decoder.decode(buffer);
|
||||
Response decoded0b = (Response)handler.getMetaData();
|
||||
|
||||
assertMetaDataResponseSame(nullToEmpty, decoded0b);
|
||||
|
||||
|
@ -90,7 +94,8 @@ public class QpackTest
|
|||
BufferUtil.clearToFill(buffer);
|
||||
encoder.encode(buffer, original1);
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
Response decoded1 = (Response)decoder.decode(buffer);
|
||||
decoder.decode(buffer);
|
||||
Response decoded1 = (Response)handler.getMetaData();
|
||||
|
||||
assertMetaDataResponseSame(original1, decoded1);
|
||||
assertEquals("custom-key", decoded1.getFields().getField("Custom-Key").getName());
|
||||
|
@ -100,7 +105,7 @@ public class QpackTest
|
|||
public void encodeDecodeTooLargeTest() throws Exception
|
||||
{
|
||||
QpackEncoder encoder = new QpackEncoder();
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 164);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 164);
|
||||
ByteBuffer buffer = BufferUtil.allocateDirect(16 * 1024);
|
||||
|
||||
HttpFields fields0 = HttpFields.build()
|
||||
|
@ -111,7 +116,8 @@ public class QpackTest
|
|||
BufferUtil.clearToFill(buffer);
|
||||
encoder.encode(buffer, original0);
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
MetaData decoded0 = decoder.decode(buffer);
|
||||
decoder.decode(buffer);
|
||||
MetaData decoded0 = handler.getMetaData();
|
||||
|
||||
assertMetaDataSame(original0, decoded0);
|
||||
|
||||
|
@ -139,7 +145,7 @@ public class QpackTest
|
|||
public void encodeDecodeNonAscii() throws Exception
|
||||
{
|
||||
QpackEncoder encoder = new QpackEncoder();
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 8192);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 8192);
|
||||
ByteBuffer buffer = BufferUtil.allocate(16 * 1024);
|
||||
|
||||
HttpFields fields0 = HttpFields.build()
|
||||
|
@ -151,7 +157,8 @@ public class QpackTest
|
|||
BufferUtil.clearToFill(buffer);
|
||||
encoder.encode(buffer, original0);
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
Response decoded0 = (Response)decoder.decode(buffer);
|
||||
decoder.decode(buffer);
|
||||
Response decoded0 = (Response)handler.getMetaData();
|
||||
|
||||
assertMetaDataSame(original0, decoded0);
|
||||
}
|
||||
|
@ -160,7 +167,7 @@ public class QpackTest
|
|||
public void evictReferencedFieldTest() throws Exception
|
||||
{
|
||||
QpackEncoder encoder = new QpackEncoder(200, 200);
|
||||
QpackDecoder decoder = new QpackDecoder(200, 1024);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 200, 1024);
|
||||
ByteBuffer buffer = BufferUtil.allocateDirect(16 * 1024);
|
||||
|
||||
String longEnoughToBeEvicted = "012345678901234567890123456789012345678901234567890";
|
||||
|
@ -173,7 +180,8 @@ public class QpackTest
|
|||
BufferUtil.clearToFill(buffer);
|
||||
encoder.encode(buffer, original0);
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
MetaData decoded0 = decoder.decode(buffer);
|
||||
decoder.decode(buffer);
|
||||
MetaData decoded0 = handler.getMetaData();
|
||||
|
||||
assertEquals(2, encoder.getQpackContext().getNumEntries());
|
||||
assertEquals(2, decoder.getQpackContext().getNumEntries());
|
||||
|
@ -190,7 +198,8 @@ public class QpackTest
|
|||
BufferUtil.clearToFill(buffer);
|
||||
encoder.encode(buffer, original1);
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
MetaData decoded1 = decoder.decode(buffer);
|
||||
decoder.decode(buffer);
|
||||
MetaData decoded1 = handler.getMetaData();
|
||||
assertMetaDataSame(original1, decoded1);
|
||||
|
||||
assertEquals(2, encoder.getQpackContext().getNumEntries());
|
||||
|
@ -203,7 +212,7 @@ public class QpackTest
|
|||
public void testHopHeadersAreRemoved() throws Exception
|
||||
{
|
||||
QpackEncoder encoder = new QpackEncoder();
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 16384);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 16384);
|
||||
|
||||
HttpFields input = HttpFields.build()
|
||||
.add(HttpHeader.ACCEPT, "*")
|
||||
|
@ -219,7 +228,8 @@ public class QpackTest
|
|||
BufferUtil.clearToFill(buffer);
|
||||
encoder.encode(buffer, new MetaData(HttpVersion.HTTP_2, input));
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
MetaData metaData = decoder.decode(buffer);
|
||||
decoder.decode(buffer);
|
||||
MetaData metaData = handler.getMetaData();
|
||||
HttpFields output = metaData.getFields();
|
||||
|
||||
assertEquals(1, output.size());
|
||||
|
@ -230,7 +240,7 @@ public class QpackTest
|
|||
public void testTETrailers() throws Exception
|
||||
{
|
||||
QpackEncoder encoder = new QpackEncoder();
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 16384);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 16384);
|
||||
|
||||
String teValue = "trailers";
|
||||
String trailerValue = "Custom";
|
||||
|
@ -243,7 +253,7 @@ public class QpackTest
|
|||
BufferUtil.clearToFill(buffer);
|
||||
encoder.encode(buffer, new MetaData(HttpVersion.HTTP_2, input));
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
MetaData metaData = decoder.decode(buffer);
|
||||
MetaData metaData = handler.getMetaData();
|
||||
HttpFields output = metaData.getFields();
|
||||
|
||||
assertEquals(2, output.size());
|
||||
|
@ -255,7 +265,7 @@ public class QpackTest
|
|||
public void testColonHeaders() throws Exception
|
||||
{
|
||||
QpackEncoder encoder = new QpackEncoder();
|
||||
QpackDecoder decoder = new QpackDecoder(4096, 16384);
|
||||
QpackDecoder decoder = new QpackDecoder(handler, 4096, 16384);
|
||||
|
||||
HttpFields input = HttpFields.build()
|
||||
.add(":status", "200")
|
||||
|
|
Loading…
Reference in New Issue