diff --git a/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicChannel.java b/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicChannel.java
index 4fc21740e..c939ec25b 100644
--- a/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicChannel.java
+++ b/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicChannel.java
@@ -172,6 +172,13 @@ default ChannelPromise voidPromise() {
*/
boolean isTimedOut();
+ /**
+ * Returns the {@link QuicTransportParameters} of the peer once received, or {@code null} if not known yet.
+ *
+ * @return peerTransportParams.
+ */
+ QuicTransportParameters peerTransportParameters();
+
/**
* Creates a stream that is using this {@link QuicChannel} and notifies the {@link Future} once done.
* The {@link ChannelHandler} (if not {@code null}) is added to the {@link io.netty.channel.ChannelPipeline} of the
diff --git a/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicConnectionStats.java b/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicConnectionStats.java
index d6e094f22..0be920974 100644
--- a/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicConnectionStats.java
+++ b/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicConnectionStats.java
@@ -17,7 +17,7 @@
/**
* Statistics about the {@code QUIC} connection. If unknown by the implementation it might return {@code -1} values
- * for the various methods.
+ * for the various methods.
*/
public interface QuicConnectionStats {
/**
diff --git a/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicTransportParameters.java b/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicTransportParameters.java
new file mode 100644
index 000000000..d6c63516a
--- /dev/null
+++ b/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicTransportParameters.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2023 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.incubator.codec.quic;
+
+/**
+ * Transport parameters for QUIC.
+ */
+public interface QuicTransportParameters {
+
+ /**
+ * The maximum idle timeout.
+ * @return timeout.
+ */
+ long maxIdleTimeout();
+
+ /**
+ * The maximum UDP payload size.
+ *
+ * @return maximum payload size.
+ */
+ long maxUdpPayloadSize();
+
+ /**
+ * The initial flow control maximum data for the connection.
+ *
+ * @return flowcontrol.
+ */
+ long initialMaxData();
+
+ /**
+ * The initial flow control maximum data for local bidirectional streams.
+ *
+ * @return flowcontrol.
+ */
+ long initialMaxStreamDataBidiLocal();
+
+ /**
+ * The initial flow control maximum data for remote bidirectional streams.
+ *
+ * @return flowcontrol.
+ */
+ long initialMaxStreamDataBidiRemote();
+
+ /**
+ * The initial flow control maximum data for unidirectional streams.
+ *
+ * @return flowcontrol.
+ */
+ long initialMaxStreamDataUni();
+
+
+ /**
+ * The initial maximum bidirectional streams.
+ *
+ * @return streams.
+ */
+ long initialMaxStreamsBidi();
+
+ /**
+ * The initial maximum unidirectional streams.
+ *
+ * @return streams.
+ */
+ long initialMaxStreamsUni();
+
+ /**
+ * The ACK delay exponent
+ *
+ * @return exponent.
+ */
+ long ackDelayExponent();
+
+ /**
+ * The max ACK delay.
+ *
+ * @return delay.
+ */
+ long maxAckDelay();
+
+ /**
+ * Whether active migration is disabled.
+ *
+ * @return disabled.
+ */
+ boolean disableActiveMigration();
+
+ /**
+ * The active connection ID limit.
+ *
+ * @return limit.
+ */
+ long activeConnIdLimit();
+
+ /**
+ * DATAGRAM frame extension parameter, if any.
+ *
+ * @return param.
+ */
+ long maxDatagramFrameSize();
+}
diff --git a/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/Quiche.java b/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/Quiche.java
index d136d02cc..5f1472f0e 100644
--- a/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/Quiche.java
+++ b/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/Quiche.java
@@ -456,6 +456,13 @@ static native int quiche_conn_stream_priority(
*/
static native long[] quiche_conn_stats(long connAddr);
+ /**
+ * See
+ *
+ * quiche_conn_stats.
+ */
+ static native long[] quiche_conn_peer_transport_params(long connAddr);
+
/**
* See
* quiche_conn_timeout_as_nanos.
diff --git a/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicheQuicChannel.java b/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicheQuicChannel.java
index f2817de96..9377f2b42 100644
--- a/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicheQuicChannel.java
+++ b/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicheQuicChannel.java
@@ -1840,4 +1840,9 @@ private QuicConnectionStats collectStats0(QuicheQuicConnection connection, Promi
promise.setSuccess(connStats);
return connStats;
}
+
+ @Override
+ public QuicTransportParameters peerTransportParameters() {
+ return connection.peerParameters();
+ }
}
diff --git a/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicheQuicConnection.java b/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicheQuicConnection.java
index d4e0d9ac2..b9642fe0a 100644
--- a/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicheQuicConnection.java
+++ b/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicheQuicConnection.java
@@ -155,6 +155,20 @@ QuicConnectionAddress connectionId(Supplier idSupplier) {
return id == null ? null : new QuicConnectionAddress(id);
}
+ QuicheQuicTransportParameters peerParameters() {
+ final long[] ret;
+ synchronized (this) {
+ if (connection == -1) {
+ return null;
+ }
+ ret = Quiche.quiche_conn_peer_transport_params(connection);
+ }
+ if (ret == null) {
+ return null;
+ }
+ return new QuicheQuicTransportParameters(ret);
+ }
+
QuicheQuicSslEngine engine() {
return engine;
}
diff --git a/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicheQuicTransportParameters.java b/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicheQuicTransportParameters.java
new file mode 100644
index 000000000..55e8a709c
--- /dev/null
+++ b/codec-classes-quic/src/main/java/io/netty/incubator/codec/quic/QuicheQuicTransportParameters.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2023 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.incubator.codec.quic;
+
+final class QuicheQuicTransportParameters implements QuicTransportParameters {
+ private final long[] values;
+
+ QuicheQuicTransportParameters(long[] values) {
+ this.values = values;
+ }
+
+ @Override
+ public long maxIdleTimeout() {
+ return values[0];
+ }
+
+ @Override
+ public long maxUdpPayloadSize() {
+ return values[1];
+ }
+
+ @Override
+ public long initialMaxData() {
+ return values[2];
+ }
+
+ @Override
+ public long initialMaxStreamDataBidiLocal() {
+ return values[3];
+ }
+
+ @Override
+ public long initialMaxStreamDataBidiRemote() {
+ return values[4];
+ }
+
+ @Override
+ public long initialMaxStreamDataUni() {
+ return values[5];
+ }
+
+ @Override
+ public long initialMaxStreamsBidi() {
+ return values[6];
+ }
+
+ @Override
+ public long initialMaxStreamsUni() {
+ return values[7];
+ }
+
+ @Override
+ public long ackDelayExponent() {
+ return values[8];
+ }
+
+ @Override
+ public long maxAckDelay() {
+ return values[9];
+ }
+
+ @Override
+ public boolean disableActiveMigration() {
+ return values[10] == 1;
+ }
+
+ @Override
+ public long activeConnIdLimit() {
+ return values[11];
+ }
+
+ @Override
+ public long maxDatagramFrameSize() {
+ return values[12];
+ }
+}
diff --git a/codec-native-quic/src/main/c/netty_quic_quiche.c b/codec-native-quic/src/main/c/netty_quic_quiche.c
index 7f8fa31a0..bb57701cb 100644
--- a/codec-native-quic/src/main/c/netty_quic_quiche.c
+++ b/codec-native-quic/src/main/c/netty_quic_quiche.c
@@ -518,6 +518,38 @@ static jlongArray netty_quiche_conn_stats(JNIEnv* env, jclass clazz, jlong conn)
return statsArray;
}
+static jlongArray netty_quiche_conn_peer_transport_params(JNIEnv* env, jclass clazz, jlong conn) {
+ // See https://github.com/cloudflare/quiche/blob/master/quiche/include/quiche.h#L563
+ quiche_transport_params params = {0,0,0,0,0,0,0,0,0,0,false,0,0};
+ if (!quiche_conn_peer_transport_params((quiche_conn *) conn, ¶ms)) {
+ return NULL;
+ }
+
+ jlongArray paramsArray = (*env)->NewLongArray(env, 13);
+ if (paramsArray == NULL) {
+ // This will put an OOME on the stack
+ return NULL;
+ }
+ jlong paramsArrayElements[] = {
+ (jlong)params.peer_max_idle_timeout,
+ (jlong)params.peer_max_udp_payload_size,
+ (jlong)params.peer_initial_max_data,
+ (jlong)params.peer_initial_max_stream_data_bidi_local,
+ (jlong)params.peer_initial_max_stream_data_bidi_remote,
+ (jlong)params.peer_initial_max_stream_data_uni,
+ (jlong)params.peer_initial_max_streams_bidi,
+ (jlong)params.peer_initial_max_streams_uni,
+ (jlong)params.peer_ack_delay_exponent,
+ (jlong)params.peer_disable_active_migration ? 1: 0,
+ (jlong)params.peer_active_conn_id_limit,
+ (jlong)params.peer_max_datagram_frame_size
+ };
+ (*env)->SetLongArrayRegion(env, paramsArray, 0, 13, paramsArrayElements);
+ return paramsArray;
+}
+
+
+
static jlong netty_quiche_conn_timeout_as_nanos(JNIEnv* env, jclass clazz, jlong conn) {
return quiche_conn_timeout_as_nanos((quiche_conn *) conn);
}
@@ -839,6 +871,7 @@ static const JNINativeMethod fixed_method_table[] = {
{ "quiche_conn_is_closed", "(J)Z", (void *) netty_quiche_conn_is_closed },
{ "quiche_conn_is_timed_out", "(J)Z", (void *) netty_quiche_conn_is_timed_out },
{ "quiche_conn_stats", "(J)[J", (void *) netty_quiche_conn_stats },
+ { "quiche_conn_peer_transport_params", "(J)[J", (void *) netty_quiche_conn_peer_transport_params },
{ "quiche_conn_timeout_as_nanos", "(J)J", (void *) netty_quiche_conn_timeout_as_nanos },
{ "quiche_conn_on_timeout", "(J)V", (void *) netty_quiche_conn_on_timeout },
{ "quiche_conn_readable", "(J)J", (void *) netty_quiche_conn_readable },
diff --git a/codec-native-quic/src/test/java/io/netty/incubator/codec/quic/QuicTransportParametersTest.java b/codec-native-quic/src/test/java/io/netty/incubator/codec/quic/QuicTransportParametersTest.java
new file mode 100644
index 000000000..dd0e189c0
--- /dev/null
+++ b/codec-native-quic/src/test/java/io/netty/incubator/codec/quic/QuicTransportParametersTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2023 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.incubator.codec.quic;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.util.concurrent.ImmediateEventExecutor;
+import io.netty.util.concurrent.Promise;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.concurrent.Executor;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class QuicTransportParametersTest extends AbstractQuicTest {
+
+ @ParameterizedTest
+ @MethodSource("newSslTaskExecutors")
+ public void testParameters(Executor executor) throws Throwable {
+ Channel server = null;
+ Channel channel = null;
+ Promise serverParams = ImmediateEventExecutor.INSTANCE.newPromise();
+ QuicChannelValidationHandler serverHandler = new QuicChannelValidationHandler() {
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) {
+ QuicheQuicChannel channel = (QuicheQuicChannel) ctx.channel();
+ serverParams.setSuccess(channel.peerTransportParameters());
+ ctx.fireChannelActive();
+ }
+ };
+ QuicChannelValidationHandler clientHandler = new QuicChannelValidationHandler();
+ try {
+ server = QuicTestUtils.newServer(executor, serverHandler, new ChannelInboundHandlerAdapter() {
+ @Override
+ public boolean isSharable() {
+ return true;
+ }
+ });
+ channel = QuicTestUtils.newClient(executor);
+
+ QuicChannel quicChannel = QuicTestUtils.newQuicChannelBootstrap(channel)
+ .handler(clientHandler)
+ .streamHandler(new ChannelInboundHandlerAdapter())
+ .remoteAddress(server.localAddress())
+ .connect().get();
+ assertTransportParameters(quicChannel.peerTransportParameters());
+ assertTransportParameters(serverParams.sync().getNow());
+
+ quicChannel.close().sync();
+ serverHandler.assertState();
+ clientHandler.assertState();
+ } finally {
+ QuicTestUtils.closeIfNotNull(channel);
+ QuicTestUtils.closeIfNotNull(server);
+
+ shutdown(executor);
+ }
+ }
+
+ private static void assertTransportParameters(QuicTransportParameters parameters) {
+ assertNotNull(parameters);
+ assertThat(parameters.maxIdleTimeout(), greaterThanOrEqualTo(1L));
+ assertThat(parameters.maxUdpPayloadSize(), greaterThanOrEqualTo(1L));
+ assertThat(parameters.initialMaxData(), greaterThanOrEqualTo(1L));
+ assertThat(parameters.initialMaxStreamDataBidiLocal(), greaterThanOrEqualTo(1L));
+ assertThat(parameters.initialMaxStreamDataBidiRemote(), greaterThanOrEqualTo(1L));
+ assertThat(parameters.initialMaxStreamDataUni(), greaterThanOrEqualTo(1L));
+ assertThat(parameters.initialMaxStreamsBidi(), greaterThanOrEqualTo(1L));
+ assertThat(parameters.initialMaxStreamsUni(), greaterThanOrEqualTo(1L));
+ assertThat(parameters.ackDelayExponent(), greaterThanOrEqualTo(1L));
+ assertThat(parameters.maxAckDelay(), greaterThanOrEqualTo(1L));
+ assertFalse(parameters.disableActiveMigration());
+ assertThat(parameters.activeConnIdLimit(), greaterThanOrEqualTo(1L));
+ assertThat(parameters.maxDatagramFrameSize(), greaterThanOrEqualTo(1L));
+ }
+}