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.channel.ChannelHandler;
19
20 import java.util.Objects;
21 import java.util.concurrent.Executor;
22 import java.util.concurrent.TimeUnit;
23 import java.util.function.Function;
24
25 import static io.netty.util.internal.ObjectUtil.checkInRange;
26 import static io.netty.util.internal.ObjectUtil.checkPositive;
27 import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
28
29
30
31
32
33
34 public abstract class QuicCodecBuilder<B extends QuicCodecBuilder<B>> {
35 private final boolean server;
36 private Boolean grease;
37 private Long maxIdleTimeout;
38 private Long maxRecvUdpPayloadSize;
39 private Long maxSendUdpPayloadSize;
40 private Long initialMaxData;
41 private Long initialMaxStreamDataBidiLocal;
42 private Long initialMaxStreamDataBidiRemote;
43 private Long initialMaxStreamDataUni;
44 private Long initialMaxStreamsBidi;
45 private Long initialMaxStreamsUni;
46 private Long ackDelayExponent;
47 private Long maxAckDelay;
48 private Boolean disableActiveMigration;
49 private Boolean enableHystart;
50 private QuicCongestionControlAlgorithm congestionControlAlgorithm;
51 private Integer initialCongestionWindowPackets;
52 private int localConnIdLength;
53 private Function<QuicChannel, ? extends QuicSslEngine> sslEngineProvider;
54 private FlushStrategy flushStrategy = FlushStrategy.DEFAULT;
55 private Integer recvQueueLen;
56 private Integer sendQueueLen;
57 private Long activeConnectionIdLimit;
58 private byte[] statelessResetToken;
59
60 private Executor sslTaskExecutor;
61
62
63 int version;
64
65 QuicCodecBuilder(boolean server) {
66 Quic.ensureAvailability();
67 this.version = Quiche.QUICHE_PROTOCOL_VERSION;
68 this.localConnIdLength = Quiche.QUICHE_MAX_CONN_ID_LEN;
69 this.server = server;
70 }
71
72 QuicCodecBuilder(QuicCodecBuilder<B> builder) {
73 Quic.ensureAvailability();
74 this.server = builder.server;
75 this.grease = builder.grease;
76 this.maxIdleTimeout = builder.maxIdleTimeout;
77 this.maxRecvUdpPayloadSize = builder.maxRecvUdpPayloadSize;
78 this.maxSendUdpPayloadSize = builder.maxSendUdpPayloadSize;
79 this.initialMaxData = builder.initialMaxData;
80 this.initialMaxStreamDataBidiLocal = builder.initialMaxStreamDataBidiLocal;
81 this.initialMaxStreamDataBidiRemote = builder.initialMaxStreamDataBidiRemote;
82 this.initialMaxStreamDataUni = builder.initialMaxStreamDataUni;
83 this.initialMaxStreamsBidi = builder.initialMaxStreamsBidi;
84 this.initialMaxStreamsUni = builder.initialMaxStreamsUni;
85 this.ackDelayExponent = builder.ackDelayExponent;
86 this.maxAckDelay = builder.maxAckDelay;
87 this.disableActiveMigration = builder.disableActiveMigration;
88 this.enableHystart = builder.enableHystart;
89 this.congestionControlAlgorithm = builder.congestionControlAlgorithm;
90 this.initialCongestionWindowPackets = builder.initialCongestionWindowPackets;
91 this.localConnIdLength = builder.localConnIdLength;
92 this.sslEngineProvider = builder.sslEngineProvider;
93 this.flushStrategy = builder.flushStrategy;
94 this.recvQueueLen = builder.recvQueueLen;
95 this.sendQueueLen = builder.sendQueueLen;
96 this.activeConnectionIdLimit = builder.activeConnectionIdLimit;
97 this.statelessResetToken = builder.statelessResetToken;
98 this.sslTaskExecutor = builder.sslTaskExecutor;
99 this.version = builder.version;
100 }
101
102
103
104
105
106
107 @SuppressWarnings("unchecked")
108 protected final B self() {
109 return (B) this;
110 }
111
112
113
114
115
116
117
118
119 public final B flushStrategy(FlushStrategy flushStrategy) {
120 this.flushStrategy = Objects.requireNonNull(flushStrategy, "flushStrategy");
121 return self();
122 }
123
124
125
126
127
128
129
130
131
132 public final B congestionControlAlgorithm(QuicCongestionControlAlgorithm congestionControlAlgorithm) {
133 this.congestionControlAlgorithm = congestionControlAlgorithm;
134 return self();
135 }
136
137
138
139
140
141
142
143
144
145 public final B initialCongestionWindowPackets(int numPackets) {
146 this.initialCongestionWindowPackets = numPackets;
147 return self();
148 }
149
150
151
152
153
154
155
156
157
158
159 public final B grease(boolean enable) {
160 grease = enable;
161 return self();
162 }
163
164
165
166
167
168
169
170
171
172
173
174 public final B maxIdleTimeout(long amount, TimeUnit unit) {
175 this.maxIdleTimeout = unit.toMillis(checkPositiveOrZero(amount, "amount"));
176 return self();
177 }
178
179
180
181
182
183
184
185
186
187
188 public final B maxSendUdpPayloadSize(long size) {
189 this.maxSendUdpPayloadSize = checkPositiveOrZero(size, "value");
190 return self();
191 }
192
193
194
195
196
197
198
199
200
201
202 public final B maxRecvUdpPayloadSize(long size) {
203 this.maxRecvUdpPayloadSize = checkPositiveOrZero(size, "value");
204 return self();
205 }
206
207
208
209
210
211
212
213
214
215
216 public final B initialMaxData(long value) {
217 this.initialMaxData = checkPositiveOrZero(value, "value");
218 return self();
219 }
220
221
222
223
224
225
226
227
228
229
230
231 public final B initialMaxStreamDataBidirectionalLocal(long value) {
232 this.initialMaxStreamDataBidiLocal = checkPositiveOrZero(value, "value");
233 return self();
234 }
235
236
237
238
239
240
241
242
243
244
245
246 public final B initialMaxStreamDataBidirectionalRemote(long value) {
247 this.initialMaxStreamDataBidiRemote = checkPositiveOrZero(value, "value");
248 return self();
249 }
250
251
252
253
254
255
256
257
258
259
260
261 public final B initialMaxStreamDataUnidirectional(long value) {
262 this.initialMaxStreamDataUni = checkPositiveOrZero(value, "value");
263 return self();
264 }
265
266
267
268
269
270
271
272
273
274
275
276 public final B initialMaxStreamsBidirectional(long value) {
277 this.initialMaxStreamsBidi = checkPositiveOrZero(value, "value");
278 return self();
279 }
280
281
282
283
284
285
286
287
288
289
290
291 public final B initialMaxStreamsUnidirectional(long value) {
292 this.initialMaxStreamsUni = checkPositiveOrZero(value, "value");
293 return self();
294 }
295
296
297
298
299
300
301
302
303
304
305
306 public final B ackDelayExponent(long value) {
307 this.ackDelayExponent = checkPositiveOrZero(value, "value");
308 return self();
309 }
310
311
312
313
314
315
316
317
318
319
320
321
322 public final B maxAckDelay(long amount, TimeUnit unit) {
323 this.maxAckDelay = unit.toMillis(checkPositiveOrZero(amount, "amount"));
324 return self();
325 }
326
327
328
329
330
331
332
333
334
335
336
337 public final B activeMigration(boolean enable) {
338 this.disableActiveMigration = !enable;
339 return self();
340 }
341
342
343
344
345
346
347
348
349
350
351
352 public final B hystart(boolean enable) {
353 this.enableHystart = enable;
354 return self();
355 }
356
357
358
359
360
361
362
363
364
365 public final B localConnectionIdLength(int value) {
366 this.localConnIdLength = checkInRange(value, 0, Quiche.QUICHE_MAX_CONN_ID_LEN, "value");
367 return self();
368 }
369
370
371
372
373
374
375
376
377
378 public final B version(int version) {
379 this.version = version;
380 return self();
381 }
382
383
384
385
386
387
388
389
390 public final B datagram(int recvQueueLen, int sendQueueLen) {
391 checkPositive(recvQueueLen, "recvQueueLen");
392 checkPositive(sendQueueLen, "sendQueueLen");
393
394 this.recvQueueLen = recvQueueLen;
395 this.sendQueueLen = sendQueueLen;
396 return self();
397 }
398
399
400
401
402
403
404
405
406
407 public final B sslContext(QuicSslContext sslContext) {
408 if (server != sslContext.isServer()) {
409 throw new IllegalArgumentException("QuicSslContext.isServer() " + sslContext.isServer()
410 + " isn't supported by this builder");
411 }
412 return sslEngineProvider(q -> sslContext.newEngine(q.alloc()));
413 }
414
415
416
417
418
419
420
421
422 public final B sslEngineProvider(Function<QuicChannel, ? extends QuicSslEngine> sslEngineProvider) {
423 this.sslEngineProvider = sslEngineProvider;
424 return self();
425 }
426
427
428
429
430
431
432
433 public final B sslTaskExecutor(Executor sslTaskExecutor) {
434 this.sslTaskExecutor = sslTaskExecutor;
435 return self();
436 }
437
438
439
440
441
442
443
444 public final B activeConnectionIdLimit(long limit) {
445 checkPositive(limit, "limit");
446 activeConnectionIdLimit = limit;
447 return self();
448 }
449
450
451
452
453
454
455
456 public final B statelessResetToken(byte[] token) {
457 if (token.length != 16) {
458 throw new IllegalArgumentException("token must be 16 bytes but was " + token.length);
459 }
460
461 this.statelessResetToken = token.clone();
462 return self();
463 }
464
465 private QuicheConfig createConfig() {
466 return new QuicheConfig(version, grease,
467 maxIdleTimeout, maxSendUdpPayloadSize, maxRecvUdpPayloadSize, initialMaxData,
468 initialMaxStreamDataBidiLocal, initialMaxStreamDataBidiRemote,
469 initialMaxStreamDataUni, initialMaxStreamsBidi, initialMaxStreamsUni,
470 ackDelayExponent, maxAckDelay, disableActiveMigration, enableHystart,
471 congestionControlAlgorithm, initialCongestionWindowPackets, recvQueueLen, sendQueueLen,
472 activeConnectionIdLimit, statelessResetToken);
473 }
474
475
476
477
478 protected void validate() {
479 if (sslEngineProvider == null) {
480 throw new IllegalStateException("sslEngineProvider can't be null");
481 }
482 }
483
484
485
486
487
488
489
490 public final ChannelHandler build() {
491 validate();
492 QuicheConfig config = createConfig();
493 try {
494 return build(config, sslEngineProvider, sslTaskExecutor, localConnIdLength, flushStrategy);
495 } catch (Throwable cause) {
496 config.free();
497 throw cause;
498 }
499 }
500
501
502
503
504
505
506 public abstract B clone();
507
508
509
510
511
512
513
514
515
516
517
518 abstract ChannelHandler build(QuicheConfig config,
519 Function<QuicChannel, ? extends QuicSslEngine> sslContextProvider,
520 Executor sslTaskExecutor,
521 int localConnIdLength, FlushStrategy flushStrategy);
522 }