From b77a8054a2f3d883ad99df2eb9a954b0b25eb8d3 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 23 Nov 2023 15:02:40 +0100 Subject: [PATCH] #9900 backport Accurate implementation of H3 Request.beginNanoTime() Signed-off-by: Ludovic Orban --- .../http3/internal/parser/MessageParser.java | 22 ++++++++++++++++++- .../http3/internal/DataGenerateParseTest.java | 6 ++++- .../internal/HeadersGenerateParseTest.java | 2 ++ .../jetty/http3/qpack/QpackDecoder.java | 9 +++++++- .../internal/metadata/MetaDataBuilder.java | 14 +++++++++++- .../internal/parser/EncodedFieldSection.java | 5 ++++- .../jetty/http3/qpack/BlockedStreamsTest.java | 2 ++ .../jetty/http3/qpack/EncodeDecodeTest.java | 2 ++ .../jetty/http3/qpack/EvictionTest.java | 2 ++ .../qpack/SectionAcknowledgmentTest.java | 2 ++ 10 files changed, 61 insertions(+), 5 deletions(-) diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/MessageParser.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/MessageParser.java index f06ff0d3e4b..4e035c6529b 100644 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/MessageParser.java +++ b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/MessageParser.java @@ -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; diff --git a/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/DataGenerateParseTest.java b/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/DataGenerateParseTest.java index 6114db0bd3f..31f36e22f0e 100644 --- a/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/DataGenerateParseTest.java +++ b/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/DataGenerateParseTest.java @@ -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 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()) { diff --git a/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/HeadersGenerateParseTest.java b/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/HeadersGenerateParseTest.java index abe1637c11f..da080d2d932 100644 --- a/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/HeadersGenerateParseTest.java +++ b/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/HeadersGenerateParseTest.java @@ -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 frames = new ArrayList<>(); MessageParser parser = new MessageParser(new ParserListener() { diff --git a/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackDecoder.java b/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackDecoder.java index ebed4579154..f0b2fba0c71 100644 --- a/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackDecoder.java +++ b/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/QpackDecoder.java @@ -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) diff --git a/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/metadata/MetaDataBuilder.java b/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/metadata/MetaDataBuilder.java index 014aa749d6d..47c3024cac3 100644 --- a/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/metadata/MetaDataBuilder.java +++ b/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/metadata/MetaDataBuilder.java @@ -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, diff --git a/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/parser/EncodedFieldSection.java b/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/parser/EncodedFieldSection.java index ddcb939dacf..1b900bbe984 100644 --- a/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/parser/EncodedFieldSection.java +++ b/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/parser/EncodedFieldSection.java @@ -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(); } diff --git a/jetty-http3/http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/BlockedStreamsTest.java b/jetty-http3/http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/BlockedStreamsTest.java index e32a98778b5..edae7d0a2b4 100644 --- a/jetty-http3/http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/BlockedStreamsTest.java +++ b/jetty-http3/http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/BlockedStreamsTest.java @@ -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 diff --git a/jetty-http3/http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/EncodeDecodeTest.java b/jetty-http3/http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/EncodeDecodeTest.java index 1bebb6bb758..4f1cf20e6ae 100644 --- a/jetty-http3/http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/EncodeDecodeTest.java +++ b/jetty-http3/http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/EncodeDecodeTest.java @@ -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)); diff --git a/jetty-http3/http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/EvictionTest.java b/jetty-http3/http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/EvictionTest.java index 14f566cd438..d8a6f2c713e 100644 --- a/jetty-http3/http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/EvictionTest.java +++ b/jetty-http3/http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/EvictionTest.java @@ -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) { diff --git a/jetty-http3/http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/SectionAcknowledgmentTest.java b/jetty-http3/http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/SectionAcknowledgmentTest.java index 7f149dd7f42..718ef18f3e3 100644 --- a/jetty-http3/http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/SectionAcknowledgmentTest.java +++ b/jetty-http3/http3-qpack/src/test/java/org/eclipse/jetty/http3/qpack/SectionAcknowledgmentTest.java @@ -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