Merged branch 'jetty-10.0.x' into 'jetty-11.0.x'.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2023-11-27 15:34:24 +01:00
commit 6ea2f68da0
No known key found for this signature in database
GPG Key ID: 1677D141BCF3584D
17 changed files with 248 additions and 34 deletions

View File

@ -151,6 +151,13 @@ public class MetaData implements Iterable<HttpField>
version, fields, contentLength);
}
public Request(long beginNanoTime, String method, String scheme, HostPortHttpField authority, String uri, HttpVersion version, HttpFields fields, long contentLength)
{
this(beginNanoTime, method,
HttpURI.build().scheme(scheme).host(authority == null ? null : authority.getHost()).port(authority == null ? -1 : authority.getPort()).pathQuery(uri),
version, fields, contentLength);
}
public Request(String method, HttpURI uri, HttpVersion version, HttpFields fields, long contentLength, Supplier<HttpFields> trailers)
{
this(NanoTime.now(), method, uri, version, fields, contentLength, trailers);
@ -222,9 +229,19 @@ public class MetaData implements Iterable<HttpField>
this(scheme == null ? null : scheme.asString(), authority, path, fields, protocol);
}
public ConnectRequest(long beginNanoTime, HttpScheme scheme, HostPortHttpField authority, String path, HttpFields fields, String protocol)
{
this(beginNanoTime, scheme == null ? null : scheme.asString(), authority, path, fields, protocol);
}
public ConnectRequest(String scheme, HostPortHttpField authority, String path, HttpFields fields, String protocol)
{
super(HttpMethod.CONNECT.asString(),
this(NanoTime.now(), scheme, authority, path, fields, protocol);
}
public ConnectRequest(long beginNanoTime, String scheme, HostPortHttpField authority, String path, HttpFields fields, String protocol)
{
super(beginNanoTime, HttpMethod.CONNECT.asString(),
HttpURI.build().scheme(scheme).host(authority == null ? null : authority.getHost()).port(authority == null ? -1 : authority.getPort()).pathQuery(path),
HttpVersion.HTTP_2, fields, Long.MIN_VALUE, null);
_protocol = protocol;

View File

@ -30,6 +30,7 @@ import org.eclipse.jetty.http2.frames.SettingsFrame;
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
import org.eclipse.jetty.http2.hpack.HpackDecoder;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.util.NanoTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -52,6 +53,8 @@ public class Parser
private int maxSettingsKeys = SettingsFrame.DEFAULT_MAX_KEYS;
private boolean continuation;
private State state = State.HEADER;
private long beginNanoTime;
private boolean nanoTimeStored;
@Deprecated
public Parser(ByteBufferPool byteBufferPool, int maxTableCapacity, int maxHeaderSize)
@ -74,7 +77,7 @@ public class Parser
{
this.byteBufferPool = byteBufferPool;
this.headerParser = new HeaderParser(rateControl == null ? RateControl.NO_RATE_CONTROL : rateControl);
this.hpackDecoder = new HpackDecoder(maxHeaderSize);
this.hpackDecoder = new HpackDecoder(maxHeaderSize, this::getBeginNanoTime);
this.bodyParsers = new BodyParser[FrameType.values().length];
}
@ -114,6 +117,25 @@ public class Parser
state = State.HEADER;
}
public long getBeginNanoTime()
{
return beginNanoTime;
}
private void clearBeginNanoTime()
{
nanoTimeStored = false;
}
private void storeBeginNanoTime()
{
if (!nanoTimeStored)
{
beginNanoTime = NanoTime.now();
nanoTimeStored = true;
}
}
/**
* <p>Parses the given {@code buffer} bytes and emit events to a {@link Listener}.</p>
* <p>When this method returns, the buffer may not be fully consumed, so invocations
@ -135,6 +157,7 @@ public class Parser
{
case HEADER:
{
storeBeginNanoTime();
if (!parseHeader(buffer))
return;
break;
@ -143,6 +166,8 @@ public class Parser
{
if (!parseBody(buffer))
return;
if (!continuation)
clearBeginNanoTime();
break;
}
default:

View File

@ -158,6 +158,102 @@ public class ContinuationParseTest
}
}
@Test
public void testBeginNanoTime() throws Exception
{
ByteBufferPool bufferPool = new MappedByteBufferPool(128);
HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(), new HpackEncoder());
final List<HeadersFrame> frames = new ArrayList<>();
Parser parser = new Parser(bufferPool, 8192);
parser.init(new Parser.Listener.Adapter()
{
@Override
public void onHeaders(HeadersFrame frame)
{
frames.add(frame);
}
@Override
public void onConnectionFailure(int error, String reason)
{
frames.add(new HeadersFrame(null, null, false));
}
});
int streamId = 13;
HttpFields fields = HttpFields.build()
.put("Accept", "text/html")
.put("User-Agent", "Jetty");
MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP.asString(), new HostPortHttpField("localhost:8080"), "/path", HttpVersion.HTTP_2, fields, -1);
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(bufferPool);
generator.generateHeaders(lease, streamId, metaData, null, true);
List<ByteBuffer> byteBuffers = lease.getByteBuffers();
assertEquals(2, byteBuffers.size());
ByteBuffer headersBody = byteBuffers.remove(1);
int start = headersBody.position();
int length = headersBody.remaining();
int firstHalf = length / 2;
int lastHalf = length - firstHalf;
// Adjust the length of the HEADERS frame.
ByteBuffer headersHeader = byteBuffers.get(0);
headersHeader.put(0, (byte)((firstHalf >>> 16) & 0xFF));
headersHeader.put(1, (byte)((firstHalf >>> 8) & 0xFF));
headersHeader.put(2, (byte)(firstHalf & 0xFF));
// Remove the END_HEADERS flag from the HEADERS header.
headersHeader.put(4, (byte)(headersHeader.get(4) & ~Flags.END_HEADERS));
// New HEADERS body.
headersBody.position(start);
headersBody.limit(start + firstHalf);
byteBuffers.add(headersBody.slice());
// Split the rest of the HEADERS body into a CONTINUATION frame.
byte[] continuationHeader = new byte[9];
continuationHeader[0] = (byte)((lastHalf >>> 16) & 0xFF);
continuationHeader[1] = (byte)((lastHalf >>> 8) & 0xFF);
continuationHeader[2] = (byte)(lastHalf & 0xFF);
continuationHeader[3] = (byte)FrameType.CONTINUATION.getType();
continuationHeader[4] = Flags.END_HEADERS;
continuationHeader[5] = 0x00;
continuationHeader[6] = 0x00;
continuationHeader[7] = 0x00;
continuationHeader[8] = (byte)streamId;
byteBuffers.add(ByteBuffer.wrap(continuationHeader));
// CONTINUATION body.
headersBody.position(start + firstHalf);
headersBody.limit(start + length);
byteBuffers.add(headersBody.slice());
assertEquals(4, byteBuffers.size());
parser.parse(byteBuffers.get(0));
long beginNanoTime = parser.getBeginNanoTime();
parser.parse(byteBuffers.get(1));
parser.parse(byteBuffers.get(2));
parser.parse(byteBuffers.get(3));
assertEquals(1, frames.size());
HeadersFrame frame = frames.get(0);
assertEquals(streamId, frame.getStreamId());
assertTrue(frame.isEndStream());
MetaData.Request request = (MetaData.Request)frame.getMetaData();
assertEquals(metaData.getMethod(), request.getMethod());
assertEquals(metaData.getURIString(), request.getURIString());
for (int i = 0; i < fields.size(); ++i)
{
HttpField field = fields.getField(i);
assertTrue(request.getFields().contains(field));
}
PriorityFrame priority = frame.getPriority();
assertNull(priority);
assertEquals(beginNanoTime, request.getBeginNanoTime());
}
@Test
public void testLargeHeadersBlock() throws Exception
{

View File

@ -14,6 +14,7 @@
package org.eclipse.jetty.http2.hpack;
import java.nio.ByteBuffer;
import java.util.function.LongSupplier;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
@ -42,14 +43,18 @@ public class HpackDecoder
private final MetaDataBuilder _builder;
private final HuffmanDecoder _huffmanDecoder;
private final NBitIntegerDecoder _integerDecoder;
private final LongSupplier _beginNanoTimeSupplier;
private int _maxTableCapacity;
/**
* @param maxHeaderSize The maximum allowed size of a decoded headers block,
* expressed as total of all name and value bytes, plus 32 bytes per field
* @param beginNanoTimeSupplier The supplier of a nano timestamp taken at
* the time the first byte was read
*/
public HpackDecoder(int maxHeaderSize)
public HpackDecoder(int maxHeaderSize, LongSupplier beginNanoTimeSupplier)
{
_beginNanoTimeSupplier = beginNanoTimeSupplier;
_context = new HpackContext(HpackContext.DEFAULT_MAX_TABLE_CAPACITY);
_builder = new MetaDataBuilder(maxHeaderSize);
_huffmanDecoder = new HuffmanDecoder();
@ -305,6 +310,7 @@ public class HpackDecoder
}
}
_builder.setBeginNanoTime(_beginNanoTimeSupplier.getAsLong());
return _builder.build();
}

View File

@ -22,6 +22,7 @@ import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.hpack.HpackException.SessionException;
import org.eclipse.jetty.util.NanoTime;
public class MetaDataBuilder
{
@ -38,6 +39,7 @@ public class MetaDataBuilder
private HpackException.StreamException _streamException;
private boolean _request;
private boolean _response;
private long _beginNanoTime = Long.MIN_VALUE;
/**
* @param maxHeadersSize The maximum size of the headers, expressed as total name and value characters.
@ -60,6 +62,13 @@ public class MetaDataBuilder
_maxSize = maxSize;
}
public void setBeginNanoTime(long beginNanoTime)
{
if (beginNanoTime == Long.MIN_VALUE)
beginNanoTime++;
_beginNanoTime = beginNanoTime;
}
/**
* Get the size.
*
@ -248,10 +257,13 @@ public class MetaDataBuilder
if (_path == null)
throw new HpackException.StreamException("No Path");
}
long nanoTime = _beginNanoTime == Long.MIN_VALUE ? NanoTime.now() : _beginNanoTime;
_beginNanoTime = Long.MIN_VALUE;
if (isConnect)
return new MetaData.ConnectRequest(_scheme, _authority, _path, fields, _protocol);
return new MetaData.ConnectRequest(nanoTime, _scheme, _authority, _path, fields, _protocol);
else
return new MetaData.Request(
nanoTime,
_method,
_scheme.asString(),
_authority,

View File

@ -23,6 +23,7 @@ import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.hpack.HpackException.CompressionException;
import org.eclipse.jetty.http2.hpack.HpackException.SessionException;
import org.eclipse.jetty.http2.hpack.HpackException.StreamException;
import org.eclipse.jetty.util.NanoTime;
import org.eclipse.jetty.util.StringUtil;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
@ -57,7 +58,7 @@ public class HpackDecoderTest
@Test
public void testDecodeD3() throws Exception
{
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
// First request
String encoded = "828684410f7777772e6578616d706c652e636f6d";
@ -105,7 +106,7 @@ public class HpackDecoderTest
@Test
public void testDecodeD4() throws Exception
{
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
// First request
String encoded = "828684418cf1e3c2e5f23a6ba0ab90f4ff";
@ -140,7 +141,7 @@ public class HpackDecoderTest
{
String value = "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==";
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
String encoded = "8682418cF1E3C2E5F23a6bA0Ab90F4Ff841f0822426173696320515778685a475270626a70766347567549484e6c633246745a513d3d";
byte[] bytes = StringUtil.fromHexString(encoded);
byte[] array = new byte[bytes.length + 1];
@ -162,7 +163,7 @@ public class HpackDecoderTest
@Test
public void testDecodeHuffmanWithArrayOffset() throws Exception
{
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
String encoded = "8286418cf1e3c2e5f23a6ba0ab90f4ff84";
byte[] bytes = StringUtil.fromHexString(encoded);
@ -186,7 +187,7 @@ public class HpackDecoderTest
String encoded = "886196C361Be940b6a65B6850400B8A00571972e080a62D1Bf5f87497cA589D34d1f9a0f0d0234327690Aa69D29aFcA954D3A5358980Ae112e0f7c880aE152A9A74a6bF3";
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
MetaData.Response response = (MetaData.Response)decoder.decode(buffer);
assertThat(response.getStatus(), is(200));
@ -204,7 +205,7 @@ public class HpackDecoderTest
{
String encoded = "203f136687A0E41d139d090760881c6490B2Cd39Ba7f";
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
MetaData metaData = decoder.decode(buffer);
assertThat(metaData.getFields().get(HttpHeader.HOST), is("localhost0"));
assertThat(metaData.getFields().get(HttpHeader.COOKIE), is("abcdefghij"));
@ -226,7 +227,7 @@ public class HpackDecoderTest
String encoded = "203f136687A0E41d139d090760881c6490B2Cd39Ba7f20";
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
try
{
decoder.decode(buffer);
@ -244,7 +245,7 @@ public class HpackDecoderTest
String encoded = "3f610f17FfEc02Df3990A190A0D4Ee5b3d2940Ec98Aa4a62D127D29e273a0aA20dEcAa190a503b262d8a2671D4A2672a927aA874988a2471D05510750c951139EdA2452a3a548cAa1aA90bE4B228342864A9E0D450A5474a92992a1aA513395448E3A0Aa17B96cFe3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f14E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F353F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F54f";
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
decoder.setMaxTableCapacity(128);
MetaData metaData = decoder.decode(buffer);
@ -258,7 +259,7 @@ public class HpackDecoderTest
String encoded = "BE";
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
decoder.setMaxTableCapacity(128);
try
@ -444,7 +445,7 @@ public class HpackDecoderTest
@Test
public void testHuffmanEncodedStandard() throws Exception
{
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
String encoded = "82868441" + "83" + "49509F";
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
@ -462,7 +463,7 @@ public class HpackDecoderTest
@Test
public void testHuffmanEncodedExtraPadding()
{
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
String encoded = "82868441" + "84" + "49509FFF";
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
@ -474,7 +475,7 @@ public class HpackDecoderTest
@Test
public void testHuffmanEncodedZeroPadding()
{
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
String encoded = "82868441" + "83" + "495090";
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
@ -487,7 +488,7 @@ public class HpackDecoderTest
@Test
public void testHuffmanEncodedWithEOS()
{
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
String encoded = "82868441" + "87" + "497FFFFFFF427F";
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
@ -499,7 +500,7 @@ public class HpackDecoderTest
@Test
public void testHuffmanEncodedOneIncompleteOctet()
{
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
String encoded = "82868441" + "81" + "FE";
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
@ -511,7 +512,7 @@ public class HpackDecoderTest
@Test
public void testHuffmanEncodedTwoIncompleteOctet()
{
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
String encoded = "82868441" + "82" + "FFFE";
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
@ -523,7 +524,7 @@ public class HpackDecoderTest
@Test
public void testZeroLengthName()
{
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
String encoded = "00000130";
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
@ -534,7 +535,7 @@ public class HpackDecoderTest
@Test
public void testZeroLengthValue() throws Exception
{
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
String encoded = "00016800";
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
@ -546,7 +547,7 @@ public class HpackDecoderTest
@Test
public void testUpperCaseName()
{
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
String encoded = "0001480130";
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));
@ -557,7 +558,7 @@ public class HpackDecoderTest
@Test
public void testWhiteSpaceName()
{
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
String encoded = "0001200130";
ByteBuffer buffer = ByteBuffer.wrap(StringUtil.fromHexString(encoded));

View File

@ -24,6 +24,7 @@ import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http.MetaData.Response;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.NanoTime;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
@ -43,7 +44,7 @@ public class HpackTest
public void encodeDecodeResponseTest() throws Exception
{
HpackEncoder encoder = new HpackEncoder();
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);
ByteBuffer buffer = BufferUtil.allocateDirect(16 * 1024);
HttpFields.Mutable fields0 = HttpFields.build()
@ -98,7 +99,7 @@ public class HpackTest
public void encodeDecodeTooLargeTest() throws Exception
{
HpackEncoder encoder = new HpackEncoder();
HpackDecoder decoder = new HpackDecoder(164);
HpackDecoder decoder = new HpackDecoder(164, NanoTime::now);
ByteBuffer buffer = BufferUtil.allocateDirect(16 * 1024);
HttpFields fields0 = HttpFields.build()
@ -158,7 +159,7 @@ public class HpackTest
@Test
public void evictReferencedFieldTest() throws Exception
{
HpackDecoder decoder = new HpackDecoder(1024);
HpackDecoder decoder = new HpackDecoder(1024, NanoTime::now);
decoder.setMaxTableCapacity(200);
HpackEncoder encoder = new HpackEncoder();
encoder.setMaxTableCapacity(decoder.getMaxTableCapacity());
@ -205,7 +206,7 @@ public class HpackTest
public void testHopHeadersAreRemoved() throws Exception
{
HpackEncoder encoder = new HpackEncoder();
HpackDecoder decoder = new HpackDecoder(16384);
HpackDecoder decoder = new HpackDecoder(16384, NanoTime::now);
HttpFields input = HttpFields.build()
.add(HttpHeader.ACCEPT, "*")
@ -232,7 +233,7 @@ public class HpackTest
public void testTETrailers() throws Exception
{
HpackEncoder encoder = new HpackEncoder();
HpackDecoder decoder = new HpackDecoder(16384);
HpackDecoder decoder = new HpackDecoder(16384, NanoTime::now);
String teValue = "trailers";
String trailerValue = "Custom";
@ -257,7 +258,7 @@ public class HpackTest
public void testColonHeaders() throws Exception
{
HpackEncoder encoder = new HpackEncoder();
HpackDecoder decoder = new HpackDecoder(16384);
HpackDecoder decoder = new HpackDecoder(16384, NanoTime::now);
HttpFields input = HttpFields.build()
.add(":status", "200")

View File

@ -23,6 +23,7 @@ import org.eclipse.jetty.http3.internal.Grease;
import org.eclipse.jetty.http3.internal.HTTP3ErrorCode;
import org.eclipse.jetty.http3.qpack.QpackDecoder;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.NanoTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -43,12 +44,15 @@ public class MessageParser
private final BooleanSupplier isLast;
private BodyParser unknownBodyParser;
private State state = State.HEADER;
protected boolean dataMode;
private boolean dataMode;
private long beginNanoTime;
private boolean beginNanoTimeStored;
public MessageParser(ParserListener listener, QpackDecoder decoder, long streamId, BooleanSupplier isLast)
{
this.listener = listener;
this.decoder = decoder;
decoder.setBeginNanoTimeSupplier(this::getBeginNanoTime);
this.streamId = streamId;
this.isLast = isLast;
}
@ -66,6 +70,21 @@ public class MessageParser
{
headerParser.reset();
state = State.HEADER;
beginNanoTimeStored = false;
}
private void storeBeginNanoTime()
{
if (!beginNanoTimeStored)
{
beginNanoTime = NanoTime.now();
beginNanoTimeStored = true;
}
}
private long getBeginNanoTime()
{
return beginNanoTime;
}
public ParserListener getListener()
@ -101,6 +120,7 @@ public class MessageParser
{
case HEADER:
{
storeBeginNanoTime();
if (headerParser.parse(buffer))
{
state = State.BODY;

View File

@ -23,9 +23,11 @@ import org.eclipse.jetty.http3.frames.DataFrame;
import org.eclipse.jetty.http3.internal.generator.MessageGenerator;
import org.eclipse.jetty.http3.internal.parser.MessageParser;
import org.eclipse.jetty.http3.internal.parser.ParserListener;
import org.eclipse.jetty.http3.qpack.QpackDecoder;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.NullByteBufferPool;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.NanoTime;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
@ -58,6 +60,8 @@ public class DataGenerateParseTest
new MessageGenerator(null, true).generate(lease, 0, input, null);
List<DataFrame> frames = new ArrayList<>();
QpackDecoder decoder = new QpackDecoder(instructions -> {});
decoder.setBeginNanoTimeSupplier(NanoTime::now);
MessageParser parser = new MessageParser(new ParserListener()
{
@Override
@ -65,7 +69,7 @@ public class DataGenerateParseTest
{
frames.add(frame);
}
}, null, 13, () -> true);
}, decoder, 13, () -> true);
parser.init(UnaryOperator.identity());
for (ByteBuffer buffer : lease.getByteBuffers())
{

View File

@ -31,6 +31,7 @@ import org.eclipse.jetty.http3.qpack.QpackDecoder;
import org.eclipse.jetty.http3.qpack.QpackEncoder;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.NullByteBufferPool;
import org.eclipse.jetty.util.NanoTime;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -54,6 +55,7 @@ public class HeadersGenerateParseTest
QpackDecoder decoder = new QpackDecoder(instructions -> {});
decoder.setMaxHeadersSize(4 * 1024);
decoder.setBeginNanoTimeSupplier(NanoTime::now);
List<HeadersFrame> frames = new ArrayList<>();
MessageParser parser = new MessageParser(new ParserListener()
{

View File

@ -21,6 +21,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.LongSupplier;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.MetaData;
@ -59,6 +60,7 @@ public class QpackDecoder implements Dumpable
private int _maxHeadersSize;
private int _maxBlockedStreams;
private int _maxTableCapacity;
private LongSupplier _beginNanoTimeSupplier;
private static class MetaDataNotification
{
@ -103,6 +105,11 @@ public class QpackDecoder implements Dumpable
return _maxHeadersSize;
}
public void setBeginNanoTimeSupplier(LongSupplier beginNanoTimeSupplier)
{
_beginNanoTimeSupplier = beginNanoTimeSupplier;
}
/**
* @param maxHeadersSize The maximum allowed size of a headers block, expressed as total of all name and value characters, plus 32 per field
*/
@ -180,7 +187,7 @@ 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, handler, requiredInsertCount, base, buffer);
EncodedFieldSection encodedFieldSection = new EncodedFieldSection(streamId, handler, requiredInsertCount, base, buffer, _beginNanoTimeSupplier.getAsLong());
// Decode it straight away if we can, otherwise add it to the list of EncodedFieldSections.
if (requiredInsertCount <= insertCount)

View File

@ -22,6 +22,7 @@ import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http3.qpack.QpackException;
import org.eclipse.jetty.util.NanoTime;
import static org.eclipse.jetty.http3.qpack.QpackException.H3_GENERAL_PROTOCOL_ERROR;
@ -40,6 +41,7 @@ public class MetaDataBuilder
private QpackException.StreamException _streamException;
private boolean _request;
private boolean _response;
private long _beginNanoTime = Long.MIN_VALUE;
/**
* @param maxHeadersSize The maximum size of the headers, expressed as total name and value characters.
@ -59,6 +61,13 @@ public class MetaDataBuilder
return _maxSize;
}
public void setBeginNanoTime(long beginNanoTime)
{
if (beginNanoTime == Long.MIN_VALUE)
beginNanoTime++;
_beginNanoTime = beginNanoTime;
}
/**
* Get the size.
*
@ -246,10 +255,13 @@ public class MetaDataBuilder
if (_path == null)
throw new QpackException.StreamException(H3_GENERAL_PROTOCOL_ERROR, "No Path");
}
long nanoTime = _beginNanoTime == Long.MIN_VALUE ? NanoTime.now() : _beginNanoTime;
_beginNanoTime = Long.MIN_VALUE;
if (isConnect)
return new MetaData.ConnectRequest(_scheme, _authority, _path, fields, _protocol);
return new MetaData.ConnectRequest(nanoTime, _scheme, _authority, _path, fields, _protocol);
else
return new MetaData.Request(
nanoTime,
_method,
_scheme.asString(),
_authority,

View File

@ -44,13 +44,15 @@ public class EncodedFieldSection
private final int _requiredInsertCount;
private final int _base;
private final QpackDecoder.Handler _handler;
private final long _beginNanoTime;
public EncodedFieldSection(long streamId, QpackDecoder.Handler handler, int requiredInsertCount, int base, ByteBuffer content) throws QpackException
public EncodedFieldSection(long streamId, QpackDecoder.Handler handler, int requiredInsertCount, int base, ByteBuffer content, long beginNanoTime) throws QpackException
{
_streamId = streamId;
_requiredInsertCount = requiredInsertCount;
_base = base;
_handler = handler;
_beginNanoTime = beginNanoTime;
try
{
@ -104,6 +106,7 @@ public class EncodedFieldSection
HttpField decodedField = encodedField.decode(context);
metaDataBuilder.emit(decodedField);
}
metaDataBuilder.setBeginNanoTime(_beginNanoTime);
return metaDataBuilder.build();
}

View File

@ -24,6 +24,7 @@ import org.eclipse.jetty.http3.qpack.internal.instruction.LiteralNameEntryInstru
import org.eclipse.jetty.http3.qpack.internal.instruction.SectionAcknowledgmentInstruction;
import org.eclipse.jetty.http3.qpack.internal.instruction.SetCapacityInstruction;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.NanoTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -56,6 +57,7 @@ public class BlockedStreamsTest
_decoderHandler = new TestDecoderHandler();
_encoder = new QpackEncoder(_encoderHandler, MAX_BLOCKED_STREAMS);
_decoder = new QpackDecoder(_decoderHandler, MAX_HEADER_SIZE);
_decoder.setBeginNanoTimeSupplier(NanoTime::now);
}
@Test

View File

@ -28,6 +28,7 @@ import org.eclipse.jetty.http3.qpack.internal.instruction.SetCapacityInstruction
import org.eclipse.jetty.http3.qpack.internal.parser.DecoderInstructionParser;
import org.eclipse.jetty.http3.qpack.internal.parser.EncoderInstructionParser;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.NanoTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@ -65,6 +66,7 @@ public class EncodeDecodeTest
}
};
_decoder = new QpackDecoder(_decoderHandler, MAX_HEADER_SIZE);
_decoder.setBeginNanoTimeSupplier(NanoTime::now);
_encoderInstructionParser = new EncoderInstructionParser(new EncoderParserDebugHandler(_encoder));
_decoderInstructionParser = new DecoderInstructionParser(new DecoderParserDebugHandler(_decoder));

View File

@ -20,6 +20,7 @@ import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.util.NanoTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -40,6 +41,7 @@ public class EvictionTest
_decoder = new QpackDecoder(_decoderHandler);
_decoder.setMaxHeadersSize(1024);
_decoder.setMaxTableCapacity(4 * 1024);
_decoder.setBeginNanoTimeSupplier(NanoTime::now);
_encoder = new QpackEncoder(_encoderHandler)
{

View File

@ -18,6 +18,7 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.http3.qpack.QpackException.SessionException;
import org.eclipse.jetty.http3.qpack.internal.instruction.SectionAcknowledgmentInstruction;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.NanoTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -48,6 +49,7 @@ public class SectionAcknowledgmentTest
_decoderHandler = new TestDecoderHandler();
_encoder = new QpackEncoder(_encoderHandler, MAX_BLOCKED_STREAMS);
_decoder = new QpackDecoder(_decoderHandler, MAX_HEADER_SIZE);
_decoder.setBeginNanoTimeSupplier(NanoTime::now);
}
@Test