Issue #11420 - fix dynamic table referencing in QpackDecoder
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
parent
83526f77d3
commit
600dbfee25
|
@ -379,7 +379,7 @@ public class QpackDecoder implements Dumpable
|
|||
LOG.debug("Duplicate: index={}", index);
|
||||
|
||||
DynamicTable dynamicTable = _context.getDynamicTable();
|
||||
Entry referencedEntry = dynamicTable.get(index);
|
||||
Entry referencedEntry = dynamicTable.getRelative(index);
|
||||
|
||||
// Add the new Entry to the DynamicTable.
|
||||
Entry entry = new Entry(referencedEntry.getHttpField());
|
||||
|
@ -396,7 +396,7 @@ public class QpackDecoder implements Dumpable
|
|||
|
||||
StaticTable staticTable = QpackContext.getStaticTable();
|
||||
DynamicTable dynamicTable = _context.getDynamicTable();
|
||||
Entry referencedEntry = isDynamicTableIndex ? dynamicTable.get(nameIndex) : staticTable.get(nameIndex);
|
||||
Entry referencedEntry = isDynamicTableIndex ? dynamicTable.getRelative(nameIndex) : staticTable.get(nameIndex);
|
||||
|
||||
// Add the new Entry to the DynamicTable.
|
||||
Entry entry = new Entry(new HttpField(referencedEntry.getHttpField().getHeader(), referencedEntry.getHttpField().getName(), value));
|
||||
|
|
|
@ -68,14 +68,14 @@ public abstract class EncodableEntry
|
|||
{
|
||||
// Indexed Field Line with Static Reference.
|
||||
buffer.put((byte)(0x80 | 0x40));
|
||||
int relativeIndex = _entry.getIndex();
|
||||
NBitIntegerEncoder.encode(buffer, 6, relativeIndex);
|
||||
int index = _entry.getIndex();
|
||||
NBitIntegerEncoder.encode(buffer, 6, index);
|
||||
}
|
||||
else if (_entry.getIndex() < base)
|
||||
{
|
||||
// Indexed Field Line with Dynamic Reference.
|
||||
buffer.put((byte)0x80);
|
||||
int relativeIndex = base - (_entry.getIndex() + 1);
|
||||
int relativeIndex = (base - 1) - _entry.getIndex();
|
||||
NBitIntegerEncoder.encode(buffer, 6, relativeIndex);
|
||||
}
|
||||
else
|
||||
|
@ -100,7 +100,7 @@ public abstract class EncodableEntry
|
|||
else if (_entry.getIndex() < base)
|
||||
{
|
||||
// Indexed Field Line with Dynamic Reference.
|
||||
int relativeIndex = base - (_entry.getIndex() + 1);
|
||||
int relativeIndex = (base - 1) - _entry.getIndex();
|
||||
return NBitIntegerEncoder.octetsNeeded(6, relativeIndex);
|
||||
}
|
||||
else
|
||||
|
@ -150,7 +150,7 @@ public abstract class EncodableEntry
|
|||
{
|
||||
// Literal Field Line with Dynamic Name Reference.
|
||||
buffer.put((byte)(0x40 | (allowIntermediary ? 0x20 : 0x00)));
|
||||
int relativeIndex = base - (_nameEntry.getIndex() + 1);
|
||||
int relativeIndex = (base - 1) - _nameEntry.getIndex();
|
||||
NBitIntegerEncoder.encode(buffer, 4, relativeIndex);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -66,6 +66,7 @@ public class QpackContext
|
|||
return _dynamicTable.get(StringUtil.asciiToLowerCase(name));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Entry get(int index)
|
||||
{
|
||||
if (index <= StaticTable.STATIC_SIZE)
|
||||
|
|
|
@ -229,7 +229,7 @@ public class EncodedFieldSection
|
|||
public HttpField decode(QpackContext context)
|
||||
{
|
||||
if (_dynamicTable)
|
||||
return context.getDynamicTable().getAbsolute(_base - (_index + 1)).getHttpField();
|
||||
return context.getDynamicTable().getRelative(_index, _base).getHttpField();
|
||||
else
|
||||
return QpackContext.getStaticTable().get(_index).getHttpField();
|
||||
}
|
||||
|
@ -247,7 +247,7 @@ public class EncodedFieldSection
|
|||
@Override
|
||||
public HttpField decode(QpackContext context)
|
||||
{
|
||||
return context.getDynamicTable().getAbsolute(_base + _index).getHttpField();
|
||||
return context.getDynamicTable().getPostBase(_index, _base).getHttpField();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,7 +272,7 @@ public class EncodedFieldSection
|
|||
{
|
||||
HttpField field;
|
||||
if (_dynamicTable)
|
||||
field = context.getDynamicTable().getAbsolute(_base - (_nameIndex + 1)).getHttpField();
|
||||
field = context.getDynamicTable().getRelative(_nameIndex, _base).getHttpField();
|
||||
else
|
||||
field = QpackContext.getStaticTable().get(_nameIndex).getHttpField();
|
||||
|
||||
|
@ -296,7 +296,7 @@ public class EncodedFieldSection
|
|||
@Override
|
||||
public HttpField decode(QpackContext context)
|
||||
{
|
||||
HttpField field = context.getDynamicTable().getAbsolute(_base + _nameIndex).getHttpField();
|
||||
HttpField field = context.getDynamicTable().getPostBase(_nameIndex, _base).getHttpField();
|
||||
return new HttpField(field.getHeader(), field.getName(), _value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ public class DynamicTable implements Iterable<Entry>, Dumpable
|
|||
if (index < 0 || index >= _entries.size())
|
||||
throw new IllegalArgumentException("Invalid Index " + index);
|
||||
|
||||
return _entries.get(index);
|
||||
return get(index);
|
||||
}
|
||||
|
||||
public Entry get(int index)
|
||||
|
@ -164,9 +164,24 @@ public class DynamicTable implements Iterable<Entry>, Dumpable
|
|||
return _fieldMap.get(field);
|
||||
}
|
||||
|
||||
public Entry getRelative(int index)
|
||||
{
|
||||
return getRelative(index, getInsertCount());
|
||||
}
|
||||
|
||||
public Entry getRelative(int index, int base)
|
||||
{
|
||||
return getAbsolute(base - 1 - index);
|
||||
}
|
||||
|
||||
public Entry getPostBase(int index, int base)
|
||||
{
|
||||
return getAbsolute(base + index);
|
||||
}
|
||||
|
||||
public int getBase()
|
||||
{
|
||||
if (_entries.size() == 0)
|
||||
if (_entries.isEmpty())
|
||||
return _absoluteIndex;
|
||||
return _entries.get(0).getIndex();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995 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.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http3.qpack.internal.table.Entry;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.NanoTime;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class QpackDecoderTest
|
||||
{
|
||||
private QpackDecoder _decoder;
|
||||
private TestDecoderHandler _decoderHandler;
|
||||
|
||||
@BeforeEach
|
||||
public void before()
|
||||
{
|
||||
_decoderHandler = new TestDecoderHandler();
|
||||
_decoder = new QpackDecoder(_decoderHandler);
|
||||
_decoder.setBeginNanoTimeSupplier(NanoTime::now);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDynamicNameReference() throws Exception
|
||||
{
|
||||
_decoder.setMaxTableCapacity(2048);
|
||||
QpackDecoder.InstructionHandler instructionHandler = _decoder.getInstructionHandler();
|
||||
instructionHandler.onSetDynamicTableCapacity(2048);
|
||||
|
||||
instructionHandler.onInsertWithLiteralName("name0", "value0");
|
||||
instructionHandler.onInsertWithLiteralName("name1", "value1");
|
||||
instructionHandler.onInsertWithLiteralName("name2", "value2");
|
||||
instructionHandler.onInsertWithLiteralName("name3", "value3");
|
||||
instructionHandler.onInsertNameWithReference(5, false, "static0");
|
||||
instructionHandler.onInsertWithLiteralName("name4", "value4");
|
||||
instructionHandler.onInsertNameWithReference(2, true, "dynamic0");
|
||||
instructionHandler.onDuplicate(6);
|
||||
|
||||
// Indexes into the static table are absolute.
|
||||
Entry entry = _decoder.getQpackContext().getDynamicTable().get(4);
|
||||
assertThat(entry.getHttpField().getName(), equalTo("cookie"));
|
||||
assertThat(entry.getHttpField().getValue(), equalTo("static0"));
|
||||
|
||||
// Named reference is relative to the most recently inserted entry.
|
||||
entry = _decoder.getQpackContext().getDynamicTable().get(6);
|
||||
assertThat(entry.getHttpField().getName(), equalTo("name3"));
|
||||
assertThat(entry.getHttpField().getValue(), equalTo("dynamic0"));
|
||||
|
||||
// Duplicate reference is relative to the most recently inserted entry.
|
||||
entry = _decoder.getQpackContext().getDynamicTable().get(7);
|
||||
assertThat(entry.getHttpField().getName(), equalTo("name0"));
|
||||
assertThat(entry.getHttpField().getValue(), equalTo("value0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodeRequest() throws Exception
|
||||
{
|
||||
_decoder.setMaxTableCapacity(2048);
|
||||
QpackDecoder.InstructionHandler instructionHandler = _decoder.getInstructionHandler();
|
||||
instructionHandler.onSetDynamicTableCapacity(2048);
|
||||
|
||||
instructionHandler.onInsertNameWithReference(0, false, "licensed.app");
|
||||
instructionHandler.onInsertWithLiteralName("sec-ch-ua", "\"Not A(Brand\";v=\"99\", \"Brave\";v=\"121\", \"Chromium\";v=\"121\"");
|
||||
instructionHandler.onInsertWithLiteralName("sec-ch-ua-mobile", "?0");
|
||||
instructionHandler.onInsertWithLiteralName("sec-ch-ua-platform", "Windows");
|
||||
instructionHandler.onInsertWithLiteralName("dnt", "1");
|
||||
instructionHandler.onInsertNameWithReference(95, false, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36");
|
||||
instructionHandler.onInsertNameWithReference(29, false, "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8");
|
||||
instructionHandler.onInsertWithLiteralName("sec-gpc", "1");
|
||||
instructionHandler.onInsertWithLiteralName("sec-fetch-site", "none");
|
||||
instructionHandler.onInsertWithLiteralName("sec-fetch-mode", "navigate");
|
||||
instructionHandler.onInsertWithLiteralName("sec-fetch-user", "?1");
|
||||
instructionHandler.onInsertWithLiteralName("sec-fetch-dest", "value=document");
|
||||
instructionHandler.onInsertNameWithReference(72, false, "en-US,en;q=0.9");
|
||||
instructionHandler.onInsertNameWithReference(8, false, "2024-02-17T02:19:47.4433882Z");
|
||||
instructionHandler.onInsertNameWithReference(1, false, "/login/GoogleLogin.js");
|
||||
instructionHandler.onInsertNameWithReference(90, false, "https://licensed.app");
|
||||
instructionHandler.onInsertNameWithReference(7, true, "same-origin");
|
||||
instructionHandler.onInsertNameWithReference(7, true, "cors");
|
||||
instructionHandler.onInsertNameWithReference(6, true, "script");
|
||||
instructionHandler.onInsertNameWithReference(13, false, "https://licensed.app/");
|
||||
|
||||
assertTrue(_decoder.decode(0, fromHex("1500D193D78592848f918e90Dd8c83828180Df87"), _decoderHandler));
|
||||
MetaData metaData = _decoderHandler.getMetaData();
|
||||
|
||||
// Check headers were correctly referenced from dynamic table.
|
||||
assertThat(metaData.getHttpFields().get("sec-fetch-site"), equalTo("same-origin"));
|
||||
assertThat(metaData.getHttpFields().get("sec-fetch-mode"), equalTo("cors"));
|
||||
assertThat(metaData.getHttpFields().get("sec-fetch-dest"), equalTo("script"));
|
||||
}
|
||||
|
||||
private ByteBuffer fromHex(String hex)
|
||||
{
|
||||
return BufferUtil.toBuffer(StringUtil.fromHexString(hex));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue