Merge remote-tracking branch 'origin/jetty-11.0.x' into jetty-12.0.x
This commit is contained in:
commit
b10e25b683
|
@ -53,14 +53,35 @@
|
|||
<profile>
|
||||
<id>jdk17</id>
|
||||
<activation>
|
||||
<jdk>[17,)</jdk>
|
||||
<jdk>17</jdk>
|
||||
</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>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.quic</groupId>
|
||||
<artifactId>jetty-quic-quiche-foreign-incubator</artifactId>
|
||||
</dependency>
|
||||
</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>
|
||||
</profiles>
|
||||
</project>
|
||||
|
|
|
@ -96,7 +96,7 @@ public class ClientQuicConnection extends QuicConnection
|
|||
if (LOG.isDebugEnabled())
|
||||
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);
|
||||
pendingSessions.put(remoteAddress, session);
|
||||
if (LOG.isDebugEnabled())
|
||||
|
|
|
@ -75,6 +75,11 @@ public class QuicClientConnectorConfigurator extends ClientConnector.Configurato
|
|||
{
|
||||
context.put(QuicConfiguration.CONTEXT_KEY, configuration);
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -24,7 +24,7 @@ public interface QuicheBinding
|
|||
int priority();
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,14 +49,14 @@ public abstract class QuicheConnection
|
|||
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.
|
||||
* @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()
|
||||
|
@ -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.
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
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.
|
||||
-->
|
||||
<id>jdk18+</id>
|
||||
<id>jdk18</id>
|
||||
<activation>
|
||||
<jdk>[18,)</jdk>
|
||||
</activation>
|
||||
|
|
|
@ -57,9 +57,9 @@ public class ForeignIncubatorQuicheBinding implements QuicheBinding
|
|||
}
|
||||
|
||||
@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
|
||||
|
@ -69,9 +69,9 @@ public class ForeignIncubatorQuicheBinding implements QuicheBinding
|
|||
}
|
||||
|
||||
@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
|
||||
|
|
|
@ -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, 0L, pathStats.address());
|
||||
return quiche_path_stats.get_cwnd(pathStats);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.16.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, 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -197,7 +197,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));
|
||||
}
|
||||
|
||||
|
@ -210,7 +210,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));
|
||||
}
|
||||
|
||||
|
@ -219,20 +219,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();
|
||||
|
@ -240,7 +240,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);
|
||||
|
||||
|
@ -249,7 +249,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));
|
||||
|
|
|
@ -57,9 +57,9 @@ public class JnaQuicheBinding implements QuicheBinding
|
|||
}
|
||||
|
||||
@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
|
||||
|
@ -69,9 +69,9 @@ public class JnaQuicheBinding implements QuicheBinding
|
|||
}
|
||||
|
||||
@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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.16.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.
|
||||
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
|
||||
{
|
||||
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.
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -62,7 +62,7 @@ public class ServerQuicConnection extends QuicConnection
|
|||
{
|
||||
ByteBufferPool byteBufferPool = getByteBufferPool();
|
||||
// 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)
|
||||
{
|
||||
ByteBuffer negotiationBuffer = byteBufferPool.acquire(getOutputBufferSize(), true);
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -58,7 +58,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.16.0</jetty-quiche-native.version>
|
||||
<jetty-test-policy.version>1.2</jetty-test-policy.version>
|
||||
<jetty.test.version>6.0</jetty.test.version>
|
||||
<jetty.xhtml.schemas-version>1.1</jetty.xhtml.schemas-version>
|
||||
|
|
Loading…
Reference in New Issue