Fixes #10390 - jetty http3 client and nghttpx. (#10744)

Fixed handling of long settings values, so that they do not overflow. Added logging for GREASE cases.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2023-10-18 17:06:56 +02:00 committed by GitHub
parent caddfb5a41
commit 8c94490e18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 92 additions and 20 deletions

View File

@ -24,6 +24,7 @@ import org.eclipse.jetty.http3.frames.SettingsFrame;
import org.eclipse.jetty.http3.internal.ControlFlusher;
import org.eclipse.jetty.http3.internal.DecoderStreamConnection;
import org.eclipse.jetty.http3.internal.EncoderStreamConnection;
import org.eclipse.jetty.http3.internal.Grease;
import org.eclipse.jetty.http3.internal.HTTP3ErrorCode;
import org.eclipse.jetty.http3.internal.InstructionFlusher;
import org.eclipse.jetty.http3.internal.InstructionHandler;
@ -63,7 +64,7 @@ public class ClientHTTP3Session extends ClientProtocolSession
if (LOG.isDebugEnabled())
LOG.debug("initializing HTTP/3 streams");
long encoderStreamId = getQuicSession().newStreamId(StreamType.CLIENT_UNIDIRECTIONAL);
long encoderStreamId = newStreamId(StreamType.CLIENT_UNIDIRECTIONAL);
QuicStreamEndPoint encoderEndPoint = openInstructionEndPoint(encoderStreamId);
InstructionFlusher encoderInstructionFlusher = new InstructionFlusher(quicSession, encoderEndPoint, EncoderStreamConnection.STREAM_TYPE);
encoder = new QpackEncoder(new InstructionHandler(encoderInstructionFlusher));
@ -72,7 +73,7 @@ public class ClientHTTP3Session extends ClientProtocolSession
if (LOG.isDebugEnabled())
LOG.debug("created encoder stream #{} on {}", encoderStreamId, encoderEndPoint);
long decoderStreamId = getQuicSession().newStreamId(StreamType.CLIENT_UNIDIRECTIONAL);
long decoderStreamId = newStreamId(StreamType.CLIENT_UNIDIRECTIONAL);
QuicStreamEndPoint decoderEndPoint = openInstructionEndPoint(decoderStreamId);
InstructionFlusher decoderInstructionFlusher = new InstructionFlusher(quicSession, decoderEndPoint, DecoderStreamConnection.STREAM_TYPE);
decoder = new QpackDecoder(new InstructionHandler(decoderInstructionFlusher));
@ -80,7 +81,7 @@ public class ClientHTTP3Session extends ClientProtocolSession
if (LOG.isDebugEnabled())
LOG.debug("created decoder stream #{} on {}", decoderStreamId, decoderEndPoint);
long controlStreamId = getQuicSession().newStreamId(StreamType.CLIENT_UNIDIRECTIONAL);
long controlStreamId = newStreamId(StreamType.CLIENT_UNIDIRECTIONAL);
QuicStreamEndPoint controlEndPoint = openControlEndPoint(controlStreamId);
controlFlusher = new ControlFlusher(quicSession, controlEndPoint, true);
addBean(controlFlusher);
@ -106,6 +107,11 @@ public class ClientHTTP3Session extends ClientProtocolSession
return session;
}
public long newStreamId(StreamType streamType)
{
return getQuicSession().newStreamId(streamType);
}
@Override
protected void onStart()
{
@ -171,21 +177,27 @@ public class ClientHTTP3Session extends ClientProtocolSession
{
if (key == SettingsFrame.MAX_TABLE_CAPACITY)
{
int maxTableCapacity = value.intValue();
int maxTableCapacity = (int)Math.min(value, Integer.MAX_VALUE);
encoder.setMaxTableCapacity(maxTableCapacity);
encoder.setTableCapacity(Math.min(maxTableCapacity, configuration.getMaxEncoderTableCapacity()));
}
else if (key == SettingsFrame.MAX_FIELD_SECTION_SIZE)
{
// Must cap the maxHeaderSize to avoid large allocations.
int maxHeadersSize = Math.min(value.intValue(), configuration.getMaxRequestHeadersSize());
int maxHeadersSize = (int)Math.min(value, configuration.getMaxResponseHeadersSize());
encoder.setMaxHeadersSize(maxHeadersSize);
}
else if (key == SettingsFrame.MAX_BLOCKED_STREAMS)
{
int maxBlockedStreams = value.intValue();
int maxBlockedStreams = (int)Math.min(value, Integer.MAX_VALUE);
encoder.setMaxBlockedStreams(maxBlockedStreams);
}
else
{
// SPEC: grease and unknown settings are ignored.
if (LOG.isDebugEnabled())
LOG.debug("ignored {} setting {}={}", Grease.isGreaseValue(key) ? "grease" : "unknown", key, value);
}
});
}

View File

@ -93,7 +93,7 @@ public class HTTP3SessionClient extends HTTP3Session implements Session.Client
@Override
public CompletableFuture<Stream> newRequest(HeadersFrame frame, Stream.Client.Listener listener)
{
long streamId = getProtocolSession().getQuicSession().newStreamId(StreamType.CLIENT_BIDIRECTIONAL);
long streamId = getProtocolSession().newStreamId(StreamType.CLIENT_BIDIRECTIONAL);
return newRequest(streamId, frame, listener);
}

View File

@ -0,0 +1,48 @@
//
// ========================================================================
// 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.internal;
import java.util.concurrent.ThreadLocalRandom;
/**
* <p>A class to support GREASE (<a href="https://www.rfc-editor.org/rfc/rfc8701.txt">RFC 8701</a>) in HTTP/3.</p>
* <p>HTTP/3 GREASE values have the form {@code 0x1F * N + 0x21} with non negative values of {@code N}.</p>
*/
public class Grease
{
/**
* @param value the value to test
* @return whether the value is a GREASE value as defined by HTTP/3
*/
public static boolean isGreaseValue(long value)
{
if (value < 0)
return false;
return (value - 0x21) % 0x1F == 0;
}
/**
* @return a random grease value as defined by HTTP/3
*/
public static long generateGreaseValue()
{
// This constant avoids to overflow VarLenInt.
long n = ThreadLocalRandom.current().nextLong(0x210842108421084L);
return 0x1F * n + 0x21;
}
private Grease()
{
}
}

View File

@ -13,8 +13,6 @@
package org.eclipse.jetty.http3.internal;
import java.util.concurrent.ThreadLocalRandom;
public enum HTTP3ErrorCode
{
NO_ERROR(0x100),
@ -45,9 +43,7 @@ public enum HTTP3ErrorCode
public static long randomReservedCode()
{
// SPEC: reserved errors have the form 0x1F * n + 0x21.
// This constant avoids to overflow VarLenInt, which is how an error code is encoded.
long n = ThreadLocalRandom.current().nextLong(0x210842108421084L);
return 0x1F * n + 0x21;
return Grease.generateGreaseValue();
}
public long code()

View File

@ -17,6 +17,7 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import org.eclipse.jetty.http3.frames.FrameType;
import org.eclipse.jetty.http3.internal.Grease;
import org.eclipse.jetty.http3.internal.HTTP3ErrorCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -91,8 +92,9 @@ public class ControlParser
return;
}
// SPEC: grease and unknown frame types are ignored.
if (LOG.isDebugEnabled())
LOG.debug("ignoring unknown frame type {}", Long.toHexString(frameType));
LOG.debug("ignoring {} frame type {}", Grease.isGreaseValue(frameType) ? "grease" : "unknown", Long.toHexString(frameType));
BodyParser.Result result = unknownBodyParser.parse(buffer);
if (result == BodyParser.Result.NO_FRAME)

View File

@ -19,6 +19,7 @@ import java.util.function.BooleanSupplier;
import java.util.function.UnaryOperator;
import org.eclipse.jetty.http3.frames.FrameType;
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;
@ -128,8 +129,9 @@ public class MessageParser
return Result.NO_FRAME;
}
// SPEC: grease and unknown frame types are ignored.
if (LOG.isDebugEnabled())
LOG.debug("ignoring unknown frame type {}", Long.toHexString(frameType));
LOG.debug("ignoring {} frame type {}", Grease.isGreaseValue(frameType) ? "grease" : "unknown", Long.toHexString(frameType));
BodyParser.Result result = unknownBodyParser.parse(buffer);
if (result == BodyParser.Result.NO_FRAME)

View File

@ -24,6 +24,7 @@ import org.eclipse.jetty.http3.frames.SettingsFrame;
import org.eclipse.jetty.http3.internal.ControlFlusher;
import org.eclipse.jetty.http3.internal.DecoderStreamConnection;
import org.eclipse.jetty.http3.internal.EncoderStreamConnection;
import org.eclipse.jetty.http3.internal.Grease;
import org.eclipse.jetty.http3.internal.HTTP3ErrorCode;
import org.eclipse.jetty.http3.internal.InstructionFlusher;
import org.eclipse.jetty.http3.internal.InstructionHandler;
@ -62,7 +63,7 @@ public class ServerHTTP3Session extends ServerProtocolSession
if (LOG.isDebugEnabled())
LOG.debug("initializing HTTP/3 streams");
long encoderStreamId = getQuicSession().newStreamId(StreamType.SERVER_UNIDIRECTIONAL);
long encoderStreamId = newStreamId(StreamType.SERVER_UNIDIRECTIONAL);
QuicStreamEndPoint encoderEndPoint = openInstructionEndPoint(encoderStreamId);
InstructionFlusher encoderInstructionFlusher = new InstructionFlusher(quicSession, encoderEndPoint, EncoderStreamConnection.STREAM_TYPE);
encoder = new QpackEncoder(new InstructionHandler(encoderInstructionFlusher));
@ -71,7 +72,7 @@ public class ServerHTTP3Session extends ServerProtocolSession
if (LOG.isDebugEnabled())
LOG.debug("created encoder stream #{} on {}", encoderStreamId, encoderEndPoint);
long decoderStreamId = getQuicSession().newStreamId(StreamType.SERVER_UNIDIRECTIONAL);
long decoderStreamId = newStreamId(StreamType.SERVER_UNIDIRECTIONAL);
QuicStreamEndPoint decoderEndPoint = openInstructionEndPoint(decoderStreamId);
InstructionFlusher decoderInstructionFlusher = new InstructionFlusher(quicSession, decoderEndPoint, DecoderStreamConnection.STREAM_TYPE);
decoder = new QpackDecoder(new InstructionHandler(decoderInstructionFlusher));
@ -79,7 +80,7 @@ public class ServerHTTP3Session extends ServerProtocolSession
if (LOG.isDebugEnabled())
LOG.debug("created decoder stream #{} on {}", decoderStreamId, decoderEndPoint);
long controlStreamId = getQuicSession().newStreamId(StreamType.SERVER_UNIDIRECTIONAL);
long controlStreamId = newStreamId(StreamType.SERVER_UNIDIRECTIONAL);
QuicStreamEndPoint controlEndPoint = openControlEndPoint(controlStreamId);
controlFlusher = new ControlFlusher(quicSession, controlEndPoint, configuration.isUseOutputDirectByteBuffers());
addBean(controlFlusher);
@ -105,6 +106,11 @@ public class ServerHTTP3Session extends ServerProtocolSession
return session;
}
public long newStreamId(StreamType streamType)
{
return getQuicSession().newStreamId(streamType);
}
@Override
protected void onStart()
{
@ -170,21 +176,27 @@ public class ServerHTTP3Session extends ServerProtocolSession
{
if (key == SettingsFrame.MAX_TABLE_CAPACITY)
{
int maxTableCapacity = value.intValue();
int maxTableCapacity = (int)Math.min(value, Integer.MAX_VALUE);
encoder.setMaxTableCapacity(maxTableCapacity);
encoder.setTableCapacity(Math.min(maxTableCapacity, configuration.getMaxEncoderTableCapacity()));
}
else if (key == SettingsFrame.MAX_FIELD_SECTION_SIZE)
{
// Must cap the maxHeaderSize to avoid large allocations.
int maxHeadersSize = Math.min(value.intValue(), configuration.getMaxResponseHeadersSize());
int maxHeadersSize = (int)Math.min(value, configuration.getMaxResponseHeadersSize());
encoder.setMaxHeadersSize(maxHeadersSize);
}
else if (key == SettingsFrame.MAX_BLOCKED_STREAMS)
{
int maxBlockedStreams = value.intValue();
int maxBlockedStreams = (int)Math.min(value, Integer.MAX_VALUE);
encoder.setMaxBlockedStreams(maxBlockedStreams);
}
else
{
// SPEC: grease and unknown settings are ignored.
if (LOG.isDebugEnabled())
LOG.debug("ignored {} setting {}={}", Grease.isGreaseValue(key) ? "grease" : "unknown", key, value);
}
});
}