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