#8695: fix inconsistencies between quiche's native API and its JNA/Foreign bindings

Signed-off-by: Ludovic Orban <lorban@bitronix.be>
This commit is contained in:
Ludovic Orban 2022-10-11 10:54:06 +02:00
parent d6a101d463
commit 15e90acab6
11 changed files with 85 additions and 40 deletions

View File

@ -21,6 +21,7 @@ import java.net.SocketException;
import java.net.SocketOption;
import java.net.StandardProtocolFamily;
import java.net.StandardSocketOptions;
import java.nio.channels.DatagramChannel;
import java.nio.channels.NetworkChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
@ -501,6 +502,13 @@ public class ClientConnector extends ContainerLifeCycle
if (sendBufferSize >= 0)
setSocketOption(channel, StandardSocketOptions.SO_SNDBUF, sendBufferSize);
}
if (selectable instanceof DatagramChannel)
{
// QUIC must know the local address, but it is non-null on datagram sockets only if it has been bound,
// so implicitly bind to 0.0.0.0:0 when no bind address has been set.
if (getBindAddress() == null)
setBindAddress(new InetSocketAddress("0.0.0.0", 0));
}
}
private <T> void setSocketOption(NetworkChannel channel, SocketOption<T> option, T value)

View File

@ -61,6 +61,43 @@
<artifactId>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>
<profile>
<!-- Don't use the Foreign binding if the JDK version != 17. -->
<id>jdk18+</id>
<activation>
<jdk>[18,)</jdk>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
@{argLine}
${jetty.surefire.argLine}
</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

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

View File

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

View File

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

View File

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

View File

@ -746,8 +746,8 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
{
if (quicheConn == null)
throw new IllegalStateException("connection was released");
quiche_h.quiche_conn_path_stats(quicheConn, pathStats.address());
return quiche_path_stats.get_cwnd(stats);
quiche_h.quiche_conn_path_stats(quicheConn, 0L, pathStats.address());
return quiche_path_stats.get_cwnd(pathStats);
}
}

View File

@ -760,11 +760,11 @@ public class quiche_h
}
}
public static int quiche_conn_path_stats(MemoryAddress conn, MemoryAddress stats)
public static int quiche_conn_path_stats(MemoryAddress conn, long idx, MemoryAddress stats)
{
try
{
return (int)quiche_conn_path_stats$MH.invokeExact(conn, stats);
return (int)quiche_conn_path_stats$MH.invokeExact(conn, idx, stats);
}
catch (Throwable ex)
{

View File

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

View File

@ -231,54 +231,54 @@ public interface LibQuiche extends Library
class quiche_path_stats extends Structure
{
// The local address used by this path.
sockaddr_storage local_addr;
size_t local_addr_len;
public sockaddr_storage local_addr;
public size_t local_addr_len;
// The peer address seen by this path.
sockaddr_storage peer_addr;
size_t peer_addr_len;
public sockaddr_storage peer_addr;
public size_t peer_addr_len;
// The validation state of the path.
ssize_t validation_state;
public ssize_t validation_state;
// Whether this path is active.
boolean active;
public boolean active;
// The number of QUIC packets received on this path.
size_t recv;
public size_t recv;
// The number of QUIC packets sent on this path.
size_t sent;
public size_t sent;
// The number of QUIC packets that were lost on this path.
size_t lost;
public size_t lost;
// The number of sent QUIC packets with retransmitted data on this path.
size_t retrans;
public size_t retrans;
// The estimated round-trip time of the path (in nanoseconds).
uint64_t rtt;
public uint64_t rtt;
// The size of the path's congestion window in bytes.
size_t cwnd;
public size_t cwnd;
// The number of sent bytes on this path.
uint64_t sent_bytes;
public uint64_t sent_bytes;
// The number of received bytes on this path.
uint64_t recv_bytes;
public uint64_t recv_bytes;
// The number of bytes lost on this path.
uint64_t lost_bytes;
public uint64_t lost_bytes;
// The number of stream bytes retransmitted on this path.
uint64_t stream_retrans_bytes;
public uint64_t stream_retrans_bytes;
// The current PMTU for the path.
size_t pmtu;
public size_t pmtu;
// The most recent data delivery rate estimate in bytes/s.
uint64_t delivery_rate;
public uint64_t delivery_rate;
}
interface LoggingCallback extends Callback

View File

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