1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.quic;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.util.ReferenceCounted;
20 import io.netty.util.ResourceLeakDetector;
21 import io.netty.util.ResourceLeakDetectorFactory;
22 import io.netty.util.ResourceLeakTracker;
23 import org.jetbrains.annotations.Nullable;
24
25 import java.net.InetSocketAddress;
26 import java.nio.ByteBuffer;
27 import java.util.function.Consumer;
28 import java.util.function.Supplier;
29
30 final class QuicheQuicConnection {
31 private static final int TOTAL_RECV_INFO_SIZE = Quiche.SIZEOF_QUICHE_RECV_INFO +
32 Quiche.SIZEOF_SOCKADDR_STORAGE + Quiche.SIZEOF_SOCKADDR_STORAGE;
33 private static final ResourceLeakDetector<QuicheQuicConnection> leakDetector =
34 ResourceLeakDetectorFactory.instance().newResourceLeakDetector(QuicheQuicConnection.class);
35 private final QuicheQuicSslEngine engine;
36
37 private final ResourceLeakTracker<QuicheQuicConnection> leakTracker;
38
39 final long ssl;
40 private ReferenceCounted refCnt;
41
42
43
44
45
46
47
48
49
50
51
52 private final ByteBuf recvInfoBuffer;
53 private final ByteBuf sendInfoBuffer;
54
55 private boolean sendInfoFirst = true;
56 private final ByteBuffer recvInfoBuffer1;
57 private final ByteBuffer sendInfoBuffer1;
58 private final ByteBuffer sendInfoBuffer2;
59
60 private long connection;
61
62 QuicheQuicConnection(long connection, long ssl, QuicheQuicSslEngine engine, ReferenceCounted refCnt) {
63 assert connection != -1;
64 this.connection = connection;
65 this.ssl = ssl;
66 this.engine = engine;
67 this.refCnt = refCnt;
68
69 recvInfoBuffer = Quiche.allocateNativeOrder(TOTAL_RECV_INFO_SIZE);
70 sendInfoBuffer = Quiche.allocateNativeOrder(2 * Quiche.SIZEOF_QUICHE_SEND_INFO);
71
72
73 recvInfoBuffer.setZero(0, recvInfoBuffer.capacity());
74 sendInfoBuffer.setZero(0, sendInfoBuffer.capacity());
75
76 recvInfoBuffer1 = recvInfoBuffer.nioBuffer(0, TOTAL_RECV_INFO_SIZE);
77 sendInfoBuffer1 = sendInfoBuffer.nioBuffer(0, Quiche.SIZEOF_QUICHE_SEND_INFO);
78 sendInfoBuffer2 = sendInfoBuffer.nioBuffer(Quiche.SIZEOF_QUICHE_SEND_INFO, Quiche.SIZEOF_QUICHE_SEND_INFO);
79 this.engine.connection = this;
80 leakTracker = leakDetector.track(this);
81 }
82
83 synchronized void reattach(ReferenceCounted refCnt) {
84 this.refCnt.release();
85 this.refCnt = refCnt;
86 }
87
88 void free() {
89 free(true);
90 }
91
92 boolean isFreed() {
93 return connection == -1;
94 }
95
96 private void free(boolean closeLeakTracker) {
97 boolean release = false;
98 synchronized (this) {
99 if (connection != -1) {
100 try {
101 BoringSSL.SSL_cleanup(ssl);
102 Quiche.quiche_conn_free(connection);
103 engine.ctx.remove(engine);
104 release = true;
105 refCnt.release();
106 } finally {
107 connection = -1;
108 }
109 }
110 }
111 if (release) {
112 recvInfoBuffer.release();
113 sendInfoBuffer.release();
114 if (closeLeakTracker && leakTracker != null) {
115 leakTracker.close(this);
116 }
117 }
118 }
119
120 @Nullable
121 Runnable sslTask() {
122 final Runnable task;
123 synchronized (this) {
124 if (connection != -1) {
125 task = BoringSSL.SSL_getTask(ssl);
126 } else {
127 task = null;
128 }
129 }
130 if (task == null) {
131 return null;
132 }
133
134 return () -> {
135 if (connection == -1) {
136 return;
137 }
138
139 task.run();
140 };
141 }
142
143 @Nullable
144 QuicConnectionAddress sourceId() {
145 return connectionId(() -> Quiche.quiche_conn_source_id(connection));
146 }
147
148 @Nullable
149 QuicConnectionAddress destinationId() {
150 return connectionId(() -> Quiche.quiche_conn_destination_id(connection));
151 }
152
153 @Nullable
154 QuicConnectionAddress connectionId(Supplier<byte[]> idSupplier) {
155 final byte[] id;
156 synchronized (this) {
157 if (connection == -1) {
158 return null;
159 }
160 id = idSupplier.get();
161 }
162 return id == null ? QuicConnectionAddress.NULL_LEN : new QuicConnectionAddress(id);
163 }
164
165 @Nullable
166 QuicheQuicTransportParameters peerParameters() {
167 final long[] ret;
168 synchronized (this) {
169 if (connection == -1) {
170 return null;
171 }
172 ret = Quiche.quiche_conn_peer_transport_params(connection);
173 }
174 if (ret == null) {
175 return null;
176 }
177 return new QuicheQuicTransportParameters(ret);
178 }
179
180 QuicheQuicSslEngine engine() {
181 return engine;
182 }
183
184 long address() {
185 assert connection != -1;
186 return connection;
187 }
188
189 void init(InetSocketAddress local, InetSocketAddress remote, Consumer<String> sniSelectedCallback) {
190 assert connection != -1;
191 assert recvInfoBuffer.refCnt() != 0;
192 assert sendInfoBuffer.refCnt() != 0;
193
194
195 QuicheRecvInfo.setRecvInfo(recvInfoBuffer1, remote, local);
196
197
198 QuicheSendInfo.setSendInfo(sendInfoBuffer1, local, remote);
199 QuicheSendInfo.setSendInfo(sendInfoBuffer2, local, remote);
200 engine.sniSelectedCallback = sniSelectedCallback;
201 }
202
203 ByteBuffer nextRecvInfo() {
204 assert recvInfoBuffer.refCnt() != 0;
205 return recvInfoBuffer1;
206 }
207
208 ByteBuffer nextSendInfo() {
209 assert sendInfoBuffer.refCnt() != 0;
210 sendInfoFirst = !sendInfoFirst;
211 return sendInfoFirst ? sendInfoBuffer1 : sendInfoBuffer2;
212 }
213
214 boolean isSendInfoChanged() {
215 assert sendInfoBuffer.refCnt() != 0;
216 return !QuicheSendInfo.isSameAddress(sendInfoBuffer1, sendInfoBuffer2);
217 }
218
219 boolean isClosed() {
220 return isFreed() || Quiche.quiche_conn_is_closed(connection);
221 }
222
223
224
225 @Override
226 protected void finalize() throws Throwable {
227 try {
228 free(false);
229 } finally {
230 super.finalize();
231 }
232 }
233 }