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 volatile long connection;
61
62
63
64 private int runningSslTasks;
65
66
67
68 private volatile boolean freePending;
69
70 QuicheQuicConnection(long connection, long ssl, QuicheQuicSslEngine engine, ReferenceCounted refCnt) {
71 assert connection != -1;
72 this.connection = connection;
73 this.ssl = ssl;
74 this.engine = engine;
75 this.refCnt = refCnt;
76
77 recvInfoBuffer = Quiche.allocateNativeOrder(TOTAL_RECV_INFO_SIZE);
78 sendInfoBuffer = Quiche.allocateNativeOrder(2 * Quiche.SIZEOF_QUICHE_SEND_INFO);
79
80
81 recvInfoBuffer.setZero(0, recvInfoBuffer.capacity());
82 sendInfoBuffer.setZero(0, sendInfoBuffer.capacity());
83
84 recvInfoBuffer1 = recvInfoBuffer.nioBuffer(0, TOTAL_RECV_INFO_SIZE);
85 sendInfoBuffer1 = sendInfoBuffer.nioBuffer(0, Quiche.SIZEOF_QUICHE_SEND_INFO);
86 sendInfoBuffer2 = sendInfoBuffer.nioBuffer(Quiche.SIZEOF_QUICHE_SEND_INFO, Quiche.SIZEOF_QUICHE_SEND_INFO);
87 this.engine.connection = this;
88 leakTracker = leakDetector.track(this);
89 }
90
91 synchronized void reattach(ReferenceCounted refCnt) {
92 this.refCnt.release();
93 this.refCnt = refCnt;
94 }
95
96 void free() {
97 free(true);
98 }
99
100 boolean isFreed() {
101
102 return connection == -1 || freePending;
103 }
104
105 private void free(boolean closeLeakTracker) {
106 boolean release = false;
107 synchronized (this) {
108 if (connection != -1) {
109 if (runningSslTasks > 0) {
110
111
112 freePending = true;
113 return;
114 }
115 try {
116 BoringSSL.SSL_cleanup(ssl);
117 Quiche.quiche_conn_free(connection);
118 engine.ctx.remove(engine);
119 release = true;
120 refCnt.release();
121 } finally {
122 connection = -1;
123 }
124 }
125 }
126 if (release) {
127 recvInfoBuffer.release();
128 sendInfoBuffer.release();
129 if (closeLeakTracker && leakTracker != null) {
130 leakTracker.close(this);
131 }
132 }
133 }
134
135 @Nullable
136 Runnable sslTask() {
137 final Runnable task;
138 synchronized (this) {
139 if (connection != -1 && !freePending) {
140 task = BoringSSL.SSL_getTask(ssl);
141 } else {
142 task = null;
143 }
144 }
145 if (task == null) {
146 return null;
147 }
148
149 return () -> {
150 synchronized (this) {
151 if (connection == -1 || freePending) {
152 return;
153 }
154
155
156
157 runningSslTasks++;
158 }
159 try {
160 task.run();
161 } finally {
162 boolean free;
163 synchronized (this) {
164 free = --runningSslTasks == 0 && freePending;
165 }
166 if (free) {
167 free(true);
168 }
169 }
170 };
171 }
172
173 @Nullable
174 QuicConnectionAddress sourceId() {
175 return connectionId(() -> Quiche.quiche_conn_source_id(connection));
176 }
177
178 @Nullable
179 QuicConnectionAddress destinationId() {
180 return connectionId(() -> Quiche.quiche_conn_destination_id(connection));
181 }
182
183 @Nullable
184 QuicConnectionAddress connectionId(Supplier<byte[]> idSupplier) {
185 final byte[] id;
186 synchronized (this) {
187 if (connection == -1) {
188 return null;
189 }
190 id = idSupplier.get();
191 }
192 return id == null ? QuicConnectionAddress.NULL_LEN : new QuicConnectionAddress(id);
193 }
194
195 @Nullable
196 QuicheQuicTransportParameters peerParameters() {
197 final long[] ret;
198 synchronized (this) {
199 if (connection == -1) {
200 return null;
201 }
202 ret = Quiche.quiche_conn_peer_transport_params(connection);
203 }
204 if (ret == null) {
205 return null;
206 }
207 return new QuicheQuicTransportParameters(ret);
208 }
209
210 QuicheQuicSslEngine engine() {
211 return engine;
212 }
213
214 long address() {
215 assert connection != -1;
216 return connection;
217 }
218
219 void init(InetSocketAddress local, InetSocketAddress remote, Consumer<String> sniSelectedCallback) {
220 assert connection != -1;
221 assert recvInfoBuffer.refCnt() != 0;
222 assert sendInfoBuffer.refCnt() != 0;
223
224
225 QuicheRecvInfo.setRecvInfo(recvInfoBuffer1, remote, local);
226
227
228 QuicheSendInfo.setSendInfo(sendInfoBuffer1, local, remote);
229 QuicheSendInfo.setSendInfo(sendInfoBuffer2, local, remote);
230 engine.sniSelectedCallback = sniSelectedCallback;
231 }
232
233 ByteBuffer nextRecvInfo() {
234 assert recvInfoBuffer.refCnt() != 0;
235 return recvInfoBuffer1;
236 }
237
238 ByteBuffer nextSendInfo() {
239 assert sendInfoBuffer.refCnt() != 0;
240 sendInfoFirst = !sendInfoFirst;
241 return sendInfoFirst ? sendInfoBuffer1 : sendInfoBuffer2;
242 }
243
244 boolean isSendInfoChanged() {
245 assert sendInfoBuffer.refCnt() != 0;
246 return !QuicheSendInfo.isSameAddress(sendInfoBuffer1, sendInfoBuffer2);
247 }
248
249 boolean isClosed() {
250 return isFreed() || Quiche.quiche_conn_is_closed(connection);
251 }
252
253
254
255 @Override
256 protected void finalize() throws Throwable {
257 try {
258 free(false);
259 } finally {
260 super.finalize();
261 }
262 }
263 }