diff --git a/src/main/java/io/netty/incubator/codec/quic/Quiche.java b/src/main/java/io/netty/incubator/codec/quic/Quiche.java index 6798fe6c3..ae71376b5 100644 --- a/src/main/java/io/netty/incubator/codec/quic/Quiche.java +++ b/src/main/java/io/netty/incubator/codec/quic/Quiche.java @@ -594,11 +594,30 @@ static long memoryAddress(ByteBuf buf) { buffer_memory_address(buf.internalNioBuffer(buf.readerIndex(), buf.readableBytes())); } + /** + * Returns the memory address of the given {@link ByteBuffer}. If you want to also respect the + * {@link ByteBuffer#position()} use {@link #memoryAddressWithPosition(ByteBuffer)}. + * + * @param buf the {@link ByteBuffer} of which we want to obtain the memory address.. + * @return the memory address of this {@link ByteBuffer}. + */ static long memoryAddress(ByteBuffer buf) { assert buf.isDirect(); return buffer_memory_address(buf); } + /** + * Returns the memory address of the given {@link ByteBuffer} taking its current {@link ByteBuffer#position()} into + * account. + * + * @param buf the {@link ByteBuffer} of which we want to obtain the memory address + * (taking its {@link ByteBuffer#position()} into account). + * @return the memory address of this {@link ByteBuffer}s position. + */ + static long memoryAddressWithPosition(ByteBuffer buf) { + return memoryAddress(buf) + buf.position(); + } + @SuppressWarnings("deprecation") static ByteBuf allocateNativeOrder(int capacity) { // Just use Unpooled as the life-time of these buffers is long. diff --git a/src/main/java/io/netty/incubator/codec/quic/QuicheQuicChannel.java b/src/main/java/io/netty/incubator/codec/quic/QuicheQuicChannel.java index e747cc805..a2e6c463c 100644 --- a/src/main/java/io/netty/incubator/codec/quic/QuicheQuicChannel.java +++ b/src/main/java/io/netty/incubator/codec/quic/QuicheQuicChannel.java @@ -118,9 +118,6 @@ public void operationComplete(ChannelFuture future) { private ByteBuffer key; private CloseData closeData; private QuicConnectionStats statsAtClose; - - private long currentRecvInfoAddress; - private long currentSendInfoAddress; private InetSocketAddress remote; private boolean supportsDatagram; private boolean recvDatagramPending; @@ -209,9 +206,7 @@ void attachQuicheConnection(QuicheQuicConnection connection) { this.traceId = new String(traceId); } - connection.initInfoAddresses(remote); - currentRecvInfoAddress = connection.recvInfoAddress(); - currentSendInfoAddress = connection.sendInfoAddress(); + connection.initInfo(remote); // Setup QLOG if needed. QLogConfiguration configuration = config.getQLogConfiguration(); @@ -240,7 +235,7 @@ void attachQuicheConnection(QuicheQuicConnection connection) { private void connect(Function engineProvider, long configAddr, int localConnIdLength, - boolean supportsDatagram, long sockaddr) throws Exception { + boolean supportsDatagram, ByteBuffer sockaddrMemory) throws Exception { assert this.connection == null; assert this.traceId == null; assert this.key == null; @@ -267,10 +262,11 @@ private void connect(Function engineProvid ByteBuffer connectId = address.connId.duplicate(); ByteBuf idBuffer = alloc().directBuffer(connectId.remaining()).writeBytes(connectId.duplicate()); try { - int sockaddrLen = SockaddrIn.write(sockaddr, remote); + int sockaddrLen = SockaddrIn.setAddress(sockaddrMemory, remote); QuicheQuicConnection connection = quicheEngine.createConnection(ssl -> Quiche.quiche_conn_new_with_tls(Quiche.memoryAddress(idBuffer) + idBuffer.readerIndex(), - idBuffer.readableBytes(), -1, -1, sockaddr, sockaddrLen, + idBuffer.readableBytes(), -1, -1, + Quiche.memoryAddressWithPosition(sockaddrMemory), sockaddrLen, configAddr, ssl, false)); if (connection == null) { failConnectPromiseAndThrow(new ConnectException()); @@ -745,7 +741,7 @@ private int streamSend0(long streamId, ByteBuf buffer, boolean fin) throws Close private int streamSend(long streamId, ByteBuffer buffer, boolean fin) throws ClosedChannelException { return Quiche.quiche_conn_stream_send(connectionAddressChecked(), streamId, - Quiche.memoryAddress(buffer) + buffer.position(), buffer.remaining(), fin); + Quiche.memoryAddressWithPosition(buffer), buffer.remaining(), fin); } StreamRecvResult streamRecv(long streamId, ByteBuf buffer) throws Exception { @@ -891,13 +887,14 @@ private boolean connectionSendSegments(SegmentedDatagramPacketAllocator segmente ByteBuf out = alloc().directBuffer(bufferSize); int lastWritten = -1; for (;;) { - long sendInfo = connection.nextSendInfoAddress(currentSendInfoAddress); + ByteBuffer sendInfo = connection.nextSendInfo(); InetSocketAddress sendToAddress = this.remote; boolean done; int writerIndex = out.writerIndex(); int written = Quiche.quiche_conn_send( - connAddr, Quiche.memoryAddress(out) + writerIndex, out.writableBytes(), sendInfo); + connAddr, Quiche.memoryAddress(out) + writerIndex, out.writableBytes(), + Quiche.memoryAddressWithPosition(sendInfo)); if (written == 0) { // No need to create a new datagram packet. Just try again. continue; @@ -929,14 +926,10 @@ private boolean connectionSendSegments(SegmentedDatagramPacketAllocator segmente boolean needWriteNow = false; - if (SockaddrIn.cmp(QuicheSendInfo.sockAddress(currentSendInfoAddress), - QuicheSendInfo.sockAddress(sendInfo)) != 0) { - // Update the current address so we can keep track when it change again. - currentSendInfoAddress = sendInfo; - + if (connection.isSendInfoChanged()) { // Change the cached address InetSocketAddress oldRemote = remote; - remote = QuicheSendInfo.read(sendInfo); + remote = QuicheSendInfo.getAddress(sendInfo); pipeline().fireUserEventTriggered( new QuicConnectionMigrationEvent(oldRemote, remote)); needWriteNow = true; @@ -1001,11 +994,12 @@ private boolean connectionSendSimple() { long connAddr = connection.address(); boolean packetWasWritten = false; for (;;) { - long sendInfo = connection.nextSendInfoAddress(currentSendInfoAddress); + ByteBuffer sendInfo = connection.nextSendInfo(); ByteBuf out = alloc().directBuffer(Quic.MAX_DATAGRAM_SIZE); int writerIndex = out.writerIndex(); int written = Quiche.quiche_conn_send( - connAddr, Quiche.memoryAddress(out) + writerIndex, out.writableBytes(), sendInfo); + connAddr, Quiche.memoryAddress(out) + writerIndex, out.writableBytes(), + Quiche.memoryAddressWithPosition(sendInfo)); try { if (Quiche.throwIfError(written)) { @@ -1023,14 +1017,10 @@ private boolean connectionSendSimple() { out.release(); continue; } - if (SockaddrIn.cmp(QuicheSendInfo.sockAddress(currentSendInfoAddress), - QuicheSendInfo.sockAddress(sendInfo)) != 0) { - // Update the current address so we can keep track when it change again. - currentSendInfoAddress = sendInfo; - + if (connection.isSendInfoChanged()) { // Change the cached address InetSocketAddress oldRemote = remote; - remote = QuicheSendInfo.read(sendInfo); + remote = QuicheSendInfo.getAddress(sendInfo); pipeline().fireUserEventTriggered( new QuicConnectionMigrationEvent(oldRemote, remote)); } @@ -1176,15 +1166,13 @@ void connectionRecv(InetSocketAddress sender, ByteBuf buffer) { int bufferReaderIndex = buffer.readerIndex(); long memoryAddress = Quiche.memoryAddress(buffer) + bufferReaderIndex; - long recvInfoAddress = connection.nextRecvInfoAddress(currentRecvInfoAddress); - QuicheRecvInfo.write(recvInfoAddress, sender); + ByteBuffer recvInfo = connection.nextRecvInfo(); + QuicheRecvInfo.setRecvInfo(recvInfo, sender); SocketAddress oldRemote = remote; - if (SockaddrIn.cmp(QuicheRecvInfo.sockAddress(currentRecvInfoAddress), - QuicheRecvInfo.sockAddress(recvInfoAddress)) != 0) { + if (connection.isRecvInfoChanged()) { // Update the cached address - currentRecvInfoAddress = recvInfoAddress; remote = sender; pipeline().fireUserEventTriggered( new QuicConnectionMigrationEvent(oldRemote, sender)); @@ -1194,7 +1182,8 @@ void connectionRecv(InetSocketAddress sender, ByteBuf buffer) { try { do { // Call quiche_conn_recv(...) until we consumed all bytes or we did receive some error. - int res = Quiche.quiche_conn_recv(connAddr, memoryAddress, bufferReadable, recvInfoAddress); + int res = Quiche.quiche_conn_recv(connAddr, memoryAddress, bufferReadable, + Quiche.memoryAddressWithPosition(recvInfo)); boolean done; try { done = Quiche.throwIfError(res); @@ -1246,8 +1235,6 @@ void connectionRecv(InetSocketAddress sender, ByteBuf buffer) { } } while (bufferReadable > 0); } finally { - // Store for later usage. - currentRecvInfoAddress = recvInfoAddress; buffer.skipBytes((int) (memoryAddress - Quiche.memoryAddress(buffer))); if (tmpBuffer != null) { tmpBuffer.release(); @@ -1432,11 +1419,11 @@ void finishConnect() { // TODO: Come up with something better. static QuicheQuicChannel handleConnect(Function sslEngineProvider, SocketAddress address, long config, int localConnIdLength, - boolean supportsDatagram, long sockaddr) throws Exception { + boolean supportsDatagram, ByteBuffer sockaddrMemory) throws Exception { if (address instanceof QuicheQuicChannel.QuicheQuicChannelAddress) { QuicheQuicChannel.QuicheQuicChannelAddress addr = (QuicheQuicChannel.QuicheQuicChannelAddress) address; QuicheQuicChannel channel = addr.channel; - channel.connect(sslEngineProvider, config, localConnIdLength, supportsDatagram, sockaddr); + channel.connect(sslEngineProvider, config, localConnIdLength, supportsDatagram, sockaddrMemory); return channel; } return null; diff --git a/src/main/java/io/netty/incubator/codec/quic/QuicheQuicClientCodec.java b/src/main/java/io/netty/incubator/codec/quic/QuicheQuicClientCodec.java index 34d08895c..4e910fb8a 100644 --- a/src/main/java/io/netty/incubator/codec/quic/QuicheQuicClientCodec.java +++ b/src/main/java/io/netty/incubator/codec/quic/QuicheQuicClientCodec.java @@ -54,7 +54,8 @@ public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, final QuicheQuicChannel channel; try { channel = QuicheQuicChannel.handleConnect(sslEngineProvider, remoteAddress, config.nativeAddress(), - localConnIdLength, config.isDatagramSupported(), sockaddrMemory.memoryAddress()); + localConnIdLength, config.isDatagramSupported(), + sockaddrMemory.internalNioBuffer(0, sockaddrMemory.capacity())); } catch (Exception e) { promise.setFailure(e); return; diff --git a/src/main/java/io/netty/incubator/codec/quic/QuicheQuicConnection.java b/src/main/java/io/netty/incubator/codec/quic/QuicheQuicConnection.java index 1ec3877b2..11e5623ea 100644 --- a/src/main/java/io/netty/incubator/codec/quic/QuicheQuicConnection.java +++ b/src/main/java/io/netty/incubator/codec/quic/QuicheQuicConnection.java @@ -19,11 +19,11 @@ import io.netty.util.ReferenceCounted; import java.net.InetSocketAddress; +import java.nio.ByteBuffer; import java.util.function.Supplier; final class QuicheQuicConnection { private static final int TOTAL_RECV_INFO_SIZE = Quiche.SIZEOF_QUICHE_RECV_INFO + Quiche.SIZEOF_SOCKADDR_STORAGE; - private static final int QUICHE_SEND_INFOS_OFFSET = 2 * TOTAL_RECV_INFO_SIZE; private final ReferenceCounted refCnt; private final QuicheQuicSslEngine engine; @@ -37,7 +37,16 @@ final class QuicheQuicConnection { // // We need to have every stored 2 times as we need to check if the last sockaddr has changed between // quiche_conn_recv and quiche_conn_send calls. If this happens we know a QUIC connection migration did happen. - private final ByteBuf infoBuffer; + private final ByteBuf recvInfoBuffer; + private final ByteBuf sendInfoBuffer; + + private boolean recvInfoFirst = true; + private boolean sendInfoFirst = true; + private final ByteBuffer recvInfoBuffer1; + private final ByteBuffer recvInfoBuffer2; + private final ByteBuffer sendInfoBuffer1; + private final ByteBuffer sendInfoBuffer2; + private long connection; QuicheQuicConnection(long connection, QuicheQuicSslEngine engine, ReferenceCounted refCnt) { @@ -45,10 +54,18 @@ final class QuicheQuicConnection { this.engine = engine; this.refCnt = refCnt; // TODO: Maybe cache these per thread as we only use them temporary within a limited scope. - infoBuffer = Quiche.allocateNativeOrder(QUICHE_SEND_INFOS_OFFSET + - 2 * Quiche.SIZEOF_QUICHE_SEND_INFO); + recvInfoBuffer = Quiche.allocateNativeOrder(2 * TOTAL_RECV_INFO_SIZE); + sendInfoBuffer = Quiche.allocateNativeOrder(2 * Quiche.SIZEOF_QUICHE_SEND_INFO); + // Let's memset the memory. - infoBuffer.setZero(0, infoBuffer.capacity()); + recvInfoBuffer.setZero(0, recvInfoBuffer.capacity()); + sendInfoBuffer.setZero(0, sendInfoBuffer.capacity()); + + recvInfoBuffer1 = recvInfoBuffer.nioBuffer(0, TOTAL_RECV_INFO_SIZE); + recvInfoBuffer2 = recvInfoBuffer.nioBuffer(TOTAL_RECV_INFO_SIZE, TOTAL_RECV_INFO_SIZE); + + sendInfoBuffer1 = sendInfoBuffer.nioBuffer(0, Quiche.SIZEOF_QUICHE_SEND_INFO); + sendInfoBuffer2 = sendInfoBuffer.nioBuffer(Quiche.SIZEOF_QUICHE_SEND_INFO, Quiche.SIZEOF_QUICHE_SEND_INFO); } void free() { @@ -65,7 +82,8 @@ void free() { } if (release) { refCnt.release(); - infoBuffer.release(); + recvInfoBuffer.release(); + sendInfoBuffer.release(); } } @@ -97,43 +115,40 @@ long address() { return connection; } - private long sendInfosAddress() { - return infoBuffer.memoryAddress() + QUICHE_SEND_INFOS_OFFSET; - } + void initInfo(InetSocketAddress address) { + assert connection != -1; + assert recvInfoBuffer.refCnt() != 0; + assert sendInfoBuffer.refCnt() != 0; - void initInfoAddresses(InetSocketAddress address) { // Fill both quiche_recv_info structs with the same address. - QuicheRecvInfo.write(infoBuffer.memoryAddress(), address); - QuicheRecvInfo.write(infoBuffer.memoryAddress() + TOTAL_RECV_INFO_SIZE, address); + QuicheRecvInfo.setRecvInfo(recvInfoBuffer1, address); + QuicheRecvInfo.setRecvInfo(recvInfoBuffer2, address); // Fill both quiche_send_info structs with the same address. - long sendInfosAddress = sendInfosAddress(); - QuicheSendInfo.write(sendInfosAddress, address); - QuicheSendInfo.write(sendInfosAddress + Quiche.SIZEOF_QUICHE_SEND_INFO, address); + QuicheSendInfo.setSendInfo(sendInfoBuffer1, address); + QuicheSendInfo.setSendInfo(sendInfoBuffer2, address); } - long recvInfoAddress() { - return infoBuffer.memoryAddress(); + ByteBuffer nextRecvInfo() { + assert recvInfoBuffer.refCnt() != 0; + recvInfoFirst = !recvInfoFirst; + return recvInfoFirst ? recvInfoBuffer1 : recvInfoBuffer2; } - long sendInfoAddress() { - return sendInfosAddress(); + ByteBuffer nextSendInfo() { + assert sendInfoBuffer.refCnt() != 0; + sendInfoFirst = !sendInfoFirst; + return sendInfoFirst ? sendInfoBuffer1 : sendInfoBuffer2; } - long nextRecvInfoAddress(long previousRecvInfoAddress) { - long memoryAddress = infoBuffer.memoryAddress(); - if (memoryAddress == previousRecvInfoAddress) { - return memoryAddress + TOTAL_RECV_INFO_SIZE; - } - return memoryAddress; + boolean isSendInfoChanged() { + assert sendInfoBuffer.refCnt() != 0; + return !QuicheSendInfo.isSameAddress(sendInfoBuffer1, sendInfoBuffer2); } - long nextSendInfoAddress(long previousSendInfoAddress) { - long memoryAddress = sendInfosAddress(); - if (memoryAddress == previousSendInfoAddress) { - return memoryAddress + Quiche.SIZEOF_QUICHE_SEND_INFO; - } - return memoryAddress; + boolean isRecvInfoChanged() { + assert recvInfoBuffer.refCnt() != 0; + return !QuicheRecvInfo.isSameAddress(recvInfoBuffer1, recvInfoBuffer2); } boolean isClosed() { @@ -151,5 +166,4 @@ protected void finalize() throws Throwable { super.finalize(); } } - } diff --git a/src/main/java/io/netty/incubator/codec/quic/QuicheQuicServerCodec.java b/src/main/java/io/netty/incubator/codec/quic/QuicheQuicServerCodec.java index 778124987..3d90e1edf 100644 --- a/src/main/java/io/netty/incubator/codec/quic/QuicheQuicServerCodec.java +++ b/src/main/java/io/netty/incubator/codec/quic/QuicheQuicServerCodec.java @@ -212,9 +212,10 @@ private QuicheQuicChannel handleServer(ChannelHandlerContext ctx, InetSocketAddr QuicheQuicSslEngine quicSslEngine = (QuicheQuicSslEngine) engine; QuicheQuicConnection connection = quicSslEngine.createConnection(ssl -> { - long peerAddr = sockaddrMemory.memoryAddress(); - int peerLen = SockaddrIn.write(peerAddr, sender); - return Quiche.quiche_conn_new_with_tls(scidAddr, scidLen, ocidAddr, ocidLen, peerAddr, peerLen, + ByteBuffer peerAddrMemory = sockaddrMemory.internalNioBuffer(0, sockaddrMemory.capacity()); + int peerLen = SockaddrIn.setAddress(peerAddrMemory, sender); + return Quiche.quiche_conn_new_with_tls(scidAddr, scidLen, ocidAddr, ocidLen, + Quiche.memoryAddressWithPosition(peerAddrMemory), peerLen, config.nativeAddress(), ssl, true); }); if (connection == null) { diff --git a/src/main/java/io/netty/incubator/codec/quic/QuicheRecvInfo.java b/src/main/java/io/netty/incubator/codec/quic/QuicheRecvInfo.java index 87be4f29b..af3af77b4 100644 --- a/src/main/java/io/netty/incubator/codec/quic/QuicheRecvInfo.java +++ b/src/main/java/io/netty/incubator/codec/quic/QuicheRecvInfo.java @@ -15,16 +15,19 @@ */ package io.netty.incubator.codec.quic; -import io.netty.util.internal.PlatformDependent; - import java.net.InetSocketAddress; +import java.nio.ByteBuffer; + +/** + * Utility class to handle access to {@code quiche_recv_info}. + */ final class QuicheRecvInfo { private QuicheRecvInfo() { } /** - * Write the {@link InetSocketAddress} into the {@code quiche_recv_info} struct. + * Set the {@link InetSocketAddress} into the {@code quiche_recv_info} struct. * *
      * typedef struct {
@@ -33,41 +36,54 @@ private QuicheRecvInfo() { }
      * } quiche_recv_info;
      * 
* - * @param memory the memory address of {@code quiche_recv_info}. + * @param memory the memory of {@code quiche_recv_info}. * @param address the {@link InetSocketAddress} to write into {@code quiche_recv_info}. */ - static void write(long memory, InetSocketAddress address) { - long sockaddr = memory + Quiche.SIZEOF_QUICHE_RECV_INFO; - int len = SockaddrIn.write(sockaddr, address); - if (Quiche.SIZEOF_SIZE_T == 4) { - PlatformDependent.putInt(memory + Quiche.QUICHE_RECV_INFO_OFFSETOF_FROM, (int) sockaddr); - } else { - PlatformDependent.putLong(memory + Quiche.QUICHE_RECV_INFO_OFFSETOF_FROM, sockaddr); - } - switch (Quiche.SIZEOF_SOCKLEN_T) { - case 1: - PlatformDependent.putByte(memory + Quiche.QUICHE_RECV_INFO_OFFSETOF_FROM_LEN, (byte) len); - break; - case 2: - PlatformDependent.putShort(memory + Quiche.QUICHE_RECV_INFO_OFFSETOF_FROM_LEN, (short) len); - break; - case 4: - PlatformDependent.putInt(memory + Quiche.QUICHE_RECV_INFO_OFFSETOF_FROM_LEN, len); - break; - case 8: - PlatformDependent.putLong(memory + Quiche.QUICHE_RECV_INFO_OFFSETOF_FROM_LEN, len); - break; - default: - throw new IllegalStateException(); + static void setRecvInfo(ByteBuffer memory, InetSocketAddress address) { + int position = memory.position(); + try { + int sockaddrPosition = position + Quiche.SIZEOF_QUICHE_RECV_INFO; + memory.position(sockaddrPosition); + + long sockaddrMemoryAddress = Quiche.memoryAddressWithPosition(memory); + int len = SockaddrIn.setAddress(memory, address); + if (Quiche.SIZEOF_SIZE_T == 4) { + memory.putInt(position + Quiche.QUICHE_RECV_INFO_OFFSETOF_FROM, (int) sockaddrMemoryAddress); + } else { + memory.putLong(position + Quiche.QUICHE_RECV_INFO_OFFSETOF_FROM, sockaddrMemoryAddress); + } + switch (Quiche.SIZEOF_SOCKLEN_T) { + case 1: + memory.put(position + Quiche.QUICHE_RECV_INFO_OFFSETOF_FROM_LEN, (byte) len); + break; + case 2: + memory.putShort(position + Quiche.QUICHE_RECV_INFO_OFFSETOF_FROM_LEN, (short) len); + break; + case 4: + memory.putInt(position + Quiche.QUICHE_RECV_INFO_OFFSETOF_FROM_LEN, len); + break; + case 8: + memory.putLong(position + Quiche.QUICHE_RECV_INFO_OFFSETOF_FROM_LEN, len); + break; + default: + throw new IllegalStateException(); + } + } finally { + memory.position(position); } } /** - * Return the memory address of the {@code sockaddr} that is contained in {@code quiche_recv_info}. - * @param memory the memory address of {@code quiche_recv_info}. - * @return the memory address of the {@code sockaddr}. + * Returns {@code true} if both {@link ByteBuffer}s have the same {@code sock_addr} stored. + * + * @param memory the first {@link ByteBuffer} which holds a {@code quiche_recv_info}. + * @param memory2 the second {@link ByteBuffer} which holds a {@code quiche_recv_info}. + * @return {@code true} if both {@link ByteBuffer}s have the same {@code sock_addr} stored, {@code false} + * otherwise. */ - static long sockAddress(long memory) { - return memory + Quiche.SIZEOF_QUICHE_RECV_INFO; + static boolean isSameAddress(ByteBuffer memory, ByteBuffer memory2) { + long address1 = Quiche.memoryAddressWithPosition(memory) + Quiche.SIZEOF_QUICHE_RECV_INFO; + long address2 = Quiche.memoryAddressWithPosition(memory2) + Quiche.SIZEOF_QUICHE_RECV_INFO; + return SockaddrIn.cmp(address1, address2) == 0; } } diff --git a/src/main/java/io/netty/incubator/codec/quic/QuicheSendInfo.java b/src/main/java/io/netty/incubator/codec/quic/QuicheSendInfo.java index 970895883..95e32135c 100644 --- a/src/main/java/io/netty/incubator/codec/quic/QuicheSendInfo.java +++ b/src/main/java/io/netty/incubator/codec/quic/QuicheSendInfo.java @@ -16,10 +16,13 @@ package io.netty.incubator.codec.quic; import io.netty.util.concurrent.FastThreadLocal; -import io.netty.util.internal.PlatformDependent; import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +/** + * Utility class to handle access to {@code quiche_send_info}. + */ final class QuicheSendInfo { private static final FastThreadLocal IPV4_ARRAYS = new FastThreadLocal() { @@ -39,38 +42,45 @@ protected byte[] initialValue() { private QuicheSendInfo() { } /** - * Read the {@link InetSocketAddress} out of the {@code quiche_send_info} struct. + * Get the {@link InetSocketAddress} out of the {@code quiche_send_info} struct. * - * @param memory the memory address of {@code quiche_send_info}. + * @param memory the memory of {@code quiche_send_info}. * @return the address that was read. */ - static InetSocketAddress read(long memory) { - long to = memory + Quiche.QUICHE_SEND_INFO_OFFSETOF_TO; - long len = readLen(memory + Quiche.QUICHE_SEND_INFO_OFFSETOF_TO_LEN); - if (len == Quiche.SIZEOF_SOCKADDR_IN) { - return SockaddrIn.readIPv4(to, IPV4_ARRAYS.get()); + static InetSocketAddress getAddress(ByteBuffer memory) { + int position = memory.position(); + try { + long len = getLen(memory, position + Quiche.QUICHE_SEND_INFO_OFFSETOF_TO_LEN); + + memory.position(position + Quiche.QUICHE_SEND_INFO_OFFSETOF_TO); + + if (len == Quiche.SIZEOF_SOCKADDR_IN) { + return SockaddrIn.getIPv4(memory, IPV4_ARRAYS.get()); + } + assert len == Quiche.SIZEOF_SOCKADDR_IN6; + return SockaddrIn.getIPv6(memory, IPV6_ARRAYS.get(), IPV4_ARRAYS.get()); + } finally { + memory.position(position); } - assert len == Quiche.SIZEOF_SOCKADDR_IN6; - return SockaddrIn.readIPv6(to, IPV6_ARRAYS.get(), IPV4_ARRAYS.get()); } - private static long readLen(long address) { + private static long getLen(ByteBuffer memory, int index) { switch (Quiche.SIZEOF_SOCKLEN_T) { case 1: - return PlatformDependent.getByte(address); + return memory.get(index); case 2: - return PlatformDependent.getShort(address); + return memory.getShort(index); case 4: - return PlatformDependent.getInt(address); + return memory.getInt(index); case 8: - return PlatformDependent.getLong(address); + return memory.getLong(index); default: throw new IllegalStateException(); } } /** - * Write the {@link InetSocketAddress} into the {@code quiche_send_info} struct. + * Set the {@link InetSocketAddress} into the {@code quiche_send_info} struct. *
      *
      * typedef struct {
@@ -80,36 +90,47 @@ private static long readLen(long address) {
      * } quiche_send_info;
      * 
* - * @param memory the memory address of {@code quiche_send_info}. + * @param memory the memory of {@code quiche_send_info}. * @param address the {@link InetSocketAddress} to write into {@code quiche_send_info}. */ - static void write(long memory, InetSocketAddress address) { - long sockaddr = sockAddress(memory); - int len = SockaddrIn.write(sockaddr, address); - switch (Quiche.SIZEOF_SOCKLEN_T) { - case 1: - PlatformDependent.putByte(memory + Quiche.QUICHE_SEND_INFO_OFFSETOF_TO_LEN, (byte) len); - break; - case 2: - PlatformDependent.putShort(memory + Quiche.QUICHE_SEND_INFO_OFFSETOF_TO_LEN, (short) len); - break; - case 4: - PlatformDependent.putInt(memory + Quiche.QUICHE_SEND_INFO_OFFSETOF_TO_LEN, len); - break; - case 8: - PlatformDependent.putLong(memory + Quiche.QUICHE_SEND_INFO_OFFSETOF_TO_LEN, len); - break; - default: - throw new IllegalStateException(); + static void setSendInfo(ByteBuffer memory, InetSocketAddress address) { + int position = memory.position(); + int sockaddrPosition = position + Quiche.QUICHE_SEND_INFO_OFFSETOF_TO; + try { + memory.position(sockaddrPosition); + int len = SockaddrIn.setAddress(memory, address); + switch (Quiche.SIZEOF_SOCKLEN_T) { + case 1: + memory.put(position + Quiche.QUICHE_SEND_INFO_OFFSETOF_TO_LEN, (byte) len); + break; + case 2: + memory.putShort(position + Quiche.QUICHE_SEND_INFO_OFFSETOF_TO_LEN, (short) len); + break; + case 4: + memory.putInt(position + Quiche.QUICHE_SEND_INFO_OFFSETOF_TO_LEN, len); + break; + case 8: + memory.putLong(position + Quiche.QUICHE_SEND_INFO_OFFSETOF_TO_LEN, len); + break; + default: + throw new IllegalStateException(); + } + } finally { + memory.position(position); } } /** - * Return the memory address of the {@code sockaddr_storage} that is contained in {@code quiche_send_info}. - * @param memory the memory address of {@code quiche_send_info}. - * @return the memory address of the {@code sockaddr_storage}. + * Returns {@code true} if both {@link ByteBuffer}s have the same {@code sockaddr_storage} stored. + * + * @param memory the first {@link ByteBuffer} which holds a {@code quiche_send_info}. + * @param memory2 the second {@link ByteBuffer} which holds a {@code quiche_send_info}. + * @return {@code true} if both {@link ByteBuffer}s have the same {@code sockaddr_storage} stored, + * {@code false} otherwise. */ - static long sockAddress(long memory) { - return memory + Quiche.QUICHE_SEND_INFO_OFFSETOF_TO; + static boolean isSameAddress(ByteBuffer memory, ByteBuffer memory2) { + long address1 = Quiche.memoryAddressWithPosition(memory) + Quiche.QUICHE_SEND_INFO_OFFSETOF_TO; + long address2 = Quiche.memoryAddressWithPosition(memory2) + Quiche.QUICHE_SEND_INFO_OFFSETOF_TO; + return SockaddrIn.cmp(address1, address2) == 0; } } diff --git a/src/main/java/io/netty/incubator/codec/quic/SockaddrIn.java b/src/main/java/io/netty/incubator/codec/quic/SockaddrIn.java index 2219e8fdb..8914c7b28 100644 --- a/src/main/java/io/netty/incubator/codec/quic/SockaddrIn.java +++ b/src/main/java/io/netty/incubator/codec/quic/SockaddrIn.java @@ -22,14 +22,15 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; - -import static io.netty.util.internal.PlatformDependent.BIG_ENDIAN_NATIVE_ORDER; +import java.nio.ByteBuffer; final class SockaddrIn { static final byte[] IPV4_MAPPED_IPV6_PREFIX = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xff, (byte) 0xff }; static final int IPV4_ADDRESS_LENGTH = 4; static final int IPV6_ADDRESS_LENGTH = 16; + static final byte[] SOCKADDR_IN6_EMPTY_ARRAY = new byte[Quiche.SIZEOF_SOCKADDR_IN6]; + static final byte[] SOCKADDR_IN_EMPTY_ARRAY = new byte[Quiche.SIZEOF_SOCKADDR_IN]; private SockaddrIn() { } @@ -37,16 +38,16 @@ static int cmp(long memory, long memory2) { return Quiche.sockaddr_cmp(memory, memory2); } - static int write(long memory, InetSocketAddress address) { + static int setAddress(ByteBuffer memory, InetSocketAddress address) { InetAddress addr = address.getAddress(); - return write(addr instanceof Inet6Address, memory, address); + return setAddress(addr instanceof Inet6Address, memory, address); } - static int write(boolean ipv6, long memory, InetSocketAddress address) { + static int setAddress(boolean ipv6, ByteBuffer memory, InetSocketAddress address) { if (ipv6) { - return SockaddrIn.writeIPv6(memory, address.getAddress(), address.getPort()); + return SockaddrIn.setIPv6(memory, address.getAddress(), address.getPort()); } else { - return SockaddrIn.writeIPv4(memory, address.getAddress(), address.getPort()); + return SockaddrIn.setIPv4(memory, address.getAddress(), address.getPort()); } } @@ -64,21 +65,28 @@ static int write(boolean ipv6, long memory, InetSocketAddress address) { * }; * */ - static int writeIPv4(long memory, InetAddress address, int port) { - PlatformDependent.setMemory(memory, Quiche.SIZEOF_SOCKADDR_IN, (byte) 0); - - PlatformDependent.putShort(memory + Quiche.SOCKADDR_IN_OFFSETOF_SIN_FAMILY, Quiche.AF_INET); - PlatformDependent.putShort(memory + Quiche.SOCKADDR_IN_OFFSETOF_SIN_PORT, handleNetworkOrder((short) port)); - byte[] bytes = address.getAddress(); - int offset = 0; - if (bytes.length == IPV6_ADDRESS_LENGTH) { - // IPV6 mapped IPV4 address, we only need the last 4 bytes. - offset = IPV4_MAPPED_IPV6_PREFIX.length; + static int setIPv4(ByteBuffer memory, InetAddress address, int port) { + int position = memory.position(); + try { + // memset + memory.put(SOCKADDR_IN_EMPTY_ARRAY); + + memory.putShort(position + Quiche.SOCKADDR_IN_OFFSETOF_SIN_FAMILY, Quiche.AF_INET); + memory.putShort(position + Quiche.SOCKADDR_IN_OFFSETOF_SIN_PORT, (short) port); + + byte[] bytes = address.getAddress(); + int offset = 0; + if (bytes.length == IPV6_ADDRESS_LENGTH) { + // IPV6 mapped IPV4 address, we only need the last 4 bytes. + offset = IPV4_MAPPED_IPV6_PREFIX.length; + } + assert bytes.length == offset + IPV4_ADDRESS_LENGTH; + memory.position(position + Quiche.SOCKADDR_IN_OFFSETOF_SIN_ADDR + Quiche.IN_ADDRESS_OFFSETOF_S_ADDR); + memory.put(bytes, offset, IPV4_ADDRESS_LENGTH); + return Quiche.SIZEOF_SOCKADDR_IN; + } finally { + memory.position(position); } - assert bytes.length == offset + 4; - PlatformDependent.copyMemory(bytes, offset, - memory + Quiche.SOCKADDR_IN_OFFSETOF_SIN_ADDR + Quiche.IN_ADDRESS_OFFSETOF_S_ADDR, 4); - return Quiche.SIZEOF_SOCKADDR_IN; } /** @@ -94,69 +102,86 @@ static int writeIPv4(long memory, InetAddress address, int port) { * unsigned char s6_addr[16]; // IPv6 address * }; */ - static int writeIPv6(long memory, InetAddress address, int port) { - PlatformDependent.setMemory(memory, Quiche.SIZEOF_SOCKADDR_IN6, (byte) 0); - PlatformDependent.putShort(memory + Quiche.SOCKADDR_IN6_OFFSETOF_SIN6_FAMILY, Quiche.AF_INET6); - PlatformDependent.putShort(memory + Quiche.SOCKADDR_IN6_OFFSETOF_SIN6_PORT, handleNetworkOrder((short) port)); - // Skip sin6_flowinfo as we did memset before - byte[] bytes = address.getAddress(); - if (bytes.length == IPV4_ADDRESS_LENGTH) { + static int setIPv6(ByteBuffer memory, InetAddress address, int port) { + int position = memory.position(); + try { + // memset + memory.put(SOCKADDR_IN6_EMPTY_ARRAY); + + memory.putShort(position + Quiche.SOCKADDR_IN6_OFFSETOF_SIN6_FAMILY, Quiche.AF_INET6); + memory.putShort(position + Quiche.SOCKADDR_IN6_OFFSETOF_SIN6_PORT, (short) port); + + // Skip sin6_flowinfo as we did memset before + byte[] bytes = address.getAddress(); int offset = Quiche.SOCKADDR_IN6_OFFSETOF_SIN6_ADDR + Quiche.IN6_ADDRESS_OFFSETOF_S6_ADDR; - PlatformDependent.copyMemory(IPV4_MAPPED_IPV6_PREFIX, 0, memory + offset, IPV4_MAPPED_IPV6_PREFIX.length); - PlatformDependent.copyMemory(bytes, 0, - memory + offset + IPV4_MAPPED_IPV6_PREFIX.length, IPV4_ADDRESS_LENGTH); - // Skip sin6_scope_id as we did memset before - } else { - PlatformDependent.copyMemory( - bytes, 0, memory + Quiche.SOCKADDR_IN6_OFFSETOF_SIN6_ADDR + Quiche.IN6_ADDRESS_OFFSETOF_S6_ADDR, - IPV6_ADDRESS_LENGTH); - PlatformDependent.putInt( - memory + Quiche.SOCKADDR_IN6_OFFSETOF_SIN6_SCOPE_ID, ((Inet6Address) address).getScopeId()); + + if (bytes.length == IPV4_ADDRESS_LENGTH) { + memory.position(position + offset); + memory.put(IPV4_MAPPED_IPV6_PREFIX); + + memory.position(position + offset + IPV4_MAPPED_IPV6_PREFIX.length); + memory.put(bytes, 0, IPV4_ADDRESS_LENGTH); + + // Skip sin6_scope_id as we did memset before + } else { + memory.position(position + offset); + memory.put(bytes, 0, IPV6_ADDRESS_LENGTH); + + memory.putInt(position + Quiche.SOCKADDR_IN6_OFFSETOF_SIN6_SCOPE_ID, + ((Inet6Address) address).getScopeId()); + } + return Quiche.SIZEOF_SOCKADDR_IN6; + } finally { + memory.position(position); } - return Quiche.SIZEOF_SOCKADDR_IN6; } - static InetSocketAddress readIPv4(long memory, byte[] tmpArray) { + static InetSocketAddress getIPv4(ByteBuffer memory, byte[] tmpArray) { assert tmpArray.length == IPV4_ADDRESS_LENGTH; - int port = handleNetworkOrder(PlatformDependent.getShort( - memory + Quiche.SOCKADDR_IN_OFFSETOF_SIN_PORT)) & 0xFFFF; - PlatformDependent.copyMemory(memory + Quiche.SOCKADDR_IN_OFFSETOF_SIN_ADDR + Quiche.IN_ADDRESS_OFFSETOF_S_ADDR, - tmpArray, 0, IPV4_ADDRESS_LENGTH); + int position = memory.position(); + try { - return new InetSocketAddress(InetAddress.getByAddress(tmpArray), port); - } catch (UnknownHostException ignore) { - return null; + int port = memory.getShort(position + Quiche.SOCKADDR_IN_OFFSETOF_SIN_PORT) & 0xFFFF; + memory.position(position + Quiche.SOCKADDR_IN_OFFSETOF_SIN_ADDR + Quiche.IN_ADDRESS_OFFSETOF_S_ADDR); + memory.get(tmpArray); + try { + return new InetSocketAddress(InetAddress.getByAddress(tmpArray), port); + } catch (UnknownHostException ignore) { + return null; + } + } finally { + memory.position(position); } } - static InetSocketAddress readIPv6(long memory, byte[] ipv6Array, byte[] ipv4Array) { + static InetSocketAddress getIPv6(ByteBuffer memory, byte[] ipv6Array, byte[] ipv4Array) { assert ipv6Array.length == IPV6_ADDRESS_LENGTH; assert ipv4Array.length == IPV4_ADDRESS_LENGTH; + int position = memory.position(); - int port = handleNetworkOrder(PlatformDependent.getShort( - memory + Quiche.SOCKADDR_IN6_OFFSETOF_SIN6_PORT)) & 0xFFFF; - PlatformDependent.copyMemory( - memory + Quiche.SOCKADDR_IN6_OFFSETOF_SIN6_ADDR + Quiche.IN6_ADDRESS_OFFSETOF_S6_ADDR, - ipv6Array, 0, IPV6_ADDRESS_LENGTH); - if (PlatformDependent.equals( - ipv6Array, 0, IPV4_MAPPED_IPV6_PREFIX, 0, IPV4_MAPPED_IPV6_PREFIX.length)) { - System.arraycopy(ipv6Array, IPV4_MAPPED_IPV6_PREFIX.length, ipv4Array, 0, IPV4_ADDRESS_LENGTH); - try { - return new InetSocketAddress(Inet4Address.getByAddress(ipv4Array), port); - } catch (UnknownHostException ignore) { - return null; - } - } else { - int scopeId = PlatformDependent.getInt(memory + Quiche.SOCKADDR_IN6_OFFSETOF_SIN6_SCOPE_ID); - try { - return new InetSocketAddress(Inet6Address.getByAddress(null, ipv6Array, scopeId), port); - } catch (UnknownHostException ignore) { - return null; + try { + int port = memory.getShort( + position + Quiche.SOCKADDR_IN6_OFFSETOF_SIN6_PORT) & 0xFFFF; + memory.position(position + Quiche.SOCKADDR_IN6_OFFSETOF_SIN6_ADDR + Quiche.IN6_ADDRESS_OFFSETOF_S6_ADDR); + memory.get(ipv6Array); + if (PlatformDependent.equals( + ipv6Array, 0, IPV4_MAPPED_IPV6_PREFIX, 0, IPV4_MAPPED_IPV6_PREFIX.length)) { + System.arraycopy(ipv6Array, IPV4_MAPPED_IPV6_PREFIX.length, ipv4Array, 0, IPV4_ADDRESS_LENGTH); + try { + return new InetSocketAddress(Inet4Address.getByAddress(ipv4Array), port); + } catch (UnknownHostException ignore) { + return null; + } + } else { + int scopeId = memory.getInt(position + Quiche.SOCKADDR_IN6_OFFSETOF_SIN6_SCOPE_ID); + try { + return new InetSocketAddress(Inet6Address.getByAddress(null, ipv6Array, scopeId), port); + } catch (UnknownHostException ignore) { + return null; + } } + } finally { + memory.position(position); } } - - private static short handleNetworkOrder(short v) { - return BIG_ENDIAN_NATIVE_ORDER ? v : Short.reverseBytes(v); - } }