1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.handler.codec.http2;
17
18 import io.netty5.channel.Channel;
19 import io.netty5.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector;
20 import io.netty5.util.internal.UnstableApi;
21
22 import static io.netty5.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_LIST_SIZE;
23 import static io.netty5.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_RESERVED_STREAMS;
24 import static io.netty5.handler.codec.http2.Http2PromisedRequestVerifier.ALWAYS_VERIFY;
25 import static io.netty5.util.internal.ObjectUtil.checkPositiveOrZero;
26 import static java.util.Objects.requireNonNull;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73 @UnstableApi
74 public abstract class AbstractHttp2ConnectionHandlerBuilder<T extends Http2ConnectionHandler,
75 B extends AbstractHttp2ConnectionHandlerBuilder<T, B>> {
76
77 private static final SensitivityDetector DEFAULT_HEADER_SENSITIVITY_DETECTOR = Http2HeadersEncoder.NEVER_SENSITIVE;
78
79
80 private Http2Settings initialSettings = Http2Settings.defaultSettings();
81 private Http2FrameListener frameListener;
82 private long gracefulShutdownTimeoutMillis = Http2CodecUtil.DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT_MILLIS;
83 private boolean decoupleCloseAndGoAway;
84 private boolean flushPreface = true;
85
86
87
88 private Boolean isServer;
89 private Integer maxReservedStreams;
90
91
92 private Http2Connection connection;
93
94
95 private Http2ConnectionDecoder decoder;
96 private Http2ConnectionEncoder encoder;
97
98
99
100
101 private Boolean validateHeaders;
102 private Http2FrameLogger frameLogger;
103 private SensitivityDetector headerSensitivityDetector;
104 private Boolean encoderEnforceMaxConcurrentStreams;
105 private Boolean encoderIgnoreMaxHeaderListSize;
106 private Http2PromisedRequestVerifier promisedRequestVerifier = ALWAYS_VERIFY;
107 private boolean autoAckSettingsFrame = true;
108 private boolean autoAckPingFrame = true;
109 private int maxQueuedControlFrames = Http2CodecUtil.DEFAULT_MAX_QUEUED_CONTROL_FRAMES;
110 private int maxConsecutiveEmptyFrames = 2;
111
112
113
114
115 protected Http2Settings initialSettings() {
116 return initialSettings;
117 }
118
119
120
121
122 protected B initialSettings(Http2Settings settings) {
123 initialSettings = requireNonNull(settings, "settings");
124 return self();
125 }
126
127
128
129
130
131
132 protected Http2FrameListener frameListener() {
133 return frameListener;
134 }
135
136
137
138
139
140 protected B frameListener(Http2FrameListener frameListener) {
141 this.frameListener = requireNonNull(frameListener, "frameListener");
142 return self();
143 }
144
145
146
147
148
149 protected long gracefulShutdownTimeoutMillis() {
150 return gracefulShutdownTimeoutMillis;
151 }
152
153
154
155
156 protected B gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) {
157 if (gracefulShutdownTimeoutMillis < -1) {
158 throw new IllegalArgumentException("gracefulShutdownTimeoutMillis: " + gracefulShutdownTimeoutMillis +
159 " (expected: -1 for indefinite or >= 0)");
160 }
161 this.gracefulShutdownTimeoutMillis = gracefulShutdownTimeoutMillis;
162 return self();
163 }
164
165
166
167
168
169 protected boolean isServer() {
170 return isServer != null ? isServer : true;
171 }
172
173
174
175
176
177 protected B server(boolean isServer) {
178 enforceConstraint("server", "connection", connection);
179 enforceConstraint("server", "codec", decoder);
180 enforceConstraint("server", "codec", encoder);
181
182 this.isServer = isServer;
183 return self();
184 }
185
186
187
188
189
190
191
192
193 protected int maxReservedStreams() {
194 return maxReservedStreams != null ? maxReservedStreams : DEFAULT_MAX_RESERVED_STREAMS;
195 }
196
197
198
199
200 protected B maxReservedStreams(int maxReservedStreams) {
201 enforceConstraint("server", "connection", connection);
202 enforceConstraint("server", "codec", decoder);
203 enforceConstraint("server", "codec", encoder);
204
205 this.maxReservedStreams = checkPositiveOrZero(maxReservedStreams, "maxReservedStreams");
206 return self();
207 }
208
209
210
211
212
213
214 protected Http2Connection connection() {
215 return connection;
216 }
217
218
219
220
221 protected B connection(Http2Connection connection) {
222 enforceConstraint("connection", "maxReservedStreams", maxReservedStreams);
223 enforceConstraint("connection", "server", isServer);
224 enforceConstraint("connection", "codec", decoder);
225 enforceConstraint("connection", "codec", encoder);
226
227 this.connection = requireNonNull(connection, "connection");
228
229 return self();
230 }
231
232
233
234
235
236
237 protected Http2ConnectionDecoder decoder() {
238 return decoder;
239 }
240
241
242
243
244
245
246 protected Http2ConnectionEncoder encoder() {
247 return encoder;
248 }
249
250
251
252
253 protected B codec(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) {
254 enforceConstraint("codec", "server", isServer);
255 enforceConstraint("codec", "maxReservedStreams", maxReservedStreams);
256 enforceConstraint("codec", "connection", connection);
257 enforceConstraint("codec", "frameLogger", frameLogger);
258 enforceConstraint("codec", "validateHeaders", validateHeaders);
259 enforceConstraint("codec", "headerSensitivityDetector", headerSensitivityDetector);
260 enforceConstraint("codec", "encoderEnforceMaxConcurrentStreams", encoderEnforceMaxConcurrentStreams);
261
262 requireNonNull(decoder, "decoder");
263 requireNonNull(encoder, "encoder");
264
265 if (decoder.connection() != encoder.connection()) {
266 throw new IllegalArgumentException("The specified encoder and decoder have different connections.");
267 }
268
269 this.decoder = decoder;
270 this.encoder = encoder;
271
272 return self();
273 }
274
275
276
277
278
279 protected boolean isValidateHeaders() {
280 return validateHeaders != null ? validateHeaders : true;
281 }
282
283
284
285
286
287 protected B validateHeaders(boolean validateHeaders) {
288 enforceNonCodecConstraints("validateHeaders");
289 this.validateHeaders = validateHeaders;
290 return self();
291 }
292
293
294
295
296
297
298 protected Http2FrameLogger frameLogger() {
299 return frameLogger;
300 }
301
302
303
304
305 protected B frameLogger(Http2FrameLogger frameLogger) {
306 enforceNonCodecConstraints("frameLogger");
307 this.frameLogger = requireNonNull(frameLogger, "frameLogger");
308 return self();
309 }
310
311
312
313
314
315 protected boolean encoderEnforceMaxConcurrentStreams() {
316 return encoderEnforceMaxConcurrentStreams != null ? encoderEnforceMaxConcurrentStreams : false;
317 }
318
319
320
321
322
323 protected B encoderEnforceMaxConcurrentStreams(boolean encoderEnforceMaxConcurrentStreams) {
324 enforceNonCodecConstraints("encoderEnforceMaxConcurrentStreams");
325 this.encoderEnforceMaxConcurrentStreams = encoderEnforceMaxConcurrentStreams;
326 return self();
327 }
328
329
330
331
332
333
334
335
336 protected int encoderEnforceMaxQueuedControlFrames() {
337 return maxQueuedControlFrames;
338 }
339
340
341
342
343
344
345
346
347 protected B encoderEnforceMaxQueuedControlFrames(int maxQueuedControlFrames) {
348 enforceNonCodecConstraints("encoderEnforceMaxQueuedControlFrames");
349 this.maxQueuedControlFrames = checkPositiveOrZero(maxQueuedControlFrames, "maxQueuedControlFrames");
350 return self();
351 }
352
353
354
355
356 protected SensitivityDetector headerSensitivityDetector() {
357 return headerSensitivityDetector != null ? headerSensitivityDetector : DEFAULT_HEADER_SENSITIVITY_DETECTOR;
358 }
359
360
361
362
363 protected B headerSensitivityDetector(SensitivityDetector headerSensitivityDetector) {
364 enforceNonCodecConstraints("headerSensitivityDetector");
365 this.headerSensitivityDetector = requireNonNull(headerSensitivityDetector, "headerSensitivityDetector");
366 return self();
367 }
368
369
370
371
372
373
374
375
376 protected B encoderIgnoreMaxHeaderListSize(boolean ignoreMaxHeaderListSize) {
377 enforceNonCodecConstraints("encoderIgnoreMaxHeaderListSize");
378 encoderIgnoreMaxHeaderListSize = ignoreMaxHeaderListSize;
379 return self();
380 }
381
382
383
384
385
386
387 @Deprecated
388 protected B initialHuffmanDecodeCapacity(int initialHuffmanDecodeCapacity) {
389 return self();
390 }
391
392
393
394
395
396 protected B promisedRequestVerifier(Http2PromisedRequestVerifier promisedRequestVerifier) {
397 enforceNonCodecConstraints("promisedRequestVerifier");
398 this.promisedRequestVerifier = requireNonNull(promisedRequestVerifier, "promisedRequestVerifier");
399 return self();
400 }
401
402
403
404
405
406 protected Http2PromisedRequestVerifier promisedRequestVerifier() {
407 return promisedRequestVerifier;
408 }
409
410
411
412
413
414
415
416
417 protected int decoderEnforceMaxConsecutiveEmptyDataFrames() {
418 return maxConsecutiveEmptyFrames;
419 }
420
421
422
423
424
425
426
427
428 protected B decoderEnforceMaxConsecutiveEmptyDataFrames(int maxConsecutiveEmptyFrames) {
429 enforceNonCodecConstraints("maxConsecutiveEmptyFrames");
430 this.maxConsecutiveEmptyFrames = checkPositiveOrZero(
431 maxConsecutiveEmptyFrames, "maxConsecutiveEmptyFrames");
432 return self();
433 }
434
435
436
437
438
439 protected B autoAckSettingsFrame(boolean autoAckSettings) {
440 enforceNonCodecConstraints("autoAckSettingsFrame");
441 autoAckSettingsFrame = autoAckSettings;
442 return self();
443 }
444
445
446
447
448
449 protected boolean isAutoAckSettingsFrame() {
450 return autoAckSettingsFrame;
451 }
452
453
454
455
456
457 protected B autoAckPingFrame(boolean autoAckPingFrame) {
458 enforceNonCodecConstraints("autoAckPingFrame");
459 this.autoAckPingFrame = autoAckPingFrame;
460 return self();
461 }
462
463
464
465
466
467 protected boolean isAutoAckPingFrame() {
468 return autoAckPingFrame;
469 }
470
471
472
473
474
475
476
477 protected B decoupleCloseAndGoAway(boolean decoupleCloseAndGoAway) {
478 this.decoupleCloseAndGoAway = decoupleCloseAndGoAway;
479 return self();
480 }
481
482
483
484
485 protected boolean decoupleCloseAndGoAway() {
486 return decoupleCloseAndGoAway;
487 }
488
489
490
491
492
493
494
495
496
497
498
499
500 protected B flushPreface(boolean flushPreface) {
501 this.flushPreface = flushPreface;
502 return self();
503 }
504
505
506
507
508
509
510
511
512
513
514
515 protected boolean flushPreface() {
516 return flushPreface;
517 }
518
519
520
521
522 protected T build() {
523 if (encoder != null) {
524 assert decoder != null;
525 return buildFromCodec(decoder, encoder);
526 }
527
528 Http2Connection connection = this.connection;
529 if (connection == null) {
530 connection = new DefaultHttp2Connection(isServer(), maxReservedStreams());
531 }
532
533 return buildFromConnection(connection);
534 }
535
536 private T buildFromConnection(Http2Connection connection) {
537 Long maxHeaderListSize = initialSettings.maxHeaderListSize();
538 Http2FrameReader reader = new DefaultHttp2FrameReader(new DefaultHttp2HeadersDecoder(isValidateHeaders(),
539 maxHeaderListSize == null ? DEFAULT_HEADER_LIST_SIZE : maxHeaderListSize));
540 Http2FrameWriter writer = encoderIgnoreMaxHeaderListSize == null ?
541 new DefaultHttp2FrameWriter(headerSensitivityDetector()) :
542 new DefaultHttp2FrameWriter(headerSensitivityDetector(), encoderIgnoreMaxHeaderListSize);
543
544 if (frameLogger != null) {
545 reader = new Http2InboundFrameLogger(reader, frameLogger);
546 writer = new Http2OutboundFrameLogger(writer, frameLogger);
547 }
548
549 Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, writer);
550 boolean encoderEnforceMaxConcurrentStreams = encoderEnforceMaxConcurrentStreams();
551
552 if (maxQueuedControlFrames != 0) {
553 encoder = new Http2ControlFrameLimitEncoder(encoder, maxQueuedControlFrames);
554 }
555 if (encoderEnforceMaxConcurrentStreams) {
556 if (connection.isServer()) {
557 encoder.close();
558 reader.close();
559 throw new IllegalArgumentException(
560 "encoderEnforceMaxConcurrentStreams: " + encoderEnforceMaxConcurrentStreams +
561 " not supported for server");
562 }
563 encoder = new StreamBufferingEncoder(encoder);
564 }
565
566 DefaultHttp2ConnectionDecoder decoder = new DefaultHttp2ConnectionDecoder(connection, encoder, reader,
567 promisedRequestVerifier(), isAutoAckSettingsFrame(), isAutoAckPingFrame());
568 return buildFromCodec(decoder, encoder);
569 }
570
571 private T buildFromCodec(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) {
572 int maxConsecutiveEmptyDataFrames = decoderEnforceMaxConsecutiveEmptyDataFrames();
573 if (maxConsecutiveEmptyDataFrames > 0) {
574 decoder = new Http2EmptyDataFrameConnectionDecoder(decoder, maxConsecutiveEmptyDataFrames);
575 }
576 final T handler;
577 try {
578
579 handler = build(decoder, encoder, initialSettings);
580 } catch (Throwable t) {
581 encoder.close();
582 decoder.close();
583 throw new IllegalStateException("failed to build an Http2ConnectionHandler", t);
584 }
585
586
587 handler.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis);
588 if (handler.decoder().frameListener() == null) {
589 handler.decoder().frameListener(frameListener);
590 }
591 return handler;
592 }
593
594
595
596
597
598
599
600
601
602
603 protected abstract T build(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
604 Http2Settings initialSettings) throws Exception;
605
606
607
608
609 @SuppressWarnings("unchecked")
610 protected final B self() {
611 return (B) this;
612 }
613
614 private void enforceNonCodecConstraints(String rejected) {
615 enforceConstraint(rejected, "server/connection", decoder);
616 enforceConstraint(rejected, "server/connection", encoder);
617 }
618
619 private static void enforceConstraint(String methodName, String rejectorName, Object value) {
620 if (value != null) {
621 throw new IllegalStateException(
622 methodName + "() cannot be called because " + rejectorName + "() has been called already.");
623 }
624 }
625 }