Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-11.0.x

This commit is contained in:
Ludovic Orban 2022-10-18 09:18:05 +02:00
commit 38e7f1e742
23 changed files with 448 additions and 118 deletions

View File

@ -53,14 +53,35 @@
<profile> <profile>
<id>jdk17</id> <id>jdk17</id>
<activation> <activation>
<jdk>[17,)</jdk> <jdk>17</jdk>
</activation> </activation>
<!--
This profile makes sure the Foreign binding is used for tests when running exactly on JDK 17.
Older and newer JDKs will revert to the JNA binding.
-->
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.eclipse.jetty.quic</groupId> <groupId>org.eclipse.jetty.quic</groupId>
<artifactId>quic-quiche-foreign-incubator</artifactId> <artifactId>quic-quiche-foreign-incubator</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<!-- Make sure to use the Foreign binding by adding and opening the jdk.incubator.foreign module. -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
@{argLine}
${jetty.surefire.argLine}
--add-modules=jdk.incubator.foreign
--add-opens jdk.incubator.foreign/jdk.incubator.foreign=ALL-UNNAMED
--enable-native-access ALL-UNNAMED
</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile> </profile>
</profiles> </profiles>
</project> </project>

View File

@ -96,7 +96,7 @@ public class ClientQuicConnection extends QuicConnection
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("connecting to {} with protocols {}", remoteAddress, protocols); LOG.debug("connecting to {} with protocols {}", remoteAddress, protocols);
QuicheConnection quicheConnection = QuicheConnection.connect(quicheConfig, remoteAddress); QuicheConnection quicheConnection = QuicheConnection.connect(quicheConfig, getEndPoint().getLocalAddress(), remoteAddress);
ClientQuicSession session = new ClientQuicSession(getExecutor(), getScheduler(), getByteBufferPool(), quicheConnection, this, remoteAddress, context); ClientQuicSession session = new ClientQuicSession(getExecutor(), getScheduler(), getByteBufferPool(), quicheConnection, this, remoteAddress, context);
pendingSessions.put(remoteAddress, session); pendingSessions.put(remoteAddress, session);
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())

View File

@ -75,6 +75,11 @@ public class QuicClientConnectorConfigurator extends ClientConnector.Configurato
{ {
context.put(QuicConfiguration.CONTEXT_KEY, configuration); context.put(QuicConfiguration.CONTEXT_KEY, configuration);
DatagramChannel channel = DatagramChannel.open(); DatagramChannel channel = DatagramChannel.open();
if (clientConnector.getBindAddress() == null)
{
// QUIC must know the local address for connection migration, so we must always bind early.
channel.bind(null);
}
return new ChannelWithAddress(channel, address); return new ChannelWithAddress(channel, address);
} }

View File

@ -306,7 +306,7 @@ public abstract class QuicSession extends ContainerLifeCycle
int remaining = cipherBufferIn.remaining(); int remaining = cipherBufferIn.remaining();
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("feeding {} cipher bytes to {}", remaining, this); LOG.debug("feeding {} cipher bytes to {}", remaining, this);
int accepted = quicheConnection.feedCipherBytes(cipherBufferIn, remoteAddress); int accepted = quicheConnection.feedCipherBytes(cipherBufferIn, getLocalAddress(), remoteAddress);
if (accepted != remaining) if (accepted != remaining)
throw new IllegalStateException(); throw new IllegalStateException();

View File

@ -25,7 +25,8 @@ public interface Quiche
interface quiche_cc_algorithm interface quiche_cc_algorithm
{ {
int QUICHE_CC_RENO = 0, int QUICHE_CC_RENO = 0,
QUICHE_CC_CUBIC = 1; QUICHE_CC_CUBIC = 1,
QUICHE_CC_BBR = 2;
} }
interface quiche_error interface quiche_error

View File

@ -24,7 +24,7 @@ public interface QuicheBinding
int priority(); int priority();
byte[] fromPacket(ByteBuffer packet); byte[] fromPacket(ByteBuffer packet);
QuicheConnection connect(QuicheConfig quicheConfig, InetSocketAddress peer, int connectionIdLength) throws IOException; QuicheConnection connect(QuicheConfig quicheConfig, InetSocketAddress local, InetSocketAddress peer, int connectionIdLength) throws IOException;
boolean negotiate(QuicheConnection.TokenMinter tokenMinter, ByteBuffer packetRead, ByteBuffer packetToSend) throws IOException; boolean negotiate(QuicheConnection.TokenMinter tokenMinter, ByteBuffer packetRead, ByteBuffer packetToSend) throws IOException;
QuicheConnection tryAccept(QuicheConfig quicheConfig, QuicheConnection.TokenValidator tokenValidator, ByteBuffer packetRead, SocketAddress peer) throws IOException; QuicheConnection tryAccept(QuicheConfig quicheConfig, QuicheConnection.TokenValidator tokenValidator, ByteBuffer packetRead, SocketAddress local, SocketAddress peer) throws IOException;
} }

View File

@ -18,7 +18,8 @@ public class QuicheConfig
public enum CongestionControl public enum CongestionControl
{ {
RENO(Quiche.quiche_cc_algorithm.QUICHE_CC_RENO), 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; private final int value;
CongestionControl(int value) CongestionControl(int value)
@ -46,6 +47,9 @@ public class QuicheConfig
private Long initialMaxStreamsBidi; private Long initialMaxStreamsBidi;
private Long initialMaxStreamsUni; private Long initialMaxStreamsUni;
private Boolean disableActiveMigration; private Boolean disableActiveMigration;
private Long maxConnectionWindow;
private Long maxStreamWindow;
private Long activeConnectionIdLimit;
public QuicheConfig() public QuicheConfig()
{ {
@ -121,6 +125,21 @@ public class QuicheConfig
return disableActiveMigration; return disableActiveMigration;
} }
public Long getMaxConnectionWindow()
{
return maxConnectionWindow;
}
public Long getMaxStreamWindow()
{
return maxStreamWindow;
}
public Long getActiveConnectionIdLimit()
{
return activeConnectionIdLimit;
}
public void setVersion(int version) public void setVersion(int version)
{ {
this.version = version; this.version = version;
@ -191,4 +210,18 @@ public class QuicheConfig
this.disableActiveMigration = disable; 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

@ -49,14 +49,14 @@ public abstract class QuicheConnection
LOG.debug("using quiche binding implementation: {}", QUICHE_BINDING.getClass().getName()); LOG.debug("using quiche binding implementation: {}", QUICHE_BINDING.getClass().getName());
} }
public static QuicheConnection connect(QuicheConfig quicheConfig, InetSocketAddress peer) throws IOException public static QuicheConnection connect(QuicheConfig quicheConfig, InetSocketAddress local, InetSocketAddress peer) throws IOException
{ {
return connect(quicheConfig, peer, Quiche.QUICHE_MAX_CONN_ID_LEN); return connect(quicheConfig, local, peer, Quiche.QUICHE_MAX_CONN_ID_LEN);
} }
public static QuicheConnection connect(QuicheConfig quicheConfig, InetSocketAddress peer, int connectionIdLength) throws IOException public static QuicheConnection connect(QuicheConfig quicheConfig, InetSocketAddress local, InetSocketAddress peer, int connectionIdLength) throws IOException
{ {
return QUICHE_BINDING.connect(quicheConfig, peer, connectionIdLength); return QUICHE_BINDING.connect(quicheConfig, local, peer, connectionIdLength);
} }
/** /**
@ -73,9 +73,9 @@ public abstract class QuicheConnection
* Fully consumes the {@code packetRead} buffer if the connection was accepted. * 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. * @return an established connection if accept succeeded, null if accept failed and negotiation should be tried.
*/ */
public static QuicheConnection tryAccept(QuicheConfig quicheConfig, TokenValidator tokenValidator, ByteBuffer packetRead, SocketAddress peer) throws IOException public static QuicheConnection tryAccept(QuicheConfig quicheConfig, TokenValidator tokenValidator, ByteBuffer packetRead, SocketAddress local, SocketAddress peer) throws IOException
{ {
return QUICHE_BINDING.tryAccept(quicheConfig, tokenValidator, packetRead, peer); return QUICHE_BINDING.tryAccept(quicheConfig, tokenValidator, packetRead, local, peer);
} }
public final List<Long> readableStreamIds() public final List<Long> readableStreamIds()
@ -93,11 +93,12 @@ public abstract class QuicheConnection
/** /**
* Read the buffer of cipher text coming from the network. * Read the buffer of cipher text coming from the network.
* @param buffer the buffer to read. * @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. * @param peer the address of the peer from which the buffer was received.
* @return how many bytes were consumed. * @return how many bytes were consumed.
* @throws IOException * @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. * Fill the given buffer with cipher text to be sent.

View File

@ -77,7 +77,7 @@
changed both its API (18) and its name (19), so tests are disabled on JDKs over 17. changed both its API (18) and its name (19), so tests are disabled on JDKs over 17.
Eventually, new Foreign modules for JDKs 18 and 19 should be added. Eventually, new Foreign modules for JDKs 18 and 19 should be added.
--> -->
<id>jdk18+</id> <id>jdk18</id>
<activation> <activation>
<jdk>[18,)</jdk> <jdk>[18,)</jdk>
</activation> </activation>

View File

@ -57,9 +57,9 @@ public class ForeignIncubatorQuicheBinding implements QuicheBinding
} }
@Override @Override
public QuicheConnection connect(QuicheConfig quicheConfig, InetSocketAddress peer, int connectionIdLength) throws IOException public QuicheConnection connect(QuicheConfig quicheConfig, InetSocketAddress local, InetSocketAddress peer, int connectionIdLength) throws IOException
{ {
return ForeignIncubatorQuicheConnection.connect(quicheConfig, peer, connectionIdLength); return ForeignIncubatorQuicheConnection.connect(quicheConfig, local, peer, connectionIdLength);
} }
@Override @Override
@ -69,9 +69,9 @@ public class ForeignIncubatorQuicheBinding implements QuicheBinding
} }
@Override @Override
public QuicheConnection tryAccept(QuicheConfig quicheConfig, QuicheConnection.TokenValidator tokenValidator, ByteBuffer packetRead, SocketAddress peer) throws IOException public QuicheConnection tryAccept(QuicheConfig quicheConfig, QuicheConnection.TokenValidator tokenValidator, ByteBuffer packetRead, SocketAddress local, SocketAddress peer) throws IOException
{ {
return ForeignIncubatorQuicheConnection.tryAccept(quicheConfig, tokenValidator, packetRead, peer); return ForeignIncubatorQuicheConnection.tryAccept(quicheConfig, tokenValidator, packetRead, local, peer);
} }
@Override @Override

View File

@ -53,6 +53,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
private MemorySegment sendInfo; private MemorySegment sendInfo;
private MemorySegment recvInfo; private MemorySegment recvInfo;
private MemorySegment stats; private MemorySegment stats;
private MemorySegment pathStats;
private ForeignIncubatorQuicheConnection(MemoryAddress quicheConn, MemoryAddress quicheConfig, ResourceScope scope) 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.sendInfo = quiche_send_info.allocate(scope);
this.recvInfo = quiche_recv_info.allocate(scope); this.recvInfo = quiche_recv_info.allocate(scope);
this.stats = quiche_stats.allocate(scope); this.stats = quiche_stats.allocate(scope);
this.pathStats = quiche_path_stats.allocate(scope);
} }
public static byte[] fromPacket(ByteBuffer packet) 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) if (connectionIdLength > QUICHE_MAX_CONN_ID_LEN)
throw new IOException("Connection ID length is too large: " + 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); scid.asByteBuffer().put(scidBytes);
MemoryAddress libQuicheConfig = buildConfig(quicheConfig, scope); MemoryAddress libQuicheConfig = buildConfig(quicheConfig, scope);
MemorySegment s = sockaddr.convert(peer, scope); MemorySegment localSockaddr = sockaddr.convert(local, scope);
MemoryAddress quicheConn = quiche_h.quiche_connect(CLinker.toCString(peer.getHostName(), scope), scid, scid.byteSize(), s, s.byteSize(), libQuicheConfig); 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); ForeignIncubatorQuicheConnection connection = new ForeignIncubatorQuicheConnection(quicheConn, libQuicheConfig, scope);
keepScope = true; keepScope = true;
return connection; return connection;
@ -227,6 +230,18 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
if (disableActiveMigration != null) if (disableActiveMigration != null)
quiche_h.quiche_config_set_disable_active_migration(quicheConfig, disableActiveMigration ? C_TRUE : C_FALSE); 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; 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; boolean keepScope = false;
ResourceScope scope = ResourceScope.newSharedScope(); ResourceScope scope = ResourceScope.newSharedScope();
@ -442,8 +457,9 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
LOG.debug("connection creation..."); LOG.debug("connection creation...");
MemoryAddress libQuicheConfig = buildConfig(quicheConfig, scope); MemoryAddress libQuicheConfig = buildConfig(quicheConfig, scope);
MemorySegment s = sockaddr.convert(peer, scope); MemorySegment localSockaddr = sockaddr.convert(local, scope);
MemoryAddress quicheConn = quiche_h.quiche_accept(dcid.address(), getLong(dcid_len), odcid.address(), odcid.byteSize(), s.address(), s.byteSize(), libQuicheConfig); 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) if (quicheConn == null)
{ {
quiche_h.quiche_config_free(libQuicheConfig); 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()); LOG.debug("accepted, immediately receiving the same packet - remaining in buffer: {}", packetRead.remaining());
while (packetRead.hasRemaining()) while (packetRead.hasRemaining())
{ {
quicheConnection.feedCipherBytes(packetRead, peer); quicheConnection.feedCipherBytes(packetRead, local, peer);
} }
keepScope = true; keepScope = true;
return quicheConnection; return quicheConnection;
@ -498,7 +514,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
} }
@Override @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()) try (AutoLock ignore = lock.lock())
{ {
@ -508,7 +524,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
long received; long received;
try (ResourceScope scope = ResourceScope.newConfinedScope()) try (ResourceScope scope = ResourceScope.newConfinedScope())
{ {
quiche_recv_info.setSocketAddress(recvInfo, peer, scope); quiche_recv_info.setSocketAddress(recvInfo, local, peer, scope);
if (buffer.isDirect()) if (buffer.isDirect())
{ {
// If the ByteBuffer is direct, it can be used without any copy. // If the ByteBuffer is direct, it can be used without any copy.
@ -696,6 +712,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
sendInfo = null; sendInfo = null;
recvInfo = null; recvInfo = null;
stats = null; stats = null;
pathStats = null;
} }
} }
@ -729,8 +746,8 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
{ {
if (quicheConn == null) if (quicheConn == null)
throw new IllegalStateException("connection was released"); throw new IllegalStateException("connection was released");
quiche_h.quiche_conn_stats(quicheConn, stats.address()); quiche_h.quiche_conn_path_stats(quicheConn, 0L, pathStats.address());
return quiche_stats.get_cwnd(stats); return quiche_path_stats.get_cwnd(pathStats);
} }
} }

View File

@ -33,7 +33,7 @@ public class quiche_h
{ {
// This interface is a translation of the quiche.h header of a specific version. // 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. // 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.16.0";
public static final byte C_FALSE = 0; public static final byte C_FALSE = 0;
public static final byte C_TRUE = 1; public static final byte C_TRUE = 1;
@ -142,6 +142,24 @@ public class quiche_h
FunctionDescriptor.ofVoid(C_POINTER, C_CHAR) 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( private static final MethodHandle quiche_config_free$MH = downcallHandle(
"quiche_config_free", "quiche_config_free",
"(Ljdk/incubator/foreign/MemoryAddress;)V", "(Ljdk/incubator/foreign/MemoryAddress;)V",
@ -150,8 +168,8 @@ public class quiche_h
private static final MethodHandle quiche_connect$MH = downcallHandle( private static final MethodHandle quiche_connect$MH = downcallHandle(
"quiche_connect", "quiche_connect",
"(Ljdk/incubator/foreign/MemoryAddress;Ljdk/incubator/foreign/MemoryAddress;JLjdk/incubator/foreign/MemoryAddress;JLjdk/incubator/foreign/MemoryAddress;)Ljdk/incubator/foreign/MemoryAddress;", "(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) 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( 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( private static final MethodHandle quiche_accept$MH = downcallHandle(
"quiche_accept", "quiche_accept",
"(Ljdk/incubator/foreign/MemoryAddress;JLjdk/incubator/foreign/MemoryAddress;JLjdk/incubator/foreign/MemoryAddress;JLjdk/incubator/foreign/MemoryAddress;)Ljdk/incubator/foreign/MemoryAddress;", "(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) 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( private static final MethodHandle quiche_negotiate_version$MH = downcallHandle(
@ -244,6 +262,12 @@ public class quiche_h
FunctionDescriptor.ofVoid(C_POINTER, C_POINTER) 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( private static final MethodHandle quiche_conn_stream_finished$MH = downcallHandle(
"quiche_conn_stream_finished", "quiche_conn_stream_finished",
"(Ljdk/incubator/foreign/MemoryAddress;J)B", "(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) public static void quiche_config_set_initial_max_data(MemoryAddress config, long v)
{ {
try 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 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) catch (Throwable ex)
{ {
@ -700,6 +760,18 @@ public class quiche_h
} }
} }
public static int quiche_conn_path_stats(MemoryAddress conn, long idx, MemoryAddress stats)
{
try
{
return (int)quiche_conn_path_stats$MH.invokeExact(conn, idx, stats);
}
catch (Throwable ex)
{
throw new AssertionError("should not reach here", ex);
}
}
public static void quiche_conn_on_timeout(MemoryAddress conn) public static void quiche_conn_on_timeout(MemoryAddress conn)
{ {
try try
@ -822,12 +894,13 @@ public class quiche_h
public static MemoryAddress quiche_accept(MemoryAddress scid, long scid_len, public static MemoryAddress quiche_accept(MemoryAddress scid, long scid_len,
MemoryAddress odcid, long odcid_len, MemoryAddress odcid, long odcid_len,
MemoryAddress from, long from_len, MemoryAddress local, long local_len,
MemoryAddress peer, long peer_len,
MemoryAddress config) MemoryAddress config)
{ {
try 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) 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( private static final MemoryLayout LAYOUT = MemoryLayout.structLayout(
C_POINTER.withName("from"), C_POINTER.withName("from"),
C_INT.withName("from_len"), C_INT.withName("from_len"),
MemoryLayout.paddingLayout(32),
C_POINTER.withName("to"),
C_INT.withName("to_len"),
MemoryLayout.paddingLayout(32) MemoryLayout.paddingLayout(32)
); );
private static final VarHandle from = MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, MemoryLayout.PathElement.groupElement("from"))); 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 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) public static MemorySegment allocate(ResourceScope scope)
{ {
return MemorySegment.allocateNative(LAYOUT, 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); MemorySegment peerSockAddrSegment = sockaddr.convert(peer, scope);
from.set(recvInfo, sockAddrSegment.address()); from.set(recvInfo, peerSockAddrSegment.address());
from_len.set(recvInfo, (int)sockAddrSegment.byteSize()); 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 public class quiche_send_info
{ {
private static final MemoryLayout LAYOUT = MemoryLayout.structLayout( // struct sockaddr_storage private static final MemoryLayout LAYOUT = MemoryLayout.structLayout(
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"), C_SHORT.withName("ss_family"),
MemoryLayout.sequenceLayout(118, C_CHAR).withName("__ss_padding"), MemoryLayout.sequenceLayout(118, C_CHAR).withName("__ss_padding"),
C_LONG.withName("__ss_align") C_LONG.withName("__ss_align")

View File

@ -29,14 +29,11 @@ public class quiche_stats
C_LONG.withName("sent"), C_LONG.withName("sent"),
C_LONG.withName("lost"), C_LONG.withName("lost"),
C_LONG.withName("retrans"), C_LONG.withName("retrans"),
C_LONG.withName("rtt"),
C_LONG.withName("cwnd"),
C_LONG.withName("sent_bytes"), C_LONG.withName("sent_bytes"),
C_LONG.withName("recv_bytes"), C_LONG.withName("recv_bytes"),
C_LONG.withName("lost_bytes"), C_LONG.withName("lost_bytes"),
C_LONG.withName("stream_retrans_bytes"), C_LONG.withName("stream_retrans_bytes"),
C_LONG.withName("pmtu"), C_LONG.withName("paths_count"),
C_LONG.withName("delivery_rate"),
C_LONG.withName("peer_max_idle_timeout"), C_LONG.withName("peer_max_idle_timeout"),
C_LONG.withName("peer_max_udp_payload_size"), C_LONG.withName("peer_max_udp_payload_size"),
C_LONG.withName("peer_initial_max_data"), C_LONG.withName("peer_initial_max_data"),
@ -58,14 +55,8 @@ public class quiche_stats
return MemorySegment.allocateNative(LAYOUT, scope); 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")); 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) public static long get_peer_initial_max_streams_bidi(MemorySegment stats)
{ {
return (long)peer_initial_max_streams_bidi.get(stats); return (long)peer_initial_max_streams_bidi.get(stats);

View File

@ -193,7 +193,7 @@ public class LowLevelQuicheTest
int drained = serverQuicheConnection.drainCipherBytes(buffer); int drained = serverQuicheConnection.drainCipherBytes(buffer);
assertThat(drained, is(expectedSize)); assertThat(drained, is(expectedSize));
buffer.flip(); buffer.flip();
int fed = clientQuicheConnection.feedCipherBytes(buffer, serverSocketAddress); int fed = clientQuicheConnection.feedCipherBytes(buffer, clientSocketAddress, serverSocketAddress);
assertThat(fed, is(expectedSize)); assertThat(fed, is(expectedSize));
} }
@ -206,7 +206,7 @@ public class LowLevelQuicheTest
int drained = clientQuicheConnection.drainCipherBytes(buffer); int drained = clientQuicheConnection.drainCipherBytes(buffer);
assertThat(drained, is(expectedSize)); assertThat(drained, is(expectedSize));
buffer.flip(); buffer.flip();
int fed = serverQuicheConnection.feedCipherBytes(buffer, clientSocketAddress); int fed = serverQuicheConnection.feedCipherBytes(buffer, serverSocketAddress, clientSocketAddress);
assertThat(fed, is(expectedSize)); assertThat(fed, is(expectedSize));
} }
@ -215,20 +215,20 @@ public class LowLevelQuicheTest
ByteBuffer buffer = ByteBuffer.allocate(QUICHE_MIN_CLIENT_INITIAL_LEN); ByteBuffer buffer = ByteBuffer.allocate(QUICHE_MIN_CLIENT_INITIAL_LEN);
ByteBuffer buffer2 = 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); connectionsToDisposeOf.add(clientQuicheConnection);
int drained = clientQuicheConnection.drainCipherBytes(buffer); int drained = clientQuicheConnection.drainCipherBytes(buffer);
assertThat(drained, is(1200)); assertThat(drained, is(1200));
buffer.flip(); buffer.flip();
ForeignIncubatorQuicheConnection serverQuicheConnection = ForeignIncubatorQuicheConnection.tryAccept(serverQuicheConfig, tokenValidator, buffer, clientSocketAddress); ForeignIncubatorQuicheConnection serverQuicheConnection = ForeignIncubatorQuicheConnection.tryAccept(serverQuicheConfig, tokenValidator, buffer, serverSocketAddress, clientSocketAddress);
assertThat(serverQuicheConnection, is(nullValue())); assertThat(serverQuicheConnection, is(nullValue()));
boolean negotiated = ForeignIncubatorQuicheConnection.negotiate(tokenMinter, buffer, buffer2); boolean negotiated = ForeignIncubatorQuicheConnection.negotiate(tokenMinter, buffer, buffer2);
assertThat(negotiated, is(true)); assertThat(negotiated, is(true));
buffer2.flip(); buffer2.flip();
int fed = clientQuicheConnection.feedCipherBytes(buffer2, serverSocketAddress); int fed = clientQuicheConnection.feedCipherBytes(buffer2, clientSocketAddress, serverSocketAddress);
assertThat(fed, is(79)); assertThat(fed, is(79));
buffer.clear(); buffer.clear();
@ -236,7 +236,7 @@ public class LowLevelQuicheTest
assertThat(drained, is(1200)); assertThat(drained, is(1200));
buffer.flip(); buffer.flip();
serverQuicheConnection = ForeignIncubatorQuicheConnection.tryAccept(serverQuicheConfig, tokenValidator, buffer, clientSocketAddress); serverQuicheConnection = ForeignIncubatorQuicheConnection.tryAccept(serverQuicheConfig, tokenValidator, buffer, serverSocketAddress, clientSocketAddress);
assertThat(serverQuicheConnection, is(not(nullValue()))); assertThat(serverQuicheConnection, is(not(nullValue())));
connectionsToDisposeOf.add(serverQuicheConnection); connectionsToDisposeOf.add(serverQuicheConnection);
@ -245,7 +245,7 @@ public class LowLevelQuicheTest
assertThat(drained, is(1200)); assertThat(drained, is(1200));
buffer.flip(); buffer.flip();
fed = clientQuicheConnection.feedCipherBytes(buffer, serverSocketAddress); fed = clientQuicheConnection.feedCipherBytes(buffer, clientSocketAddress, serverSocketAddress);
assertThat(fed, is(1200)); assertThat(fed, is(1200));
assertThat(serverQuicheConnection.isConnectionEstablished(), is(false)); assertThat(serverQuicheConnection.isConnectionEstablished(), is(false));

View File

@ -57,9 +57,9 @@ public class JnaQuicheBinding implements QuicheBinding
} }
@Override @Override
public QuicheConnection connect(QuicheConfig quicheConfig, InetSocketAddress peer, int connectionIdLength) throws IOException public QuicheConnection connect(QuicheConfig quicheConfig, InetSocketAddress local, InetSocketAddress peer, int connectionIdLength) throws IOException
{ {
return JnaQuicheConnection.connect(quicheConfig, peer, connectionIdLength); return JnaQuicheConnection.connect(quicheConfig, local, peer, connectionIdLength);
} }
@Override @Override
@ -69,9 +69,9 @@ public class JnaQuicheBinding implements QuicheBinding
} }
@Override @Override
public QuicheConnection tryAccept(QuicheConfig quicheConfig, QuicheConnection.TokenValidator tokenValidator, ByteBuffer packetRead, SocketAddress peer) throws IOException public QuicheConnection tryAccept(QuicheConfig quicheConfig, QuicheConnection.TokenValidator tokenValidator, ByteBuffer packetRead, SocketAddress local, SocketAddress peer) throws IOException
{ {
return JnaQuicheConnection.tryAccept(quicheConfig, tokenValidator, packetRead, peer); return JnaQuicheConnection.tryAccept(quicheConfig, tokenValidator, packetRead, local, peer);
} }
@Override @Override

View File

@ -99,12 +99,12 @@ public class JnaQuicheConnection extends QuicheConnection
return sizedBuffer; 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) if (connectionIdLength > QUICHE_MAX_CONN_ID_LEN)
throw new IOException("Connection ID length is too large: " + 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); SECURE_RANDOM.nextBytes(scid);
LibQuiche.quiche_config libQuicheConfig = buildConfig(quicheConfig); LibQuiche.quiche_config libQuicheConfig = buildConfig(quicheConfig);
SizedStructure<sockaddr> s = sockaddr.convert(peer); SizedStructure<sockaddr> localSockaddr = sockaddr.convert(local);
LibQuiche.quiche_conn quicheConn = LibQuiche.INSTANCE.quiche_connect(peer.getHostName(), scid, new size_t(scid.length), s.getStructure(), s.getSize(), libQuicheConfig); 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); return new JnaQuicheConnection(quicheConn, libQuicheConfig);
} }
@ -185,6 +186,18 @@ public class JnaQuicheConnection extends QuicheConnection
if (disableActiveMigration != null) if (disableActiveMigration != null)
LibQuiche.INSTANCE.quiche_config_set_disable_active_migration(quicheConfig, disableActiveMigration); 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; return quicheConfig;
} }
@ -297,7 +310,7 @@ public class JnaQuicheConnection extends QuicheConnection
* Fully consumes the {@code packetRead} buffer if the connection was accepted. * 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. * @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(); uint8_t_pointer type = new uint8_t_pointer();
uint32_t_pointer version = new uint32_t_pointer(); uint32_t_pointer version = new uint32_t_pointer();
@ -350,8 +363,9 @@ public class JnaQuicheConnection extends QuicheConnection
LOG.debug("connection creation..."); LOG.debug("connection creation...");
LibQuiche.quiche_config libQuicheConfig = buildConfig(quicheConfig); LibQuiche.quiche_config libQuicheConfig = buildConfig(quicheConfig);
SizedStructure<sockaddr> s = sockaddr.convert(peer); SizedStructure<sockaddr> localSockaddr = sockaddr.convert(local);
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> 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) 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()); LOG.debug("accepted, immediately receiving the same packet - remaining in buffer: {}", packetRead.remaining());
while (packetRead.hasRemaining()) while (packetRead.hasRemaining())
{ {
quicheConnection.feedCipherBytes(packetRead, peer); quicheConnection.feedCipherBytes(packetRead, local, peer);
} }
return quicheConnection; 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 @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()) try (AutoLock ignore = lock.lock())
{ {
@ -416,9 +423,12 @@ public class JnaQuicheConnection extends QuicheConnection
throw new IOException("Cannot receive when not connected"); throw new IOException("Cannot receive when not connected");
LibQuiche.quiche_recv_info info = new LibQuiche.quiche_recv_info(); LibQuiche.quiche_recv_info info = new LibQuiche.quiche_recv_info();
SizedStructure<sockaddr> s = sockaddr.convert(peer); SizedStructure<sockaddr> localSockaddr = sockaddr.convert(local);
info.from = s.getStructure().byReference(); info.to = localSockaddr.getStructure().byReference();
info.from_len = s.getSize(); 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(); int received = LibQuiche.INSTANCE.quiche_conn_recv(quicheConn, buffer, new size_t(buffer.remaining()), info).intValue();
if (received < 0) if (received < 0)
throw new IOException("failed to receive packet; err=" + quiche_error.errToString(received)); 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"); throw new IOException("Cannot send when not connected");
LibQuiche.quiche_send_info quiche_send_info = new LibQuiche.quiche_send_info(); 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 = new sockaddr_storage();
quiche_send_info.to_len = new size_t(quiche_send_info.to.size()); 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(); 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) if (quicheConn == null)
throw new IllegalStateException("connection was released"); throw new IllegalStateException("connection was released");
LibQuiche.quiche_stats stats = new LibQuiche.quiche_stats(); LibQuiche.quiche_path_stats stats = new LibQuiche.quiche_path_stats();
LibQuiche.INSTANCE.quiche_conn_stats(quicheConn, stats); LibQuiche.INSTANCE.quiche_conn_path_stats(quicheConn, new size_t(0L), stats);
return stats.cwnd.longValue(); 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. // 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. // It needs to be reviewed each time the native lib version changes.
String EXPECTED_QUICHE_VERSION = "0.12.0"; String EXPECTED_QUICHE_VERSION = "0.16.0";
// The charset used to convert java.lang.String to char * and vice versa. // The charset used to convert java.lang.String to char * and vice versa.
Charset CHARSET = StandardCharsets.UTF_8; Charset CHARSET = StandardCharsets.UTF_8;
@ -122,6 +122,18 @@ public interface LibQuiche extends Library
// Sets the `disable_active_migration` transport parameter. // Sets the `disable_active_migration` transport parameter.
void quiche_config_set_disable_active_migration(quiche_config config, boolean v); 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. // Frees the config object.
void quiche_config_free(quiche_config config); void quiche_config_free(quiche_config config);
@ -133,8 +145,8 @@ public interface LibQuiche extends Library
} }
@Structure.FieldOrder({ @Structure.FieldOrder({
"recv", "sent", "lost", "retrans", "rtt", "cwnd", "sent_bytes", "recv_bytes", "lost_bytes", "recv", "sent", "lost", "retrans", "sent_bytes", "recv_bytes", "lost_bytes",
"stream_retrans_bytes", "pmtu", "delivery_rate", "peer_max_idle_timeout", "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_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_stream_data_bidi_remote", "peer_initial_max_stream_data_uni",
"peer_initial_max_streams_bidi", "peer_initial_max_streams_uni", "peer_ack_delay_exponent", "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. // The number of sent QUIC packets with retranmitted data.
public size_t retrans; 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. // The number of sent bytes.
public uint64_t sent_bytes; public uint64_t sent_bytes;
// The number of recevied bytes. // The number of received bytes.
public uint64_t recv_bytes; public uint64_t recv_bytes;
// The number of bytes lost. // The number of bytes lost.
@ -173,11 +179,8 @@ public interface LibQuiche extends Library
// The number of stream bytes retransmitted. // The number of stream bytes retransmitted.
public uint64_t stream_retrans_bytes; public uint64_t stream_retrans_bytes;
// The current PMTU for the connection. // The number of known paths for the connection.
public size_t pmtu; public size_t paths_count;
// The most recent data delivery rate estimate in bytes/s.
public uint64_t delivery_rate;
// The maximum idle timeout. // The maximum idle timeout.
public uint64_t peer_max_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; 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.
public sockaddr_storage local_addr;
public size_t local_addr_len;
// The peer address seen by this path.
public sockaddr_storage peer_addr;
public size_t peer_addr_len;
// The validation state of the path.
public ssize_t validation_state;
// Whether this path is active.
public boolean active;
// The number of QUIC packets received on this path.
public size_t recv;
// The number of QUIC packets sent on this path.
public size_t sent;
// The number of QUIC packets that were lost on this path.
public size_t lost;
// The number of sent QUIC packets with retransmitted data on this path.
public size_t retrans;
// The estimated round-trip time of the path (in nanoseconds).
public uint64_t rtt;
// The size of the path's congestion window in bytes.
public size_t cwnd;
// The number of sent bytes on this path.
public uint64_t sent_bytes;
// The number of received bytes on this path.
public uint64_t recv_bytes;
// The number of bytes lost on this path.
public uint64_t lost_bytes;
// The number of stream bytes retransmitted on this path.
public uint64_t stream_retrans_bytes;
// The current PMTU for the path.
public size_t pmtu;
// The most recent data delivery rate estimate in bytes/s.
public uint64_t delivery_rate;
}
interface LoggingCallback extends Callback interface LoggingCallback extends Callback
{ {
void log(String msg, Pointer argp); void log(String msg, Pointer argp);
@ -228,7 +290,10 @@ public interface LibQuiche extends Library
int quiche_enable_debug_logging(LoggingCallback cb, Pointer argp); int quiche_enable_debug_logging(LoggingCallback cb, Pointer argp);
// Creates a new client-side connection. // 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 interface packet_type
{ {
@ -285,7 +350,10 @@ public interface LibQuiche extends Library
uint32_t version, ByteBuffer out, size_t out_len); uint32_t version, ByteBuffer out, size_t out_len);
// Creates a new server-side connection. // 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. // Returns the amount of time until the next timeout event, in milliseconds.
uint64_t quiche_conn_timeout_as_millis(quiche_conn conn); 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. // Collects and returns statistics about the connection.
void quiche_conn_stats(quiche_conn conn, quiche_stats out); 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 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 sockaddr_storage to;
public size_t to_len; public size_t to_len;
// The time to send the packet out.
public timespec at; public timespec at;
} }
// Writes a single QUIC packet to be sent to the peer. // 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); 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 class quiche_recv_info extends Structure
{ {
// The remote address the packet was received from.
public sockaddr.ByReference from; public sockaddr.ByReference from;
public size_t from_len; 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. // Processes QUIC packets received from the peer.

View File

@ -191,7 +191,7 @@ public class LowLevelQuicheTest
int drained = serverQuicheConnection.drainCipherBytes(buffer); int drained = serverQuicheConnection.drainCipherBytes(buffer);
assertThat(drained, is(expectedSize)); assertThat(drained, is(expectedSize));
buffer.flip(); buffer.flip();
int fed = clientQuicheConnection.feedCipherBytes(buffer, serverSocketAddress); int fed = clientQuicheConnection.feedCipherBytes(buffer, clientSocketAddress, serverSocketAddress);
assertThat(fed, is(expectedSize)); assertThat(fed, is(expectedSize));
} }
@ -204,7 +204,7 @@ public class LowLevelQuicheTest
int drained = clientQuicheConnection.drainCipherBytes(buffer); int drained = clientQuicheConnection.drainCipherBytes(buffer);
assertThat(drained, is(expectedSize)); assertThat(drained, is(expectedSize));
buffer.flip(); buffer.flip();
int fed = serverQuicheConnection.feedCipherBytes(buffer, clientSocketAddress); int fed = serverQuicheConnection.feedCipherBytes(buffer, serverSocketAddress, clientSocketAddress);
assertThat(fed, is(expectedSize)); assertThat(fed, is(expectedSize));
} }
@ -213,20 +213,20 @@ public class LowLevelQuicheTest
ByteBuffer buffer = ByteBuffer.allocate(QUICHE_MIN_CLIENT_INITIAL_LEN); ByteBuffer buffer = ByteBuffer.allocate(QUICHE_MIN_CLIENT_INITIAL_LEN);
ByteBuffer buffer2 = 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); connectionsToDisposeOf.add(clientQuicheConnection);
int drained = clientQuicheConnection.drainCipherBytes(buffer); int drained = clientQuicheConnection.drainCipherBytes(buffer);
assertThat(drained, is(1200)); assertThat(drained, is(1200));
buffer.flip(); buffer.flip();
JnaQuicheConnection serverQuicheConnection = JnaQuicheConnection.tryAccept(serverQuicheConfig, tokenValidator, buffer, clientSocketAddress); JnaQuicheConnection serverQuicheConnection = JnaQuicheConnection.tryAccept(serverQuicheConfig, tokenValidator, buffer, serverSocketAddress, clientSocketAddress);
assertThat(serverQuicheConnection, is(nullValue())); assertThat(serverQuicheConnection, is(nullValue()));
boolean negotiated = JnaQuicheConnection.negotiate(tokenMinter, buffer, buffer2); boolean negotiated = JnaQuicheConnection.negotiate(tokenMinter, buffer, buffer2);
assertThat(negotiated, is(true)); assertThat(negotiated, is(true));
buffer2.flip(); buffer2.flip();
int fed = clientQuicheConnection.feedCipherBytes(buffer2, serverSocketAddress); int fed = clientQuicheConnection.feedCipherBytes(buffer2, clientSocketAddress, serverSocketAddress);
assertThat(fed, is(79)); assertThat(fed, is(79));
buffer.clear(); buffer.clear();
@ -234,7 +234,7 @@ public class LowLevelQuicheTest
assertThat(drained, is(1200)); assertThat(drained, is(1200));
buffer.flip(); buffer.flip();
serverQuicheConnection = JnaQuicheConnection.tryAccept(serverQuicheConfig, tokenValidator, buffer, clientSocketAddress); serverQuicheConnection = JnaQuicheConnection.tryAccept(serverQuicheConfig, tokenValidator, buffer, serverSocketAddress, clientSocketAddress);
assertThat(serverQuicheConnection, is(not(nullValue()))); assertThat(serverQuicheConnection, is(not(nullValue())));
connectionsToDisposeOf.add(serverQuicheConnection); connectionsToDisposeOf.add(serverQuicheConnection);
@ -243,7 +243,7 @@ public class LowLevelQuicheTest
assertThat(drained, is(1200)); assertThat(drained, is(1200));
buffer.flip(); buffer.flip();
fed = clientQuicheConnection.feedCipherBytes(buffer, serverSocketAddress); fed = clientQuicheConnection.feedCipherBytes(buffer, clientSocketAddress, serverSocketAddress);
assertThat(fed, is(1200)); assertThat(fed, is(1200));
assertThat(serverQuicheConnection.isConnectionEstablished(), is(false)); assertThat(serverQuicheConnection.isConnectionEstablished(), is(false));

View File

@ -62,7 +62,7 @@ public class ServerQuicConnection extends QuicConnection
{ {
ByteBufferPool byteBufferPool = getByteBufferPool(); ByteBufferPool byteBufferPool = getByteBufferPool();
// TODO make the token validator configurable // TODO make the token validator configurable
QuicheConnection quicheConnection = QuicheConnection.tryAccept(connector.newQuicheConfig(), new SimpleTokenValidator((InetSocketAddress)remoteAddress), cipherBuffer, remoteAddress); QuicheConnection quicheConnection = QuicheConnection.tryAccept(connector.newQuicheConfig(), new SimpleTokenValidator((InetSocketAddress)remoteAddress), cipherBuffer, getEndPoint().getLocalAddress(), remoteAddress);
if (quicheConnection == null) if (quicheConnection == null)
{ {
ByteBuffer negotiationBuffer = byteBufferPool.acquire(getOutputBufferSize(), true); ByteBuffer negotiationBuffer = byteBufferPool.acquire(getOutputBufferSize(), true);

View File

@ -84,8 +84,8 @@
<jboss-threads.version>3.5.0.Final</jboss-threads.version> <jboss-threads.version>3.5.0.Final</jboss-threads.version>
<jetty-assembly-descriptors.version>1.1</jetty-assembly-descriptors.version> <jetty-assembly-descriptors.version>1.1</jetty-assembly-descriptors.version>
<jetty.perf-helper.version>1.0.7</jetty.perf-helper.version> <jetty.perf-helper.version>1.0.7</jetty.perf-helper.version>
<jetty-quiche-native.version>0.16.0</jetty-quiche-native.version>
<jetty.servlet.api.version>5.0.2</jetty.servlet.api.version> <jetty.servlet.api.version>5.0.2</jetty.servlet.api.version>
<jetty-quiche-native.version>0.12.0</jetty-quiche-native.version>
<jetty-test-policy.version>1.2</jetty-test-policy.version> <jetty-test-policy.version>1.2</jetty-test-policy.version>
<jetty.test.version>6.0</jetty.test.version> <jetty.test.version>6.0</jetty.test.version>
<jmh.version>1.35</jmh.version> <jmh.version>1.35</jmh.version>