Skip to content

Commit

Permalink
#9900: Accurate implementation of H2C Request.beginNanoTime()
Browse files Browse the repository at this point in the history
Signed-off-by: Ludovic Orban <[email protected]>
  • Loading branch information
lorban committed Nov 21, 2023
1 parent 59a7bc1 commit 88aab9b
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
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;

Expand All @@ -53,6 +54,7 @@ public class Parser
private int maxSettingsKeys = SettingsFrame.DEFAULT_MAX_KEYS;
private boolean continuation;
private State state = State.HEADER;
protected long beginNanoTime = Long.MIN_VALUE;

public Parser(ByteBufferPool bufferPool, int maxHeaderSize)
{
Expand All @@ -63,7 +65,7 @@ public Parser(ByteBufferPool bufferPool, int maxHeaderSize, RateControl rateCont
{
this.bufferPool = bufferPool;
this.headerParser = new HeaderParser(rateControl == null ? RateControl.NO_RATE_CONTROL : rateControl);
this.hpackDecoder = new HpackDecoder(maxHeaderSize);
this.hpackDecoder = new HpackDecoder(maxHeaderSize, () -> beginNanoTime);
this.bodyParsers = new BodyParser[FrameType.values().length];
}

Expand Down Expand Up @@ -101,6 +103,7 @@ private void reset()
{
headerParser.reset();
state = State.HEADER;
beginNanoTime = Long.MIN_VALUE;
}

/**
Expand All @@ -124,6 +127,7 @@ public void parse(ByteBuffer buffer)
{
case HEADER:
{
beginNanoTime = NanoTime.now(); // TODO #9900 check beginNanoTime's accuracy
if (!parseHeader(buffer))
return;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.eclipse.jetty.http2.frames.FrameType;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.NanoTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -93,6 +94,7 @@ public void parse(ByteBuffer buffer)
{
case PREFACE:
{
beginNanoTime = NanoTime.now(); // TODO #9900 check beginNanoTime's accuracy
if (!prefaceParser.parse(buffer))
return;
if (notifyPreface)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.HttpFields;
Expand Down Expand Up @@ -43,14 +44,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();
Expand Down Expand Up @@ -296,6 +301,7 @@ public MetaData decode(ByteBuffer buffer) throws HpackException.SessionException
}
}

_builder.setBeginNanoTime(_beginNanoTimeSupplier.getAsLong());
return _builder.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,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.
Expand All @@ -63,6 +64,13 @@ public void setMaxSize(int maxSize)
_maxSize = maxSize;
}

public void setBeginNanoTime(long beginNanoTime)
{
if (beginNanoTime == Long.MIN_VALUE)
beginNanoTime++;
_beginNanoTime = beginNanoTime;
}

/**
* Get the size.
*
Expand Down Expand Up @@ -250,12 +258,14 @@ public MetaData build() throws HpackException.StreamException
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(NanoTime.now(), _scheme, _authority, _path, fields, _protocol); // TODO #9900 make beginNanoTime accurate
return new MetaData.ConnectRequest(nanoTime, _scheme, _authority, _path, fields, _protocol);
else
return new MetaData.Request(
NanoTime.now(), // TODO #9900 make beginNanoTime accurate
_method,
nanoTime,
_method,
_scheme.asString(),
_authority,
_path,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.eclipse.jetty.http2.hpack.HpackException.SessionException;
import org.eclipse.jetty.http2.hpack.HpackException.StreamException;
import org.eclipse.jetty.http2.hpack.internal.MetaDataBuilder;
import org.eclipse.jetty.util.NanoTime;
import org.eclipse.jetty.util.StringUtil;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -58,7 +59,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";
Expand Down Expand Up @@ -106,7 +107,7 @@ public void testDecodeD3() throws Exception
@Test
public void testDecodeD4() throws Exception
{
HpackDecoder decoder = new HpackDecoder(8192);
HpackDecoder decoder = new HpackDecoder(8192, NanoTime::now);

// First request
String encoded = "828684418cf1e3c2e5f23a6ba0ab90f4ff";
Expand Down Expand Up @@ -141,7 +142,7 @@ public void testDecodeWithArrayOffset() throws Exception
{
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];
Expand All @@ -163,7 +164,7 @@ public void testDecodeWithArrayOffset() throws Exception
@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);
Expand All @@ -187,7 +188,7 @@ public void testNghttpx() throws Exception
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));
Expand All @@ -205,7 +206,7 @@ public void testResize() throws Exception
{
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.getHttpFields().get(HttpHeader.HOST), is("localhost0"));
assertThat(metaData.getHttpFields().get(HttpHeader.COOKIE), is("abcdefghij"));
Expand All @@ -227,7 +228,7 @@ public void testBadResize() throws Exception

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);
Expand All @@ -245,7 +246,7 @@ public void testTooBigToIndex() throws Exception
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);

Expand All @@ -259,7 +260,7 @@ public void testUnknownIndex() throws Exception
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
Expand Down Expand Up @@ -445,7 +446,7 @@ public void test8123RequestPseudoHeaderFields() throws Exception
@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));
Expand All @@ -463,7 +464,7 @@ public void testHuffmanEncodedStandard() throws Exception
@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));
Expand All @@ -475,7 +476,7 @@ public void testHuffmanEncodedExtraPadding()
@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));
Expand All @@ -488,7 +489,7 @@ public void testHuffmanEncodedZeroPadding()
@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));
Expand All @@ -500,7 +501,7 @@ public void testHuffmanEncodedWithEOS()
@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));
Expand All @@ -512,7 +513,7 @@ public void testHuffmanEncodedOneIncompleteOctet()
@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));
Expand All @@ -524,7 +525,7 @@ public void testHuffmanEncodedTwoIncompleteOctet()
@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));
Expand All @@ -535,7 +536,7 @@ public void testZeroLengthName()
@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));
Expand All @@ -547,7 +548,7 @@ public void testZeroLengthValue() throws Exception
@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));
Expand All @@ -558,7 +559,7 @@ public void testUpperCaseName()
@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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
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;
Expand All @@ -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);

long contentLength = 1024;
Expand Down Expand Up @@ -99,7 +100,7 @@ public void encodeDecodeResponseTest() throws Exception
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()
Expand Down Expand Up @@ -159,7 +160,7 @@ public void encodeNonAscii() throws Exception
@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());
Expand Down Expand Up @@ -206,7 +207,7 @@ public void evictReferencedFieldTest() throws Exception
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, "*")
Expand All @@ -233,7 +234,7 @@ public void testHopHeadersAreRemoved() throws Exception
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";
Expand All @@ -258,7 +259,7 @@ public void testTETrailers() throws Exception
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")
Expand Down

0 comments on commit 88aab9b

Please sign in to comment.