From d6a101d46364bc91d3d4612e946a2b8d7493e2b4 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Fri, 7 Oct 2022 17:54:40 +0200 Subject: [PATCH] #8695: update quiche to version 0.15.0 Signed-off-by: Ludovic Orban --- .../jetty/quic/common/QuicSession.java | 2 +- .../org/eclipse/jetty/quic/quiche/Quiche.java | 3 +- .../jetty/quic/quiche/QuicheConfig.java | 35 ++++- .../jetty/quic/quiche/QuicheConnection.java | 3 +- .../ForeignIncubatorQuicheConnection.java | 43 ++++-- .../quiche/foreign/incubator/quiche_h.java | 91 +++++++++++-- .../foreign/incubator/quiche_path_stats.java | 72 ++++++++++ .../foreign/incubator/quiche_recv_info.java | 16 ++- .../foreign/incubator/quiche_send_info.java | 11 +- .../foreign/incubator/quiche_stats.java | 11 +- .../foreign/incubator/LowLevelQuicheTest.java | 14 +- .../quic/quiche/jna/JnaQuicheConnection.java | 56 +++++--- .../jetty/quic/quiche/jna/LibQuiche.java | 127 +++++++++++++++--- .../quic/quiche/jna/LowLevelQuicheTest.java | 14 +- pom.xml | 2 +- 15 files changed, 402 insertions(+), 98 deletions(-) create mode 100644 jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_path_stats.java diff --git a/jetty-quic/quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicSession.java b/jetty-quic/quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicSession.java index 0cef4b6c6b7..aacabae5c45 100644 --- a/jetty-quic/quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicSession.java +++ b/jetty-quic/quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicSession.java @@ -306,7 +306,7 @@ public abstract class QuicSession extends ContainerLifeCycle int remaining = cipherBufferIn.remaining(); if (LOG.isDebugEnabled()) LOG.debug("feeding {} cipher bytes to {}", remaining, this); - int accepted = quicheConnection.feedCipherBytes(cipherBufferIn, remoteAddress); + int accepted = quicheConnection.feedCipherBytes(cipherBufferIn, getLocalAddress(), remoteAddress); if (accepted != remaining) throw new IllegalStateException(); diff --git a/jetty-quic/quic-quiche/quic-quiche-common/src/main/java/org/eclipse/jetty/quic/quiche/Quiche.java b/jetty-quic/quic-quiche/quic-quiche-common/src/main/java/org/eclipse/jetty/quic/quiche/Quiche.java index 233eaa65951..17866a6d1c6 100644 --- a/jetty-quic/quic-quiche/quic-quiche-common/src/main/java/org/eclipse/jetty/quic/quiche/Quiche.java +++ b/jetty-quic/quic-quiche/quic-quiche-common/src/main/java/org/eclipse/jetty/quic/quiche/Quiche.java @@ -25,7 +25,8 @@ public interface Quiche interface quiche_cc_algorithm { int QUICHE_CC_RENO = 0, - QUICHE_CC_CUBIC = 1; + QUICHE_CC_CUBIC = 1, + QUICHE_CC_BBR = 2; } interface quiche_error diff --git a/jetty-quic/quic-quiche/quic-quiche-common/src/main/java/org/eclipse/jetty/quic/quiche/QuicheConfig.java b/jetty-quic/quic-quiche/quic-quiche-common/src/main/java/org/eclipse/jetty/quic/quiche/QuicheConfig.java index 54105ff7c9c..c78d51cf89b 100644 --- a/jetty-quic/quic-quiche/quic-quiche-common/src/main/java/org/eclipse/jetty/quic/quiche/QuicheConfig.java +++ b/jetty-quic/quic-quiche/quic-quiche-common/src/main/java/org/eclipse/jetty/quic/quiche/QuicheConfig.java @@ -18,7 +18,8 @@ public class QuicheConfig public enum CongestionControl { RENO(Quiche.quiche_cc_algorithm.QUICHE_CC_RENO), - CUBIC(Quiche.quiche_cc_algorithm.QUICHE_CC_CUBIC); + CUBIC(Quiche.quiche_cc_algorithm.QUICHE_CC_CUBIC), + BBR(Quiche.quiche_cc_algorithm.QUICHE_CC_BBR); private final int value; CongestionControl(int value) @@ -46,6 +47,9 @@ public class QuicheConfig private Long initialMaxStreamsBidi; private Long initialMaxStreamsUni; private Boolean disableActiveMigration; + private Long maxConnectionWindow; + private Long maxStreamWindow; + private Long activeConnectionIdLimit; public QuicheConfig() { @@ -121,6 +125,21 @@ public class QuicheConfig return disableActiveMigration; } + public Long getMaxConnectionWindow() + { + return maxConnectionWindow; + } + + public Long getMaxStreamWindow() + { + return maxStreamWindow; + } + + public Long getActiveConnectionIdLimit() + { + return activeConnectionIdLimit; + } + public void setVersion(int version) { this.version = version; @@ -191,4 +210,18 @@ public class QuicheConfig this.disableActiveMigration = disable; } + public void setMaxConnectionWindow(Long sizeInBytes) + { + this.maxConnectionWindow = sizeInBytes; + } + + public void setMaxStreamWindow(Long maxStreamWindow) + { + this.maxStreamWindow = maxStreamWindow; + } + + public void setActiveConnectionIdLimit(Long activeConnectionIdLimit) + { + this.activeConnectionIdLimit = activeConnectionIdLimit; + } } diff --git a/jetty-quic/quic-quiche/quic-quiche-common/src/main/java/org/eclipse/jetty/quic/quiche/QuicheConnection.java b/jetty-quic/quic-quiche/quic-quiche-common/src/main/java/org/eclipse/jetty/quic/quiche/QuicheConnection.java index 6d7726d3c9c..0f3323a1f04 100644 --- a/jetty-quic/quic-quiche/quic-quiche-common/src/main/java/org/eclipse/jetty/quic/quiche/QuicheConnection.java +++ b/jetty-quic/quic-quiche/quic-quiche-common/src/main/java/org/eclipse/jetty/quic/quiche/QuicheConnection.java @@ -93,11 +93,12 @@ public abstract class QuicheConnection /** * Read the buffer of cipher text coming from the network. * @param buffer the buffer to read. + * @param local the local address on which the buffer was received. * @param peer the address of the peer from which the buffer was received. * @return how many bytes were consumed. * @throws IOException */ - public abstract int feedCipherBytes(ByteBuffer buffer, SocketAddress peer) throws IOException; + public abstract int feedCipherBytes(ByteBuffer buffer, SocketAddress local, SocketAddress peer) throws IOException; /** * Fill the given buffer with cipher text to be sent. diff --git a/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/ForeignIncubatorQuicheConnection.java b/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/ForeignIncubatorQuicheConnection.java index eedd4314ab8..19aec4b5a50 100644 --- a/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/ForeignIncubatorQuicheConnection.java +++ b/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/ForeignIncubatorQuicheConnection.java @@ -53,6 +53,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection private MemorySegment sendInfo; private MemorySegment recvInfo; private MemorySegment stats; + private MemorySegment pathStats; private ForeignIncubatorQuicheConnection(MemoryAddress quicheConn, MemoryAddress quicheConfig, ResourceScope scope) { @@ -62,6 +63,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection this.sendInfo = quiche_send_info.allocate(scope); this.recvInfo = quiche_recv_info.allocate(scope); this.stats = quiche_stats.allocate(scope); + this.pathStats = quiche_path_stats.allocate(scope); } public static byte[] fromPacket(ByteBuffer packet) @@ -124,12 +126,12 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection } } - public static ForeignIncubatorQuicheConnection connect(QuicheConfig quicheConfig, InetSocketAddress peer) throws IOException + public static ForeignIncubatorQuicheConnection connect(QuicheConfig quicheConfig, InetSocketAddress local, InetSocketAddress peer) throws IOException { - return connect(quicheConfig, peer, QUICHE_MAX_CONN_ID_LEN); + return connect(quicheConfig, local, peer, QUICHE_MAX_CONN_ID_LEN); } - public static ForeignIncubatorQuicheConnection connect(QuicheConfig quicheConfig, InetSocketAddress peer, int connectionIdLength) throws IOException + public static ForeignIncubatorQuicheConnection connect(QuicheConfig quicheConfig, InetSocketAddress local, InetSocketAddress peer, int connectionIdLength) throws IOException { if (connectionIdLength > QUICHE_MAX_CONN_ID_LEN) throw new IOException("Connection ID length is too large: " + connectionIdLength + " > " + QUICHE_MAX_CONN_ID_LEN); @@ -144,8 +146,9 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection scid.asByteBuffer().put(scidBytes); MemoryAddress libQuicheConfig = buildConfig(quicheConfig, scope); - MemorySegment s = sockaddr.convert(peer, scope); - MemoryAddress quicheConn = quiche_h.quiche_connect(CLinker.toCString(peer.getHostName(), scope), scid, scid.byteSize(), s, s.byteSize(), libQuicheConfig); + MemorySegment localSockaddr = sockaddr.convert(local, scope); + MemorySegment peerSockaddr = sockaddr.convert(peer, scope); + MemoryAddress quicheConn = quiche_h.quiche_connect(CLinker.toCString(peer.getHostName(), scope), scid, scid.byteSize(), localSockaddr, localSockaddr.byteSize(), peerSockaddr, peerSockaddr.byteSize(), libQuicheConfig); ForeignIncubatorQuicheConnection connection = new ForeignIncubatorQuicheConnection(quicheConn, libQuicheConfig, scope); keepScope = true; return connection; @@ -227,6 +230,18 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection if (disableActiveMigration != null) quiche_h.quiche_config_set_disable_active_migration(quicheConfig, disableActiveMigration ? C_TRUE : C_FALSE); + Long maxConnectionWindow = config.getMaxConnectionWindow(); + if (maxConnectionWindow != null) + quiche_h.quiche_config_set_max_connection_window(quicheConfig, maxConnectionWindow); + + Long maxStreamWindow = config.getMaxStreamWindow(); + if (maxStreamWindow != null) + quiche_h.quiche_config_set_max_stream_window(quicheConfig, maxStreamWindow); + + Long activeConnectionIdLimit = config.getActiveConnectionIdLimit(); + if (activeConnectionIdLimit != null) + quiche_h.quiche_config_set_active_connection_id_limit(quicheConfig, activeConnectionIdLimit); + return quicheConfig; } @@ -354,7 +369,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection } } - public static ForeignIncubatorQuicheConnection tryAccept(QuicheConfig quicheConfig, TokenValidator tokenValidator, ByteBuffer packetRead, SocketAddress peer) throws IOException + public static ForeignIncubatorQuicheConnection tryAccept(QuicheConfig quicheConfig, TokenValidator tokenValidator, ByteBuffer packetRead, SocketAddress local, SocketAddress peer) throws IOException { boolean keepScope = false; ResourceScope scope = ResourceScope.newSharedScope(); @@ -442,8 +457,9 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection LOG.debug("connection creation..."); MemoryAddress libQuicheConfig = buildConfig(quicheConfig, scope); - MemorySegment s = sockaddr.convert(peer, scope); - MemoryAddress quicheConn = quiche_h.quiche_accept(dcid.address(), getLong(dcid_len), odcid.address(), odcid.byteSize(), s.address(), s.byteSize(), libQuicheConfig); + MemorySegment localSockaddr = sockaddr.convert(local, scope); + MemorySegment peerSockaddr = sockaddr.convert(peer, scope); + MemoryAddress quicheConn = quiche_h.quiche_accept(dcid.address(), getLong(dcid_len), odcid.address(), odcid.byteSize(), localSockaddr.address(), localSockaddr.byteSize(), peerSockaddr.address(), peerSockaddr.byteSize(), libQuicheConfig); if (quicheConn == null) { quiche_h.quiche_config_free(libQuicheConfig); @@ -455,7 +471,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection LOG.debug("accepted, immediately receiving the same packet - remaining in buffer: {}", packetRead.remaining()); while (packetRead.hasRemaining()) { - quicheConnection.feedCipherBytes(packetRead, peer); + quicheConnection.feedCipherBytes(packetRead, local, peer); } keepScope = true; return quicheConnection; @@ -498,7 +514,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection } @Override - public int feedCipherBytes(ByteBuffer buffer, SocketAddress peer) throws IOException + public int feedCipherBytes(ByteBuffer buffer, SocketAddress local, SocketAddress peer) throws IOException { try (AutoLock ignore = lock.lock()) { @@ -508,7 +524,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection long received; try (ResourceScope scope = ResourceScope.newConfinedScope()) { - quiche_recv_info.setSocketAddress(recvInfo, peer, scope); + quiche_recv_info.setSocketAddress(recvInfo, local, peer, scope); if (buffer.isDirect()) { // If the ByteBuffer is direct, it can be used without any copy. @@ -696,6 +712,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection sendInfo = null; recvInfo = null; stats = null; + pathStats = null; } } @@ -729,8 +746,8 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection { if (quicheConn == null) throw new IllegalStateException("connection was released"); - quiche_h.quiche_conn_stats(quicheConn, stats.address()); - return quiche_stats.get_cwnd(stats); + quiche_h.quiche_conn_path_stats(quicheConn, pathStats.address()); + return quiche_path_stats.get_cwnd(stats); } } diff --git a/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_h.java b/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_h.java index af8584f1881..b4f244270e1 100644 --- a/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_h.java +++ b/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_h.java @@ -33,7 +33,7 @@ public class quiche_h { // This interface is a translation of the quiche.h header of a specific version. // It needs to be reviewed each time the native lib version changes. - private static final String EXPECTED_QUICHE_VERSION = "0.12.0"; + private static final String EXPECTED_QUICHE_VERSION = "0.15.0"; public static final byte C_FALSE = 0; public static final byte C_TRUE = 1; @@ -142,6 +142,24 @@ public class quiche_h FunctionDescriptor.ofVoid(C_POINTER, C_CHAR) ); + private static final MethodHandle quiche_config_set_max_connection_window$MH = downcallHandle( + "quiche_config_set_max_connection_window", + "(Ljdk/incubator/foreign/MemoryAddress;J)V", + FunctionDescriptor.ofVoid(C_POINTER, C_LONG) + ); + + private static final MethodHandle quiche_config_set_max_stream_window$MH = downcallHandle( + "quiche_config_set_max_stream_window", + "(Ljdk/incubator/foreign/MemoryAddress;J)V", + FunctionDescriptor.ofVoid(C_POINTER, C_LONG) + ); + + private static final MethodHandle quiche_config_set_active_connection_id_limit$MH = downcallHandle( + "quiche_config_set_active_connection_id_limit", + "(Ljdk/incubator/foreign/MemoryAddress;J)V", + FunctionDescriptor.ofVoid(C_POINTER, C_LONG) + ); + private static final MethodHandle quiche_config_free$MH = downcallHandle( "quiche_config_free", "(Ljdk/incubator/foreign/MemoryAddress;)V", @@ -150,8 +168,8 @@ public class quiche_h private static final MethodHandle quiche_connect$MH = downcallHandle( "quiche_connect", - "(Ljdk/incubator/foreign/MemoryAddress;Ljdk/incubator/foreign/MemoryAddress;JLjdk/incubator/foreign/MemoryAddress;JLjdk/incubator/foreign/MemoryAddress;)Ljdk/incubator/foreign/MemoryAddress;", - FunctionDescriptor.of(C_POINTER, C_POINTER, C_POINTER, C_LONG, C_POINTER, C_LONG, C_POINTER) + "(Ljdk/incubator/foreign/MemoryAddress;Ljdk/incubator/foreign/MemoryAddress;JLjdk/incubator/foreign/MemoryAddress;JLjdk/incubator/foreign/MemoryAddress;JLjdk/incubator/foreign/MemoryAddress;)Ljdk/incubator/foreign/MemoryAddress;", + FunctionDescriptor.of(C_POINTER, C_POINTER, C_POINTER, C_LONG, C_POINTER, C_LONG, C_POINTER, C_LONG, C_POINTER) ); private static final MethodHandle quiche_conn_send$MH = downcallHandle( @@ -174,8 +192,8 @@ public class quiche_h private static final MethodHandle quiche_accept$MH = downcallHandle( "quiche_accept", - "(Ljdk/incubator/foreign/MemoryAddress;JLjdk/incubator/foreign/MemoryAddress;JLjdk/incubator/foreign/MemoryAddress;JLjdk/incubator/foreign/MemoryAddress;)Ljdk/incubator/foreign/MemoryAddress;", - FunctionDescriptor.of(C_POINTER, C_POINTER, C_LONG, C_POINTER, C_LONG, C_POINTER, C_LONG, C_POINTER) + "(Ljdk/incubator/foreign/MemoryAddress;JLjdk/incubator/foreign/MemoryAddress;JLjdk/incubator/foreign/MemoryAddress;JLjdk/incubator/foreign/MemoryAddress;JLjdk/incubator/foreign/MemoryAddress;)Ljdk/incubator/foreign/MemoryAddress;", + FunctionDescriptor.of(C_POINTER, C_POINTER, C_LONG, C_POINTER, C_LONG, C_POINTER, C_LONG, C_POINTER, C_LONG, C_POINTER) ); private static final MethodHandle quiche_negotiate_version$MH = downcallHandle( @@ -244,6 +262,12 @@ public class quiche_h FunctionDescriptor.ofVoid(C_POINTER, C_POINTER) ); + private static final MethodHandle quiche_conn_path_stats$MH = downcallHandle( + "quiche_conn_path_stats", + "(Ljdk/incubator/foreign/MemoryAddress;JLjdk/incubator/foreign/MemoryAddress;)I", + FunctionDescriptor.of(C_INT, C_POINTER, C_LONG, C_POINTER) + ); + private static final MethodHandle quiche_conn_stream_finished$MH = downcallHandle( "quiche_conn_stream_finished", "(Ljdk/incubator/foreign/MemoryAddress;J)B", @@ -412,6 +436,42 @@ public class quiche_h } } + public static void quiche_config_set_max_connection_window(MemoryAddress config, long v) + { + try + { + quiche_config_set_max_connection_window$MH.invokeExact(config, v); + } + catch (Throwable ex) + { + throw new AssertionError("should not reach here", ex); + } + } + + public static void quiche_config_set_max_stream_window(MemoryAddress config, long v) + { + try + { + quiche_config_set_max_stream_window$MH.invokeExact(config, v); + } + catch (Throwable ex) + { + throw new AssertionError("should not reach here", ex); + } + } + + public static void quiche_config_set_active_connection_id_limit(MemoryAddress config, long v) + { + try + { + quiche_config_set_active_connection_id_limit$MH.invokeExact(config, v); + } + catch (Throwable ex) + { + throw new AssertionError("should not reach here", ex); + } + } + public static void quiche_config_set_initial_max_data(MemoryAddress config, long v) { try @@ -544,11 +604,11 @@ public class quiche_h } } - public static MemoryAddress quiche_connect(Addressable server_name, Addressable scid, long scid_len, Addressable to, long to_len, Addressable config) + public static MemoryAddress quiche_connect(Addressable server_name, Addressable scid, long scid_len, Addressable local, long local_len, Addressable peer, long peer_len, Addressable config) { try { - return (MemoryAddress) quiche_connect$MH.invokeExact(server_name.address(), scid.address(), scid_len, to.address(), to_len, config.address()); + return (MemoryAddress) quiche_connect$MH.invokeExact(server_name.address(), scid.address(), scid_len, local.address(), local_len, peer.address(), peer_len, config.address()); } catch (Throwable ex) { @@ -700,6 +760,18 @@ public class quiche_h } } + public static int quiche_conn_path_stats(MemoryAddress conn, MemoryAddress stats) + { + try + { + return (int)quiche_conn_path_stats$MH.invokeExact(conn, stats); + } + catch (Throwable ex) + { + throw new AssertionError("should not reach here", ex); + } + } + public static void quiche_conn_on_timeout(MemoryAddress conn) { try @@ -822,12 +894,13 @@ public class quiche_h public static MemoryAddress quiche_accept(MemoryAddress scid, long scid_len, MemoryAddress odcid, long odcid_len, - MemoryAddress from, long from_len, + MemoryAddress local, long local_len, + MemoryAddress peer, long peer_len, MemoryAddress config) { try { - return (MemoryAddress)quiche_accept$MH.invokeExact(scid, scid_len, odcid, odcid_len, from, from_len, config); + return (MemoryAddress)quiche_accept$MH.invokeExact(scid, scid_len, odcid, odcid_len, local, local_len, peer, peer_len, config); } catch (Throwable ex) { diff --git a/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_path_stats.java b/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_path_stats.java new file mode 100644 index 00000000000..338fbd19c60 --- /dev/null +++ b/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_path_stats.java @@ -0,0 +1,72 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 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.quic.quiche.foreign.incubator; + +import java.lang.invoke.VarHandle; + +import jdk.incubator.foreign.MemoryLayout; +import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.ResourceScope; + +import static jdk.incubator.foreign.CLinker.C_CHAR; +import static jdk.incubator.foreign.CLinker.C_INT; +import static jdk.incubator.foreign.CLinker.C_LONG; +import static jdk.incubator.foreign.CLinker.C_SHORT; + +public class quiche_path_stats +{ + private static final MemoryLayout LAYOUT = MemoryLayout.structLayout( + MemoryLayout.structLayout( // struct sockaddr_storage + C_SHORT.withName("ss_family"), + MemoryLayout.sequenceLayout(118, C_CHAR).withName("__ss_padding"), + C_LONG.withName("__ss_align") + ).withName("local_addr"), + C_INT.withName("local_addr_len"), + MemoryLayout.paddingLayout(32), + MemoryLayout.structLayout( // struct sockaddr_storage + C_SHORT.withName("ss_family"), + MemoryLayout.sequenceLayout(118, C_CHAR).withName("__ss_padding"), + C_LONG.withName("__ss_align") + ).withName("peer_addr"), + C_INT.withName("peer_addr_len"), + MemoryLayout.paddingLayout(32), + C_LONG.withName("validation_state"), + C_CHAR.withName("active"), + MemoryLayout.paddingLayout(56), + C_LONG.withName("recv"), + C_LONG.withName("sent"), + C_LONG.withName("lost"), + C_LONG.withName("retrans"), + C_LONG.withName("rtt"), + C_LONG.withName("cwnd"), + C_LONG.withName("sent_bytes"), + C_LONG.withName("recv_bytes"), + C_LONG.withName("lost_bytes"), + C_LONG.withName("stream_retrans_bytes"), + C_LONG.withName("pmtu"), + C_LONG.withName("delivery_rate") + ); + + private static final VarHandle cwnd = LAYOUT.varHandle(long.class, MemoryLayout.PathElement.groupElement("cwnd")); + + public static long get_cwnd(MemorySegment stats) + { + return (long)cwnd.get(stats); + } + + public static MemorySegment allocate(ResourceScope scope) + { + return MemorySegment.allocateNative(LAYOUT, scope); + } +} diff --git a/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_recv_info.java b/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_recv_info.java index 2c7c9833b82..ff18f0472c0 100644 --- a/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_recv_info.java +++ b/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_recv_info.java @@ -29,21 +29,29 @@ public class quiche_recv_info private static final MemoryLayout LAYOUT = MemoryLayout.structLayout( C_POINTER.withName("from"), C_INT.withName("from_len"), + MemoryLayout.paddingLayout(32), + C_POINTER.withName("to"), + C_INT.withName("to_len"), MemoryLayout.paddingLayout(32) ); private static final VarHandle from = MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, MemoryLayout.PathElement.groupElement("from"))); private static final VarHandle from_len = LAYOUT.varHandle(int.class, MemoryLayout.PathElement.groupElement("from_len")); + private static final VarHandle to = MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, MemoryLayout.PathElement.groupElement("to"))); + private static final VarHandle to_len = LAYOUT.varHandle(int.class, MemoryLayout.PathElement.groupElement("to_len")); public static MemorySegment allocate(ResourceScope scope) { return MemorySegment.allocateNative(LAYOUT, scope); } - public static void setSocketAddress(MemorySegment recvInfo, SocketAddress peer, ResourceScope scope) + public static void setSocketAddress(MemorySegment recvInfo, SocketAddress local, SocketAddress peer, ResourceScope scope) { - MemorySegment sockAddrSegment = sockaddr.convert(peer, scope); - from.set(recvInfo, sockAddrSegment.address()); - from_len.set(recvInfo, (int)sockAddrSegment.byteSize()); + MemorySegment peerSockAddrSegment = sockaddr.convert(peer, scope); + from.set(recvInfo, peerSockAddrSegment.address()); + from_len.set(recvInfo, (int)peerSockAddrSegment.byteSize()); + MemorySegment localSockAddrSegment = sockaddr.convert(local, scope); + to.set(recvInfo, localSockAddrSegment.address()); + to_len.set(recvInfo, (int)localSockAddrSegment.byteSize()); } } diff --git a/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_send_info.java b/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_send_info.java index b3f6a1b4c86..e5d8109b3d3 100644 --- a/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_send_info.java +++ b/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_send_info.java @@ -24,8 +24,15 @@ import static jdk.incubator.foreign.CLinker.C_SHORT; public class quiche_send_info { - private static final MemoryLayout LAYOUT = MemoryLayout.structLayout( // struct sockaddr_storage - MemoryLayout.structLayout( + private static final MemoryLayout LAYOUT = MemoryLayout.structLayout( + MemoryLayout.structLayout( // struct sockaddr_storage + C_SHORT.withName("ss_family"), + MemoryLayout.sequenceLayout(118, C_CHAR).withName("__ss_padding"), + C_LONG.withName("__ss_align") + ).withName("from"), + C_INT.withName("from_len"), + MemoryLayout.paddingLayout(32), + MemoryLayout.structLayout( // struct sockaddr_storage C_SHORT.withName("ss_family"), MemoryLayout.sequenceLayout(118, C_CHAR).withName("__ss_padding"), C_LONG.withName("__ss_align") diff --git a/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_stats.java b/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_stats.java index ba8c9141369..9c11a70e8d7 100644 --- a/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_stats.java +++ b/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/main/java/org/eclipse/jetty/quic/quiche/foreign/incubator/quiche_stats.java @@ -29,14 +29,11 @@ public class quiche_stats C_LONG.withName("sent"), C_LONG.withName("lost"), C_LONG.withName("retrans"), - C_LONG.withName("rtt"), - C_LONG.withName("cwnd"), C_LONG.withName("sent_bytes"), C_LONG.withName("recv_bytes"), C_LONG.withName("lost_bytes"), C_LONG.withName("stream_retrans_bytes"), - C_LONG.withName("pmtu"), - C_LONG.withName("delivery_rate"), + C_LONG.withName("paths_count"), C_LONG.withName("peer_max_idle_timeout"), C_LONG.withName("peer_max_udp_payload_size"), C_LONG.withName("peer_initial_max_data"), @@ -58,14 +55,8 @@ public class quiche_stats return MemorySegment.allocateNative(LAYOUT, scope); } - private static final VarHandle cwnd = LAYOUT.varHandle(long.class, MemoryLayout.PathElement.groupElement("cwnd")); private static final VarHandle peer_initial_max_streams_bidi = LAYOUT.varHandle(long.class, MemoryLayout.PathElement.groupElement("peer_initial_max_streams_bidi")); - public static long get_cwnd(MemorySegment stats) - { - return (long)cwnd.get(stats); - } - public static long get_peer_initial_max_streams_bidi(MemorySegment stats) { return (long)peer_initial_max_streams_bidi.get(stats); diff --git a/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/test/java/org/eclipse/jetty/quic/quiche/foreign/incubator/LowLevelQuicheTest.java b/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/test/java/org/eclipse/jetty/quic/quiche/foreign/incubator/LowLevelQuicheTest.java index f578bf0fe01..f88b08c126d 100644 --- a/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/test/java/org/eclipse/jetty/quic/quiche/foreign/incubator/LowLevelQuicheTest.java +++ b/jetty-quic/quic-quiche/quic-quiche-foreign-incubator/src/test/java/org/eclipse/jetty/quic/quiche/foreign/incubator/LowLevelQuicheTest.java @@ -193,7 +193,7 @@ public class LowLevelQuicheTest int drained = serverQuicheConnection.drainCipherBytes(buffer); assertThat(drained, is(expectedSize)); buffer.flip(); - int fed = clientQuicheConnection.feedCipherBytes(buffer, serverSocketAddress); + int fed = clientQuicheConnection.feedCipherBytes(buffer, clientSocketAddress, serverSocketAddress); assertThat(fed, is(expectedSize)); } @@ -206,7 +206,7 @@ public class LowLevelQuicheTest int drained = clientQuicheConnection.drainCipherBytes(buffer); assertThat(drained, is(expectedSize)); buffer.flip(); - int fed = serverQuicheConnection.feedCipherBytes(buffer, clientSocketAddress); + int fed = serverQuicheConnection.feedCipherBytes(buffer, serverSocketAddress, clientSocketAddress); assertThat(fed, is(expectedSize)); } @@ -215,20 +215,20 @@ public class LowLevelQuicheTest ByteBuffer buffer = ByteBuffer.allocate(QUICHE_MIN_CLIENT_INITIAL_LEN); ByteBuffer buffer2 = ByteBuffer.allocate(QUICHE_MIN_CLIENT_INITIAL_LEN); - ForeignIncubatorQuicheConnection clientQuicheConnection = ForeignIncubatorQuicheConnection.connect(clientQuicheConfig, serverSocketAddress); + ForeignIncubatorQuicheConnection clientQuicheConnection = ForeignIncubatorQuicheConnection.connect(clientQuicheConfig, clientSocketAddress, serverSocketAddress); connectionsToDisposeOf.add(clientQuicheConnection); int drained = clientQuicheConnection.drainCipherBytes(buffer); assertThat(drained, is(1200)); buffer.flip(); - ForeignIncubatorQuicheConnection serverQuicheConnection = ForeignIncubatorQuicheConnection.tryAccept(serverQuicheConfig, tokenValidator, buffer, clientSocketAddress); + ForeignIncubatorQuicheConnection serverQuicheConnection = ForeignIncubatorQuicheConnection.tryAccept(serverQuicheConfig, tokenValidator, buffer, serverSocketAddress, clientSocketAddress); assertThat(serverQuicheConnection, is(nullValue())); boolean negotiated = ForeignIncubatorQuicheConnection.negotiate(tokenMinter, buffer, buffer2); assertThat(negotiated, is(true)); buffer2.flip(); - int fed = clientQuicheConnection.feedCipherBytes(buffer2, serverSocketAddress); + int fed = clientQuicheConnection.feedCipherBytes(buffer2, clientSocketAddress, serverSocketAddress); assertThat(fed, is(79)); buffer.clear(); @@ -236,7 +236,7 @@ public class LowLevelQuicheTest assertThat(drained, is(1200)); buffer.flip(); - serverQuicheConnection = ForeignIncubatorQuicheConnection.tryAccept(serverQuicheConfig, tokenValidator, buffer, clientSocketAddress); + serverQuicheConnection = ForeignIncubatorQuicheConnection.tryAccept(serverQuicheConfig, tokenValidator, buffer, serverSocketAddress, clientSocketAddress); assertThat(serverQuicheConnection, is(not(nullValue()))); connectionsToDisposeOf.add(serverQuicheConnection); @@ -245,7 +245,7 @@ public class LowLevelQuicheTest assertThat(drained, is(1200)); buffer.flip(); - fed = clientQuicheConnection.feedCipherBytes(buffer, serverSocketAddress); + fed = clientQuicheConnection.feedCipherBytes(buffer, clientSocketAddress, serverSocketAddress); assertThat(fed, is(1200)); assertThat(serverQuicheConnection.isConnectionEstablished(), is(false)); diff --git a/jetty-quic/quic-quiche/quic-quiche-jna/src/main/java/org/eclipse/jetty/quic/quiche/jna/JnaQuicheConnection.java b/jetty-quic/quic-quiche/quic-quiche-jna/src/main/java/org/eclipse/jetty/quic/quiche/jna/JnaQuicheConnection.java index d2b068d33a3..8d70876e21f 100644 --- a/jetty-quic/quic-quiche/quic-quiche-jna/src/main/java/org/eclipse/jetty/quic/quiche/jna/JnaQuicheConnection.java +++ b/jetty-quic/quic-quiche/quic-quiche-jna/src/main/java/org/eclipse/jetty/quic/quiche/jna/JnaQuicheConnection.java @@ -99,12 +99,12 @@ public class JnaQuicheConnection extends QuicheConnection return sizedBuffer; } - public static JnaQuicheConnection connect(QuicheConfig quicheConfig, InetSocketAddress peer) throws IOException + public static JnaQuicheConnection connect(QuicheConfig quicheConfig, InetSocketAddress local, InetSocketAddress peer) throws IOException { - return connect(quicheConfig, peer, QUICHE_MAX_CONN_ID_LEN); + return connect(quicheConfig, local, peer, QUICHE_MAX_CONN_ID_LEN); } - public static JnaQuicheConnection connect(QuicheConfig quicheConfig, InetSocketAddress peer, int connectionIdLength) throws IOException + public static JnaQuicheConnection connect(QuicheConfig quicheConfig, InetSocketAddress local, InetSocketAddress peer, int connectionIdLength) throws IOException { if (connectionIdLength > QUICHE_MAX_CONN_ID_LEN) throw new IOException("Connection ID length is too large: " + connectionIdLength + " > " + QUICHE_MAX_CONN_ID_LEN); @@ -112,8 +112,9 @@ public class JnaQuicheConnection extends QuicheConnection SECURE_RANDOM.nextBytes(scid); LibQuiche.quiche_config libQuicheConfig = buildConfig(quicheConfig); - SizedStructure s = sockaddr.convert(peer); - LibQuiche.quiche_conn quicheConn = LibQuiche.INSTANCE.quiche_connect(peer.getHostName(), scid, new size_t(scid.length), s.getStructure(), s.getSize(), libQuicheConfig); + SizedStructure localSockaddr = sockaddr.convert(local); + SizedStructure peerSockaddr = sockaddr.convert(peer); + LibQuiche.quiche_conn quicheConn = LibQuiche.INSTANCE.quiche_connect(peer.getHostName(), scid, new size_t(scid.length), localSockaddr.getStructure(), localSockaddr.getSize(), peerSockaddr.getStructure(), peerSockaddr.getSize(), libQuicheConfig); return new JnaQuicheConnection(quicheConn, libQuicheConfig); } @@ -185,6 +186,18 @@ public class JnaQuicheConnection extends QuicheConnection if (disableActiveMigration != null) LibQuiche.INSTANCE.quiche_config_set_disable_active_migration(quicheConfig, disableActiveMigration); + Long maxConnectionWindow = config.getMaxConnectionWindow(); + if (maxConnectionWindow != null) + LibQuiche.INSTANCE.quiche_config_set_max_connection_window(quicheConfig, new uint64_t(maxConnectionWindow)); + + Long maxStreamWindow = config.getMaxStreamWindow(); + if (maxStreamWindow != null) + LibQuiche.INSTANCE.quiche_config_set_max_stream_window(quicheConfig, new uint64_t(maxStreamWindow)); + + Long activeConnectionIdLimit = config.getActiveConnectionIdLimit(); + if (activeConnectionIdLimit != null) + LibQuiche.INSTANCE.quiche_config_set_active_connection_id_limit(quicheConfig, new uint64_t(activeConnectionIdLimit)); + return quicheConfig; } @@ -297,7 +310,7 @@ public class JnaQuicheConnection extends QuicheConnection * Fully consumes the {@code packetRead} buffer if the connection was accepted. * @return an established connection if accept succeeded, null if accept failed and negotiation should be tried. */ - public static JnaQuicheConnection tryAccept(QuicheConfig quicheConfig, TokenValidator tokenValidator, ByteBuffer packetRead, SocketAddress peer) throws IOException + public static JnaQuicheConnection tryAccept(QuicheConfig quicheConfig, TokenValidator tokenValidator, ByteBuffer packetRead, SocketAddress local, SocketAddress peer) throws IOException { uint8_t_pointer type = new uint8_t_pointer(); uint32_t_pointer version = new uint32_t_pointer(); @@ -350,8 +363,9 @@ public class JnaQuicheConnection extends QuicheConnection LOG.debug("connection creation..."); LibQuiche.quiche_config libQuicheConfig = buildConfig(quicheConfig); - SizedStructure s = sockaddr.convert(peer); - LibQuiche.quiche_conn quicheConn = LibQuiche.INSTANCE.quiche_accept(dcid, dcid_len.getPointee(), odcid, new size_t(odcid.length), s.getStructure(), s.getSize(), libQuicheConfig); + SizedStructure localSockaddr = sockaddr.convert(local); + SizedStructure peerSockaddr = sockaddr.convert(peer); + LibQuiche.quiche_conn quicheConn = LibQuiche.INSTANCE.quiche_accept(dcid, dcid_len.getPointee(), odcid, new size_t(odcid.length), localSockaddr.getStructure(), localSockaddr.getSize(), peerSockaddr.getStructure(), peerSockaddr.getSize(), libQuicheConfig); if (quicheConn == null) { @@ -364,7 +378,7 @@ public class JnaQuicheConnection extends QuicheConnection LOG.debug("accepted, immediately receiving the same packet - remaining in buffer: {}", packetRead.remaining()); while (packetRead.hasRemaining()) { - quicheConnection.feedCipherBytes(packetRead, peer); + quicheConnection.feedCipherBytes(packetRead, local, peer); } return quicheConnection; } @@ -400,15 +414,8 @@ public class JnaQuicheConnection extends QuicheConnection } } - /** - * Read the buffer of cipher text coming from the network. - * @param buffer the buffer to read. - * @param peer the address of the peer from which the buffer was received. - * @return how many bytes were consumed. - * @throws IOException - */ @Override - public int feedCipherBytes(ByteBuffer buffer, SocketAddress peer) throws IOException + public int feedCipherBytes(ByteBuffer buffer, SocketAddress local, SocketAddress peer) throws IOException { try (AutoLock ignore = lock.lock()) { @@ -416,9 +423,12 @@ public class JnaQuicheConnection extends QuicheConnection throw new IOException("Cannot receive when not connected"); LibQuiche.quiche_recv_info info = new LibQuiche.quiche_recv_info(); - SizedStructure s = sockaddr.convert(peer); - info.from = s.getStructure().byReference(); - info.from_len = s.getSize(); + SizedStructure localSockaddr = sockaddr.convert(local); + info.to = localSockaddr.getStructure().byReference(); + info.to_len = localSockaddr.getSize(); + SizedStructure peerSockaddr = sockaddr.convert(peer); + info.from = peerSockaddr.getStructure().byReference(); + info.from_len = peerSockaddr.getSize(); int received = LibQuiche.INSTANCE.quiche_conn_recv(quicheConn, buffer, new size_t(buffer.remaining()), info).intValue(); if (received < 0) throw new IOException("failed to receive packet; err=" + quiche_error.errToString(received)); @@ -442,6 +452,8 @@ public class JnaQuicheConnection extends QuicheConnection throw new IOException("Cannot send when not connected"); LibQuiche.quiche_send_info quiche_send_info = new LibQuiche.quiche_send_info(); + quiche_send_info.from = new sockaddr_storage(); + quiche_send_info.from_len = new size_t(quiche_send_info.to.size()); quiche_send_info.to = new sockaddr_storage(); quiche_send_info.to_len = new size_t(quiche_send_info.to.size()); int written = LibQuiche.INSTANCE.quiche_conn_send(quicheConn, buffer, new size_t(buffer.remaining()), quiche_send_info).intValue(); @@ -591,8 +603,8 @@ public class JnaQuicheConnection extends QuicheConnection { if (quicheConn == null) throw new IllegalStateException("connection was released"); - LibQuiche.quiche_stats stats = new LibQuiche.quiche_stats(); - LibQuiche.INSTANCE.quiche_conn_stats(quicheConn, stats); + LibQuiche.quiche_path_stats stats = new LibQuiche.quiche_path_stats(); + LibQuiche.INSTANCE.quiche_conn_path_stats(quicheConn, new size_t(0L), stats); return stats.cwnd.longValue(); } } diff --git a/jetty-quic/quic-quiche/quic-quiche-jna/src/main/java/org/eclipse/jetty/quic/quiche/jna/LibQuiche.java b/jetty-quic/quic-quiche/quic-quiche-jna/src/main/java/org/eclipse/jetty/quic/quiche/jna/LibQuiche.java index 2ccb3295a96..04e91651d02 100644 --- a/jetty-quic/quic-quiche/quic-quiche-jna/src/main/java/org/eclipse/jetty/quic/quiche/jna/LibQuiche.java +++ b/jetty-quic/quic-quiche/quic-quiche-jna/src/main/java/org/eclipse/jetty/quic/quiche/jna/LibQuiche.java @@ -31,7 +31,7 @@ public interface LibQuiche extends Library { // This interface is a translation of the quiche.h header of a specific version. // It needs to be reviewed each time the native lib version changes. - String EXPECTED_QUICHE_VERSION = "0.12.0"; + String EXPECTED_QUICHE_VERSION = "0.15.0"; // The charset used to convert java.lang.String to char * and vice versa. Charset CHARSET = StandardCharsets.UTF_8; @@ -122,6 +122,18 @@ public interface LibQuiche extends Library // Sets the `disable_active_migration` transport parameter. void quiche_config_set_disable_active_migration(quiche_config config, boolean v); + // Sets the maximum connection window. + void quiche_config_set_max_connection_window(quiche_config config, uint64_t v); + + // Sets the maximum stream window. + void quiche_config_set_max_stream_window(quiche_config config, uint64_t v); + + // Sets the limit of active connection IDs. + void quiche_config_set_active_connection_id_limit(quiche_config config, uint64_t v); + + // Sets the initial stateless reset token. |v| must contain 16 bytes, otherwise the behaviour is undefined. + void quiche_config_set_stateless_reset_token(quiche_config config, byte[] v); + // Frees the config object. void quiche_config_free(quiche_config config); @@ -133,8 +145,8 @@ public interface LibQuiche extends Library } @Structure.FieldOrder({ - "recv", "sent", "lost", "retrans", "rtt", "cwnd", "sent_bytes", "recv_bytes", "lost_bytes", - "stream_retrans_bytes", "pmtu", "delivery_rate", "peer_max_idle_timeout", + "recv", "sent", "lost", "retrans", "sent_bytes", "recv_bytes", "lost_bytes", + "stream_retrans_bytes", "paths_count", "peer_max_idle_timeout", "peer_max_udp_payload_size", "peer_initial_max_data", "peer_initial_max_stream_data_bidi_local", "peer_initial_max_stream_data_bidi_remote", "peer_initial_max_stream_data_uni", "peer_initial_max_streams_bidi", "peer_initial_max_streams_uni", "peer_ack_delay_exponent", @@ -155,16 +167,10 @@ public interface LibQuiche extends Library // The number of sent QUIC packets with retranmitted data. public size_t retrans; - // The estimated round-trip time of the connection (in nanoseconds). - public uint64_t rtt; - - // The size of the connection's congestion window in bytes. - public size_t cwnd; - // The number of sent bytes. public uint64_t sent_bytes; - // The number of recevied bytes. + // The number of received bytes. public uint64_t recv_bytes; // The number of bytes lost. @@ -173,11 +179,8 @@ public interface LibQuiche extends Library // The number of stream bytes retransmitted. public uint64_t stream_retrans_bytes; - // The current PMTU for the connection. - public size_t pmtu; - - // The most recent data delivery rate estimate in bytes/s. - public uint64_t delivery_rate; + // The number of known paths for the connection. + public size_t paths_count; // The maximum idle timeout. public uint64_t peer_max_idle_timeout; @@ -219,6 +222,65 @@ public interface LibQuiche extends Library public ssize_t peer_max_datagram_frame_size; } + @Structure.FieldOrder({ + "local_addr", "local_addr_len", "peer_addr", "peer_addr_len", + "validation_state", "active", "recv", "sent", "lost", "retrans", + "rtt", "cwnd", "sent_bytes", "recv_bytes", "lost_bytes", + "stream_retrans_bytes", "pmtu", "delivery_rate" + }) + class quiche_path_stats extends Structure + { + // The local address used by this path. + sockaddr_storage local_addr; + size_t local_addr_len; + + // The peer address seen by this path. + sockaddr_storage peer_addr; + size_t peer_addr_len; + + // The validation state of the path. + ssize_t validation_state; + + // Whether this path is active. + boolean active; + + // The number of QUIC packets received on this path. + size_t recv; + + // The number of QUIC packets sent on this path. + size_t sent; + + // The number of QUIC packets that were lost on this path. + size_t lost; + + // The number of sent QUIC packets with retransmitted data on this path. + size_t retrans; + + // The estimated round-trip time of the path (in nanoseconds). + uint64_t rtt; + + // The size of the path's congestion window in bytes. + size_t cwnd; + + // The number of sent bytes on this path. + uint64_t sent_bytes; + + // The number of received bytes on this path. + uint64_t recv_bytes; + + // The number of bytes lost on this path. + uint64_t lost_bytes; + + // The number of stream bytes retransmitted on this path. + uint64_t stream_retrans_bytes; + + // The current PMTU for the path. + size_t pmtu; + + // The most recent data delivery rate estimate in bytes/s. + uint64_t delivery_rate; + } + interface LoggingCallback extends Callback { void log(String msg, Pointer argp); @@ -228,7 +290,10 @@ public interface LibQuiche extends Library int quiche_enable_debug_logging(LoggingCallback cb, Pointer argp); // Creates a new client-side connection. - quiche_conn quiche_connect(String server_name, byte[] scid, size_t scid_len, sockaddr to, size_t to_len, quiche_config config); + quiche_conn quiche_connect(String server_name, byte[] scid, size_t scid_len, + sockaddr local, size_t local_len, + sockaddr peer, size_t peer_len, + quiche_config config); interface packet_type { @@ -285,7 +350,10 @@ public interface LibQuiche extends Library uint32_t version, ByteBuffer out, size_t out_len); // Creates a new server-side connection. - quiche_conn quiche_accept(byte[] scid, size_t scid_len, byte[] odcid, size_t odcid_len, sockaddr from, size_t from_len, quiche_config config); + quiche_conn quiche_accept(byte[] scid, size_t scid_len, byte[] odcid, size_t odcid_len, + sockaddr local, size_t local_len, + sockaddr peer, size_t peer_len, + quiche_config config); // Returns the amount of time until the next timeout event, in milliseconds. uint64_t quiche_conn_timeout_as_millis(quiche_conn conn); @@ -296,22 +364,43 @@ public interface LibQuiche extends Library // Collects and returns statistics about the connection. void quiche_conn_stats(quiche_conn conn, quiche_stats out); - @Structure.FieldOrder({"to", "to_len", "at"}) + // Collects and returns statistics about the specified path for the connection. + // + // The `idx` argument represent the path's index (also see the `paths_count` + // field of `quiche_stats`). + int quiche_conn_path_stats(quiche_conn conn, size_t idx, quiche_path_stats out); + + @Structure.FieldOrder({"from", "from_len", "to", "to_len", "at"}) class quiche_send_info extends Structure { + // The local address the packet should be sent from. + public sockaddr_storage from; + public size_t from_len; + + // The remote address the packet should be sent to. public sockaddr_storage to; public size_t to_len; + + // The time to send the packet out. public timespec at; } // Writes a single QUIC packet to be sent to the peer. ssize_t quiche_conn_send(quiche_conn conn, ByteBuffer out, size_t out_len, quiche_send_info out_info); - @Structure.FieldOrder({"from", "from_len"}) + // Returns the size of the send quantum, in bytes. + size_t quiche_conn_send_quantum(quiche_conn conn); + + @Structure.FieldOrder({"from", "from_len", "to", "to_len"}) class quiche_recv_info extends Structure { + // The remote address the packet was received from. public sockaddr.ByReference from; public size_t from_len; + + // The local address the packet was received on. + public sockaddr.ByReference to; + public size_t to_len; } // Processes QUIC packets received from the peer. diff --git a/jetty-quic/quic-quiche/quic-quiche-jna/src/test/java/org/eclipse/jetty/quic/quiche/jna/LowLevelQuicheTest.java b/jetty-quic/quic-quiche/quic-quiche-jna/src/test/java/org/eclipse/jetty/quic/quiche/jna/LowLevelQuicheTest.java index 1472eb0a9dd..68cd709ba98 100644 --- a/jetty-quic/quic-quiche/quic-quiche-jna/src/test/java/org/eclipse/jetty/quic/quiche/jna/LowLevelQuicheTest.java +++ b/jetty-quic/quic-quiche/quic-quiche-jna/src/test/java/org/eclipse/jetty/quic/quiche/jna/LowLevelQuicheTest.java @@ -192,7 +192,7 @@ public class LowLevelQuicheTest int drained = serverQuicheConnection.drainCipherBytes(buffer); assertThat(drained, is(expectedSize)); buffer.flip(); - int fed = clientQuicheConnection.feedCipherBytes(buffer, serverSocketAddress); + int fed = clientQuicheConnection.feedCipherBytes(buffer, clientSocketAddress, serverSocketAddress); assertThat(fed, is(expectedSize)); } @@ -205,7 +205,7 @@ public class LowLevelQuicheTest int drained = clientQuicheConnection.drainCipherBytes(buffer); assertThat(drained, is(expectedSize)); buffer.flip(); - int fed = serverQuicheConnection.feedCipherBytes(buffer, clientSocketAddress); + int fed = serverQuicheConnection.feedCipherBytes(buffer, serverSocketAddress, clientSocketAddress); assertThat(fed, is(expectedSize)); } @@ -214,20 +214,20 @@ public class LowLevelQuicheTest ByteBuffer buffer = ByteBuffer.allocate(QUICHE_MIN_CLIENT_INITIAL_LEN); ByteBuffer buffer2 = ByteBuffer.allocate(QUICHE_MIN_CLIENT_INITIAL_LEN); - JnaQuicheConnection clientQuicheConnection = JnaQuicheConnection.connect(clientQuicheConfig, serverSocketAddress); + JnaQuicheConnection clientQuicheConnection = JnaQuicheConnection.connect(clientQuicheConfig, clientSocketAddress, serverSocketAddress); connectionsToDisposeOf.add(clientQuicheConnection); int drained = clientQuicheConnection.drainCipherBytes(buffer); assertThat(drained, is(1200)); buffer.flip(); - JnaQuicheConnection serverQuicheConnection = JnaQuicheConnection.tryAccept(serverQuicheConfig, tokenValidator, buffer, clientSocketAddress); + JnaQuicheConnection serverQuicheConnection = JnaQuicheConnection.tryAccept(serverQuicheConfig, tokenValidator, buffer, serverSocketAddress, clientSocketAddress); assertThat(serverQuicheConnection, is(nullValue())); boolean negotiated = JnaQuicheConnection.negotiate(tokenMinter, buffer, buffer2); assertThat(negotiated, is(true)); buffer2.flip(); - int fed = clientQuicheConnection.feedCipherBytes(buffer2, serverSocketAddress); + int fed = clientQuicheConnection.feedCipherBytes(buffer2, clientSocketAddress, serverSocketAddress); assertThat(fed, is(79)); buffer.clear(); @@ -235,7 +235,7 @@ public class LowLevelQuicheTest assertThat(drained, is(1200)); buffer.flip(); - serverQuicheConnection = JnaQuicheConnection.tryAccept(serverQuicheConfig, tokenValidator, buffer, clientSocketAddress); + serverQuicheConnection = JnaQuicheConnection.tryAccept(serverQuicheConfig, tokenValidator, buffer, serverSocketAddress, clientSocketAddress); assertThat(serverQuicheConnection, is(not(nullValue()))); connectionsToDisposeOf.add(serverQuicheConnection); @@ -244,7 +244,7 @@ public class LowLevelQuicheTest assertThat(drained, is(1200)); buffer.flip(); - fed = clientQuicheConnection.feedCipherBytes(buffer, serverSocketAddress); + fed = clientQuicheConnection.feedCipherBytes(buffer, clientSocketAddress, serverSocketAddress); assertThat(fed, is(1200)); assertThat(serverQuicheConnection.isConnectionEstablished(), is(false)); diff --git a/pom.xml b/pom.xml index 0f55db41497..ada3b5d5229 100644 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ 3.5.0.Final 1.1 1.0.7 - 0.12.0 + 0.15.0 4.0.6 1.2 6.0