1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http2;
17
18 import io.netty.channel.Channel;
19 import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector;
20
21 import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_LIST_SIZE;
22 import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_RESERVED_STREAMS;
23 import static io.netty.handler.codec.http2.Http2PromisedRequestVerifier.ALWAYS_VERIFY;
24 import static io.netty.util.internal.ObjectUtil.checkNotNull;
25 import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
26
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 public abstract class AbstractHttp2ConnectionHandlerBuilder<T extends Http2ConnectionHandler,
73 B extends AbstractHttp2ConnectionHandlerBuilder<T, B>> {
74
75 private static final SensitivityDetector DEFAULT_HEADER_SENSITIVITY_DETECTOR = Http2HeadersEncoder.NEVER_SENSITIVE;
76
77 private static final int DEFAULT_MAX_RST_FRAMES_PER_CONNECTION_FOR_SERVER = 200;
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 private Integer maxDecodedRstFramesPerWindow;
112 private int maxDecodedRstFramesSecondsPerWindow = 30;
113 private Integer maxEncodedRstFramesPerWindow;
114 private int maxEncodedRstFramesSecondsPerWindow = 30;
115 private int maxSmallContinuationFrames = Http2CodecUtil.DEFAULT_MAX_SMALL_CONTINUATION_FRAME;
116
117
118
119
120 protected Http2Settings initialSettings() {
121 return initialSettings;
122 }
123
124
125
126
127 protected B initialSettings(Http2Settings settings) {
128 initialSettings = checkNotNull(settings, "settings");
129 return self();
130 }
131
132
133
134
135
136
137 protected Http2FrameListener frameListener() {
138 return frameListener;
139 }
140
141
142
143
144
145 protected B frameListener(Http2FrameListener frameListener) {
146 this.frameListener = checkNotNull(frameListener, "frameListener");
147 return self();
148 }
149
150
151
152
153
154 protected long gracefulShutdownTimeoutMillis() {
155 return gracefulShutdownTimeoutMillis;
156 }
157
158
159
160
161 protected B gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) {
162 if (gracefulShutdownTimeoutMillis < -1) {
163 throw new IllegalArgumentException("gracefulShutdownTimeoutMillis: " + gracefulShutdownTimeoutMillis +
164 " (expected: -1 for indefinite or >= 0)");
165 }
166 this.gracefulShutdownTimeoutMillis = gracefulShutdownTimeoutMillis;
167 return self();
168 }
169
170
171
172
173
174 protected boolean isServer() {
175 return isServer != null ? isServer : true;
176 }
177
178
179
180
181
182 protected B server(boolean isServer) {
183 enforceConstraint("server", "connection", connection);
184 enforceConstraint("server", "codec", decoder);
185 enforceConstraint("server", "codec", encoder);
186
187 this.isServer = isServer;
188 return self();
189 }
190
191
192
193
194
195
196
197
198 protected int maxReservedStreams() {
199 return maxReservedStreams != null ? maxReservedStreams : DEFAULT_MAX_RESERVED_STREAMS;
200 }
201
202
203
204
205 protected B maxReservedStreams(int maxReservedStreams) {
206 enforceConstraint("server", "connection", connection);
207 enforceConstraint("server", "codec", decoder);
208 enforceConstraint("server", "codec", encoder);
209
210 this.maxReservedStreams = checkPositiveOrZero(maxReservedStreams, "maxReservedStreams");
211 return self();
212 }
213
214
215
216
217
218
219 protected Http2Connection connection() {
220 return connection;
221 }
222
223
224
225
226 protected B connection(Http2Connection connection) {
227 enforceConstraint("connection", "maxReservedStreams", maxReservedStreams);
228 enforceConstraint("connection", "server", isServer);
229 enforceConstraint("connection", "codec", decoder);
230 enforceConstraint("connection", "codec", encoder);
231
232 this.connection = checkNotNull(connection, "connection");
233
234 return self();
235 }
236
237
238
239
240
241
242 protected Http2ConnectionDecoder decoder() {
243 return decoder;
244 }
245
246
247
248
249
250
251 protected Http2ConnectionEncoder encoder() {
252 return encoder;
253 }
254
255
256
257
258 protected B codec(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) {
259 enforceConstraint("codec", "server", isServer);
260 enforceConstraint("codec", "maxReservedStreams", maxReservedStreams);
261 enforceConstraint("codec", "connection", connection);
262 enforceConstraint("codec", "frameLogger", frameLogger);
263 enforceConstraint("codec", "validateHeaders", validateHeaders);
264 enforceConstraint("codec", "headerSensitivityDetector", headerSensitivityDetector);
265 enforceConstraint("codec", "encoderEnforceMaxConcurrentStreams", encoderEnforceMaxConcurrentStreams);
266
267 checkNotNull(decoder, "decoder");
268 checkNotNull(encoder, "encoder");
269
270 if (decoder.connection() != encoder.connection()) {
271 throw new IllegalArgumentException("The specified encoder and decoder have different connections.");
272 }
273
274 this.decoder = decoder;
275 this.encoder = encoder;
276
277 return self();
278 }
279
280
281
282
283
284 protected boolean isValidateHeaders() {
285 return validateHeaders != null ? validateHeaders : true;
286 }
287
288
289
290
291
292 protected B validateHeaders(boolean validateHeaders) {
293 enforceNonCodecConstraints("validateHeaders");
294 this.validateHeaders = validateHeaders;
295 return self();
296 }
297
298
299
300
301
302
303 protected Http2FrameLogger frameLogger() {
304 return frameLogger;
305 }
306
307
308
309
310 protected B frameLogger(Http2FrameLogger frameLogger) {
311 enforceNonCodecConstraints("frameLogger");
312 this.frameLogger = checkNotNull(frameLogger, "frameLogger");
313 return self();
314 }
315
316
317
318
319
320 protected boolean encoderEnforceMaxConcurrentStreams() {
321 return encoderEnforceMaxConcurrentStreams != null ? encoderEnforceMaxConcurrentStreams : false;
322 }
323
324
325
326
327
328 protected B encoderEnforceMaxConcurrentStreams(boolean encoderEnforceMaxConcurrentStreams) {
329 enforceNonCodecConstraints("encoderEnforceMaxConcurrentStreams");
330 this.encoderEnforceMaxConcurrentStreams = encoderEnforceMaxConcurrentStreams;
331 return self();
332 }
333
334
335
336
337
338
339
340
341 protected int encoderEnforceMaxQueuedControlFrames() {
342 return maxQueuedControlFrames;
343 }
344
345
346
347
348
349
350
351
352 protected B encoderEnforceMaxQueuedControlFrames(int maxQueuedControlFrames) {
353 enforceNonCodecConstraints("encoderEnforceMaxQueuedControlFrames");
354 this.maxQueuedControlFrames = checkPositiveOrZero(maxQueuedControlFrames, "maxQueuedControlFrames");
355 return self();
356 }
357
358
359
360
361 protected SensitivityDetector headerSensitivityDetector() {
362 return headerSensitivityDetector != null ? headerSensitivityDetector : DEFAULT_HEADER_SENSITIVITY_DETECTOR;
363 }
364
365
366
367
368 protected B headerSensitivityDetector(SensitivityDetector headerSensitivityDetector) {
369 enforceNonCodecConstraints("headerSensitivityDetector");
370 this.headerSensitivityDetector = checkNotNull(headerSensitivityDetector, "headerSensitivityDetector");
371 return self();
372 }
373
374
375
376
377
378
379
380
381 protected B encoderIgnoreMaxHeaderListSize(boolean ignoreMaxHeaderListSize) {
382 enforceNonCodecConstraints("encoderIgnoreMaxHeaderListSize");
383 encoderIgnoreMaxHeaderListSize = ignoreMaxHeaderListSize;
384 return self();
385 }
386
387
388
389
390
391
392 @Deprecated
393 protected B initialHuffmanDecodeCapacity(int initialHuffmanDecodeCapacity) {
394 return self();
395 }
396
397
398
399
400
401 protected B promisedRequestVerifier(Http2PromisedRequestVerifier promisedRequestVerifier) {
402 enforceNonCodecConstraints("promisedRequestVerifier");
403 this.promisedRequestVerifier = checkNotNull(promisedRequestVerifier, "promisedRequestVerifier");
404 return self();
405 }
406
407
408
409
410
411 protected Http2PromisedRequestVerifier promisedRequestVerifier() {
412 return promisedRequestVerifier;
413 }
414
415
416
417
418
419
420
421
422 protected int decoderEnforceMaxConsecutiveEmptyDataFrames() {
423 return maxConsecutiveEmptyFrames;
424 }
425
426
427
428
429
430
431
432
433 protected B decoderEnforceMaxConsecutiveEmptyDataFrames(int maxConsecutiveEmptyFrames) {
434 enforceNonCodecConstraints("maxConsecutiveEmptyFrames");
435 this.maxConsecutiveEmptyFrames = checkPositiveOrZero(
436 maxConsecutiveEmptyFrames, "maxConsecutiveEmptyFrames");
437 return self();
438 }
439
440
441
442
443
444
445
446
447 protected B decoderEnforceMaxRstFramesPerWindow(int maxRstFramesPerWindow, int secondsPerWindow) {
448 enforceNonCodecConstraints("decoderEnforceMaxRstFramesPerWindow");
449 this.maxDecodedRstFramesPerWindow = checkPositiveOrZero(
450 maxRstFramesPerWindow, "maxRstFramesPerWindow");
451 this.maxDecodedRstFramesSecondsPerWindow = checkPositiveOrZero(secondsPerWindow, "secondsPerWindow");
452 return self();
453 }
454
455
456
457
458
459
460
461
462 protected B encoderEnforceMaxRstFramesPerWindow(int maxRstFramesPerWindow, int secondsPerWindow) {
463 enforceNonCodecConstraints("encoderEnforceMaxRstFramesPerWindow");
464 this.maxEncodedRstFramesPerWindow = checkPositiveOrZero(
465 maxRstFramesPerWindow, "maxRstFramesPerWindow");
466 this.maxEncodedRstFramesSecondsPerWindow = checkPositiveOrZero(secondsPerWindow, "secondsPerWindow");
467 return self();
468 }
469
470
471
472
473
474
475
476
477 protected int decoderEnforceMaxSmallContinuationFrames() {
478 return maxSmallContinuationFrames;
479 }
480
481
482
483
484
485
486
487 protected B decoderEnforceMaxSmallContinuationFrames(int maxSmallContinuationFrames) {
488 enforceNonCodecConstraints("maxSmallContinuationFrames");
489 this.maxSmallContinuationFrames = checkPositiveOrZero(
490 maxSmallContinuationFrames, "maxSmallContinuationFrames");
491 return self();
492 }
493
494
495
496
497
498 protected B autoAckSettingsFrame(boolean autoAckSettings) {
499 enforceNonCodecConstraints("autoAckSettingsFrame");
500 autoAckSettingsFrame = autoAckSettings;
501 return self();
502 }
503
504
505
506
507
508 protected boolean isAutoAckSettingsFrame() {
509 return autoAckSettingsFrame;
510 }
511
512
513
514
515
516 protected B autoAckPingFrame(boolean autoAckPingFrame) {
517 enforceNonCodecConstraints("autoAckPingFrame");
518 this.autoAckPingFrame = autoAckPingFrame;
519 return self();
520 }
521
522
523
524
525
526 protected boolean isAutoAckPingFrame() {
527 return autoAckPingFrame;
528 }
529
530
531
532
533
534
535
536 protected B decoupleCloseAndGoAway(boolean decoupleCloseAndGoAway) {
537 this.decoupleCloseAndGoAway = decoupleCloseAndGoAway;
538 return self();
539 }
540
541
542
543
544 protected boolean decoupleCloseAndGoAway() {
545 return decoupleCloseAndGoAway;
546 }
547
548
549
550
551
552
553
554
555
556
557
558
559 protected B flushPreface(boolean flushPreface) {
560 this.flushPreface = flushPreface;
561 return self();
562 }
563
564
565
566
567
568
569
570
571
572
573
574 protected boolean flushPreface() {
575 return flushPreface;
576 }
577
578
579
580
581 protected T build() {
582 if (encoder != null) {
583 assert decoder != null;
584 return buildFromCodec(decoder, encoder);
585 }
586
587 Http2Connection connection = this.connection;
588 if (connection == null) {
589 connection = new DefaultHttp2Connection(isServer(), maxReservedStreams());
590 }
591
592 return buildFromConnection(connection);
593 }
594
595 private T buildFromConnection(Http2Connection connection) {
596 Long maxHeaderListSize = initialSettings.maxHeaderListSize();
597 Http2FrameReader reader = new DefaultHttp2FrameReader(new DefaultHttp2HeadersDecoder(isValidateHeaders(),
598 maxHeaderListSize == null ? DEFAULT_HEADER_LIST_SIZE : maxHeaderListSize,
599 -1), maxSmallContinuationFrames);
600 Http2FrameWriter writer = encoderIgnoreMaxHeaderListSize == null ?
601 new DefaultHttp2FrameWriter(headerSensitivityDetector()) :
602 new DefaultHttp2FrameWriter(headerSensitivityDetector(), encoderIgnoreMaxHeaderListSize);
603
604 if (frameLogger != null) {
605 reader = new Http2InboundFrameLogger(reader, frameLogger);
606 writer = new Http2OutboundFrameLogger(writer, frameLogger);
607 }
608
609 Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(connection, writer);
610 boolean encoderEnforceMaxConcurrentStreams = encoderEnforceMaxConcurrentStreams();
611
612 if (maxQueuedControlFrames != 0) {
613 encoder = new Http2ControlFrameLimitEncoder(encoder, maxQueuedControlFrames);
614 }
615 final int maxEncodedRstFrames;
616 if (maxEncodedRstFramesPerWindow == null) {
617
618 if (isServer()) {
619 maxEncodedRstFrames = DEFAULT_MAX_RST_FRAMES_PER_CONNECTION_FOR_SERVER;
620 } else {
621 maxEncodedRstFrames = 0;
622 }
623 } else {
624 maxEncodedRstFrames = maxEncodedRstFramesPerWindow;
625 }
626 if (maxEncodedRstFrames > 0 && maxEncodedRstFramesSecondsPerWindow > 0) {
627 encoder = new Http2MaxRstFrameLimitEncoder(
628 encoder, maxEncodedRstFrames, maxEncodedRstFramesSecondsPerWindow);
629 }
630 if (encoderEnforceMaxConcurrentStreams) {
631 if (connection.isServer()) {
632 encoder.close();
633 reader.close();
634 throw new IllegalArgumentException(
635 "encoderEnforceMaxConcurrentStreams: " + encoderEnforceMaxConcurrentStreams +
636 " not supported for server");
637 }
638 encoder = new StreamBufferingEncoder(encoder);
639 }
640
641 DefaultHttp2ConnectionDecoder decoder = new DefaultHttp2ConnectionDecoder(connection, encoder, reader,
642 promisedRequestVerifier(), isAutoAckSettingsFrame(), isAutoAckPingFrame(), isValidateHeaders());
643 return buildFromCodec(decoder, encoder);
644 }
645
646 private T buildFromCodec(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) {
647 int maxConsecutiveEmptyDataFrames = decoderEnforceMaxConsecutiveEmptyDataFrames();
648 if (maxConsecutiveEmptyDataFrames > 0) {
649 decoder = new Http2EmptyDataFrameConnectionDecoder(decoder, maxConsecutiveEmptyDataFrames);
650 }
651 final int maxDecodedRstFrames;
652 if (maxDecodedRstFramesPerWindow == null) {
653
654 if (isServer()) {
655 maxDecodedRstFrames = DEFAULT_MAX_RST_FRAMES_PER_CONNECTION_FOR_SERVER;
656 } else {
657 maxDecodedRstFrames = 0;
658 }
659 } else {
660 maxDecodedRstFrames = maxDecodedRstFramesPerWindow;
661 }
662 if (maxDecodedRstFrames > 0 && maxDecodedRstFramesSecondsPerWindow > 0) {
663 decoder = new Http2MaxRstFrameDecoder(decoder, maxDecodedRstFrames, maxDecodedRstFramesSecondsPerWindow);
664 }
665 final T handler;
666 try {
667
668 handler = build(decoder, encoder, initialSettings);
669 } catch (Throwable t) {
670 encoder.close();
671 decoder.close();
672 throw new IllegalStateException("failed to build an Http2ConnectionHandler", t);
673 }
674
675
676 handler.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis);
677 if (handler.decoder().frameListener() == null) {
678 handler.decoder().frameListener(frameListener);
679 }
680 return handler;
681 }
682
683
684
685
686
687
688
689
690
691
692 protected abstract T build(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
693 Http2Settings initialSettings) throws Exception;
694
695
696
697
698 @SuppressWarnings("unchecked")
699 protected final B self() {
700 return (B) this;
701 }
702
703 private void enforceNonCodecConstraints(String rejected) {
704 enforceConstraint(rejected, "server/connection", decoder);
705 enforceConstraint(rejected, "server/connection", encoder);
706 }
707
708 private static void enforceConstraint(String methodName, String rejectorName, Object value) {
709 if (value != null) {
710 throw new IllegalStateException(
711 methodName + "() cannot be called because " + rejectorName + "() has been called already.");
712 }
713 }
714 }