#8695: update quiche to version 0.15.0

Signed-off-by: Ludovic Orban <lorban@bitronix.be>
This commit is contained in:
Ludovic Orban 2022-10-07 17:54:40 +02:00
parent d9b6aa02f2
commit d6a101d463
15 changed files with 402 additions and 98 deletions

View File

@ -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();

View File

@ -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

View File

@ -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;
}
}

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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)
{

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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")

View File

@ -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);

View File

@ -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));

View File

@ -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<sockaddr> 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<sockaddr> localSockaddr = sockaddr.convert(local);
SizedStructure<sockaddr> 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<sockaddr> 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<sockaddr> localSockaddr = sockaddr.convert(local);
SizedStructure<sockaddr> 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<sockaddr> s = sockaddr.convert(peer);
info.from = s.getStructure().byReference();
info.from_len = s.getSize();
SizedStructure<sockaddr> localSockaddr = sockaddr.convert(local);
info.to = localSockaddr.getStructure().byReference();
info.to_len = localSockaddr.getSize();
SizedStructure<sockaddr> 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();
}
}

View File

@ -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.

View File

@ -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));

View File

@ -83,7 +83,7 @@
<jboss-threads.version>3.5.0.Final</jboss-threads.version>
<jetty-assembly-descriptors.version>1.1</jetty-assembly-descriptors.version>
<jetty.perf-helper.version>1.0.7</jetty.perf-helper.version>
<jetty-quiche-native.version>0.12.0</jetty-quiche-native.version>
<jetty-quiche-native.version>0.15.0</jetty-quiche-native.version>
<jetty.servlet.api.version>4.0.6</jetty.servlet.api.version>
<jetty-test-policy.version>1.2</jetty-test-policy.version>
<jetty.test.version>6.0</jetty.test.version>