1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package io.netty.handler.codec.http2;
16
17 import io.netty.buffer.ByteBuf;
18 import io.netty.buffer.ByteBufUtil;
19 import io.netty.buffer.Unpooled;
20 import io.netty.channel.ChannelFuture;
21 import io.netty.channel.ChannelFutureListener;
22 import io.netty.channel.ChannelHandlerContext;
23 import io.netty.channel.ChannelOutboundHandler;
24 import io.netty.channel.ChannelPromise;
25 import io.netty.handler.codec.ByteToMessageDecoder;
26 import io.netty.handler.codec.http.HttpResponseStatus;
27 import io.netty.handler.codec.http2.Http2Exception.CompositeStreamException;
28 import io.netty.handler.codec.http2.Http2Exception.StreamException;
29 import io.netty.util.CharsetUtil;
30 import io.netty.util.concurrent.Future;
31 import io.netty.util.internal.logging.InternalLogger;
32 import io.netty.util.internal.logging.InternalLoggerFactory;
33
34 import java.net.SocketAddress;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Set;
38 import java.util.concurrent.TimeUnit;
39
40 import static io.netty.buffer.ByteBufUtil.hexDump;
41 import static io.netty.buffer.Unpooled.EMPTY_BUFFER;
42 import static io.netty.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_STREAM_ID;
43 import static io.netty.handler.codec.http2.Http2CodecUtil.connectionPrefaceBuf;
44 import static io.netty.handler.codec.http2.Http2CodecUtil.getEmbeddedHttp2Exception;
45 import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR;
46 import static io.netty.handler.codec.http2.Http2Error.NO_ERROR;
47 import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
48 import static io.netty.handler.codec.http2.Http2Exception.connectionError;
49 import static io.netty.handler.codec.http2.Http2Exception.isStreamError;
50 import static io.netty.handler.codec.http2.Http2FrameTypes.SETTINGS;
51 import static io.netty.handler.codec.http2.Http2Stream.State.IDLE;
52 import static io.netty.util.CharsetUtil.UTF_8;
53 import static io.netty.util.internal.ObjectUtil.checkNotNull;
54 import static java.lang.Math.min;
55 import static java.util.concurrent.TimeUnit.MILLISECONDS;
56
57
58
59
60
61
62
63
64
65
66 public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http2LifecycleManager,
67 ChannelOutboundHandler {
68
69 private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2ConnectionHandler.class);
70
71 private static final Http2Headers HEADERS_TOO_LARGE_HEADERS = ReadOnlyHttp2Headers.serverHeaders(false,
72 HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE.codeAsText());
73 private static final ByteBuf HTTP_1_X_BUF = Unpooled.unreleasableBuffer(
74 Unpooled.wrappedBuffer(new byte[] {'H', 'T', 'T', 'P', '/', '1', '.'})).asReadOnly();
75
76 private final Http2ConnectionDecoder decoder;
77 private final Http2ConnectionEncoder encoder;
78 private final Http2Settings initialSettings;
79 private final boolean decoupleCloseAndGoAway;
80 private final boolean flushPreface;
81 private ChannelFutureListener closeListener;
82 private BaseDecoder byteDecoder;
83 private long gracefulShutdownTimeoutMillis;
84 private boolean inFlush;
85 private boolean flushAgain;
86
87 protected Http2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
88 Http2Settings initialSettings) {
89 this(decoder, encoder, initialSettings, false);
90 }
91
92 protected Http2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
93 Http2Settings initialSettings, boolean decoupleCloseAndGoAway) {
94 this(decoder, encoder, initialSettings, decoupleCloseAndGoAway, true);
95 }
96
97 protected Http2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
98 Http2Settings initialSettings, boolean decoupleCloseAndGoAway,
99 boolean flushPreface) {
100 this.initialSettings = checkNotNull(initialSettings, "initialSettings");
101 this.decoder = checkNotNull(decoder, "decoder");
102 this.encoder = checkNotNull(encoder, "encoder");
103 this.decoupleCloseAndGoAway = decoupleCloseAndGoAway;
104 this.flushPreface = flushPreface;
105 if (encoder.connection() != decoder.connection()) {
106 throw new IllegalArgumentException("Encoder and Decoder do not share the same connection object");
107 }
108 }
109
110
111
112
113
114
115 public long gracefulShutdownTimeoutMillis() {
116 return gracefulShutdownTimeoutMillis;
117 }
118
119
120
121
122
123
124
125 public void gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) {
126 if (gracefulShutdownTimeoutMillis < -1) {
127 throw new IllegalArgumentException("gracefulShutdownTimeoutMillis: " + gracefulShutdownTimeoutMillis +
128 " (expected: -1 for indefinite or >= 0)");
129 }
130 this.gracefulShutdownTimeoutMillis = gracefulShutdownTimeoutMillis;
131 }
132
133 public Http2Connection connection() {
134 return encoder.connection();
135 }
136
137 public Http2ConnectionDecoder decoder() {
138 return decoder;
139 }
140
141 public Http2ConnectionEncoder encoder() {
142 return encoder;
143 }
144
145 private boolean prefaceSent() {
146 return byteDecoder != null && byteDecoder.prefaceSent();
147 }
148
149
150
151
152
153 public void onHttpClientUpgrade() throws Http2Exception {
154 if (connection().isServer()) {
155 throw connectionError(PROTOCOL_ERROR, "Client-side HTTP upgrade requested for a server");
156 }
157 if (!prefaceSent()) {
158
159
160 throw connectionError(INTERNAL_ERROR, "HTTP upgrade must occur after preface was sent");
161 }
162 if (decoder.prefaceReceived()) {
163 throw connectionError(PROTOCOL_ERROR, "HTTP upgrade must occur before HTTP/2 preface is received");
164 }
165
166
167 connection().local().createStream(HTTP_UPGRADE_STREAM_ID, true);
168 }
169
170
171
172
173
174 public void onHttpServerUpgrade(Http2Settings settings) throws Http2Exception {
175 if (!connection().isServer()) {
176 throw connectionError(PROTOCOL_ERROR, "Server-side HTTP upgrade requested for a client");
177 }
178 if (!prefaceSent()) {
179
180
181 throw connectionError(INTERNAL_ERROR, "HTTP upgrade must occur after preface was sent");
182 }
183 if (decoder.prefaceReceived()) {
184 throw connectionError(PROTOCOL_ERROR, "HTTP upgrade must occur before HTTP/2 preface is received");
185 }
186
187
188 encoder.remoteSettings(settings);
189
190
191 connection().remote().createStream(HTTP_UPGRADE_STREAM_ID, true);
192 }
193
194 @Override
195 public void flush(ChannelHandlerContext ctx) {
196 if (inFlush) {
197
198
199 flushAgain = true;
200 return;
201 }
202 inFlush = true;
203 try {
204 do {
205 flushAgain = false;
206
207 encoder.flowController().writePendingBytes();
208 ctx.flush();
209
210
211
212 } while (flushAgain);
213 } catch (Http2Exception e) {
214 onError(ctx, true, e);
215 } catch (Throwable cause) {
216 onError(ctx, true, connectionError(INTERNAL_ERROR, cause, "Error flushing"));
217 } finally {
218 inFlush = false;
219 }
220 }
221
222 private boolean hasPendingData() {
223 final Http2RemoteFlowController flowController = encoder.flowController();
224 try {
225
226
227 return connection().forEachActiveStream(stream -> !flowController.hasFlowControlled(stream)) != null;
228 } catch (Http2Exception e) {
229 return false;
230 }
231 }
232
233 private abstract class BaseDecoder {
234 public abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;
235 public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { }
236 public void channelActive(ChannelHandlerContext ctx) throws Exception { }
237
238 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
239
240 encoder().close();
241 decoder().close();
242
243
244
245 connection().close(ctx.voidPromise());
246 }
247
248
249
250
251 public boolean prefaceSent() {
252 return true;
253 }
254
255
256
257
258
259
260
261 public void sendPrefaceIfNeeded(ChannelHandlerContext ctx) throws Exception {
262
263 }
264 }
265
266 private final class PrefaceDecoder extends BaseDecoder {
267 private ByteBuf clientPrefaceString;
268 private boolean prefaceSent;
269
270 PrefaceDecoder(ChannelHandlerContext ctx) throws Exception {
271 clientPrefaceString = clientPrefaceString(encoder.connection());
272
273
274 sendPrefaceIfNeeded(ctx);
275 }
276
277 @Override
278 public boolean prefaceSent() {
279 return prefaceSent;
280 }
281
282 @Override
283 public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
284 try {
285 if (ctx.channel().isActive() && readClientPrefaceString(in) && verifyFirstFrameIsSettings(in)) {
286
287 byteDecoder = new FrameDecoder();
288 byteDecoder.decode(ctx, in, out);
289 }
290 } catch (Throwable e) {
291 if (byteDecoder != null) {
292
293 in.skipBytes(in.readableBytes());
294 }
295 onError(ctx, false, e);
296 }
297 }
298
299 @Override
300 public void channelActive(ChannelHandlerContext ctx) throws Exception {
301
302 sendPrefaceIfNeeded(ctx);
303 }
304
305 @Override
306 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
307 cleanup();
308 super.channelInactive(ctx);
309 }
310
311
312
313
314 @Override
315 public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
316 cleanup();
317 }
318
319
320
321
322 private void cleanup() {
323 if (clientPrefaceString != null) {
324 clientPrefaceString.release();
325 clientPrefaceString = null;
326 }
327 }
328
329
330
331
332
333
334
335 private boolean readClientPrefaceString(ByteBuf in) throws Http2Exception {
336 if (clientPrefaceString == null) {
337 return true;
338 }
339
340 int prefaceRemaining = clientPrefaceString.readableBytes();
341 int bytesRead = min(in.readableBytes(), prefaceRemaining);
342
343
344 if (bytesRead == 0 || !ByteBufUtil.equals(in, in.readerIndex(),
345 clientPrefaceString, clientPrefaceString.readerIndex(),
346 bytesRead)) {
347 int maxSearch = 1024;
348 int http1Index =
349 ByteBufUtil.indexOf(HTTP_1_X_BUF, in.slice(in.readerIndex(), min(in.readableBytes(), maxSearch)));
350 if (http1Index != -1) {
351 String chunk = in.toString(in.readerIndex(), http1Index - in.readerIndex(), CharsetUtil.US_ASCII);
352 throw connectionError(PROTOCOL_ERROR, "Unexpected HTTP/1.x request: %s", chunk);
353 }
354 String receivedBytes = hexDump(in, in.readerIndex(),
355 min(in.readableBytes(), clientPrefaceString.readableBytes()));
356 throw connectionError(PROTOCOL_ERROR, "HTTP/2 client preface string missing or corrupt. " +
357 "Hex dump for received bytes: %s", receivedBytes);
358 }
359 in.skipBytes(bytesRead);
360 clientPrefaceString.skipBytes(bytesRead);
361
362 if (!clientPrefaceString.isReadable()) {
363
364 clientPrefaceString.release();
365 clientPrefaceString = null;
366 return true;
367 }
368 return false;
369 }
370
371
372
373
374
375
376
377
378
379 private boolean verifyFirstFrameIsSettings(ByteBuf in) throws Http2Exception {
380 if (in.readableBytes() < 5) {
381
382 return false;
383 }
384
385 short frameType = in.getUnsignedByte(in.readerIndex() + 3);
386 if (frameType != SETTINGS) {
387 throw connectionError(PROTOCOL_ERROR, "First received frame was not SETTINGS. " +
388 "Hex dump for first 5 bytes: %s",
389 hexDump(in, in.readerIndex(), 5));
390 }
391 short flags = in.getUnsignedByte(in.readerIndex() + 4);
392 if ((flags & Http2Flags.ACK) != 0) {
393 throw connectionError(PROTOCOL_ERROR, "First received frame was SETTINGS frame but had ACK flag set. " +
394 "Hex dump for first 5 bytes: %s",
395 hexDump(in, in.readerIndex(), 5));
396 }
397 return true;
398 }
399
400
401
402
403 @Override
404 public void sendPrefaceIfNeeded(ChannelHandlerContext ctx) throws Exception {
405 if (prefaceSent || !ctx.channel().isActive()) {
406 return;
407 }
408
409 prefaceSent = true;
410
411 final boolean isClient = !connection().isServer();
412 if (isClient) {
413
414 ctx.write(connectionPrefaceBuf()).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
415 }
416
417
418 encoder.writeSettings(ctx, initialSettings, ctx.newPromise()).addListener(
419 ChannelFutureListener.CLOSE_ON_FAILURE);
420
421 try {
422 if (isClient) {
423
424
425
426 userEventTriggered(ctx, Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE);
427 }
428 } finally {
429 if (flushPreface) {
430
431
432
433 ctx.flush();
434 }
435 }
436 }
437 }
438
439 private final class FrameDecoder extends BaseDecoder {
440 @Override
441 public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
442 try {
443 decoder.decodeFrame(ctx, in, out);
444 } catch (Throwable e) {
445 onError(ctx, false, e);
446 }
447 }
448 }
449
450 @Override
451 public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
452
453 encoder.lifecycleManager(this);
454 decoder.lifecycleManager(this);
455 encoder.flowController().channelHandlerContext(ctx);
456 decoder.flowController().channelHandlerContext(ctx);
457 byteDecoder = new PrefaceDecoder(ctx);
458
459
460
461 connection().addListener(new Http2ConnectionAdapter() {
462 @Override
463 public void onStreamClosed(Http2Stream stream) {
464 checkCloseConnection(ctx.newSucceededFuture());
465 }
466 });
467 }
468
469 @Override
470 protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
471 if (byteDecoder != null) {
472 byteDecoder.handlerRemoved(ctx);
473 byteDecoder = null;
474 }
475 }
476
477 @Override
478 public void channelActive(ChannelHandlerContext ctx) throws Exception {
479 if (byteDecoder == null) {
480 byteDecoder = new PrefaceDecoder(ctx);
481 }
482 byteDecoder.channelActive(ctx);
483 super.channelActive(ctx);
484 }
485
486 @Override
487 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
488
489 super.channelInactive(ctx);
490 if (byteDecoder != null) {
491 byteDecoder.channelInactive(ctx);
492 byteDecoder = null;
493 }
494 }
495
496 @Override
497 public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
498 try {
499
500
501
502 if (ctx.channel().isWritable() && hasPendingData()) {
503 flush(ctx);
504 }
505 encoder.flowController().channelWritabilityChanged();
506 } finally {
507 super.channelWritabilityChanged(ctx);
508 }
509 }
510
511 @Override
512 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
513 byteDecoder.decode(ctx, in, out);
514 }
515
516 @Override
517 public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
518
519
520
521 ctx.bind(localAddress, ctx.newPromise()).addListener(new PrefaceSendListener(ctx, promise));
522 }
523
524 @Override
525 public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
526 ChannelPromise promise) throws Exception {
527
528
529
530 ctx.connect(remoteAddress, localAddress, ctx.newPromise()).addListener(new PrefaceSendListener(ctx, promise));
531 }
532
533 @Override
534 public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
535 ctx.disconnect(promise);
536 }
537
538 @Override
539 public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
540 if (decoupleCloseAndGoAway) {
541 ctx.close(promise);
542 return;
543 }
544 promise = promise.unvoid();
545
546 if (!ctx.channel().isActive() || !prefaceSent()) {
547 ctx.close(promise);
548 return;
549 }
550
551
552
553
554
555
556 ChannelFuture f = connection().goAwaySent() ? ctx.write(EMPTY_BUFFER) : goAway(ctx, null, ctx.newPromise());
557 ctx.flush();
558 doGracefulShutdown(ctx, f, promise);
559 }
560
561 private ChannelFutureListener newClosingChannelFutureListener(
562 ChannelHandlerContext ctx, ChannelPromise promise) {
563 long gracefulShutdownTimeoutMillis = this.gracefulShutdownTimeoutMillis;
564 return gracefulShutdownTimeoutMillis < 0 ?
565 new ClosingChannelFutureListener(ctx, promise) :
566 new ClosingChannelFutureListener(ctx, promise, gracefulShutdownTimeoutMillis, MILLISECONDS);
567 }
568
569 private void doGracefulShutdown(ChannelHandlerContext ctx, ChannelFuture future, final ChannelPromise promise) {
570 final ChannelFutureListener listener = newClosingChannelFutureListener(ctx, promise);
571 if (isGracefulShutdownComplete()) {
572
573
574 future.addListener(listener);
575 } else {
576
577
578
579
580 if (closeListener == null) {
581 closeListener = listener;
582 } else if (promise != null) {
583 final ChannelFutureListener oldCloseListener = closeListener;
584 closeListener = future1 -> {
585 try {
586 oldCloseListener.operationComplete(future1);
587 } finally {
588 listener.operationComplete(future1);
589 }
590 };
591 }
592 }
593 }
594
595 @Override
596 public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
597 ctx.deregister(promise);
598 }
599
600 @Override
601 public void read(ChannelHandlerContext ctx) throws Exception {
602 ctx.read();
603 }
604
605 @Override
606 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
607 ctx.write(msg, promise);
608 }
609
610 @Override
611 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
612
613
614 try {
615
616 channelReadComplete0(ctx);
617 } finally {
618 flush(ctx);
619 }
620 }
621
622 final void channelReadComplete0(ChannelHandlerContext ctx) {
623
624 discardSomeReadBytes();
625
626
627
628
629 if (!ctx.channel().config().isAutoRead()) {
630 ctx.read();
631 }
632
633 ctx.fireChannelReadComplete();
634 }
635
636
637
638
639 @Override
640 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
641 if (getEmbeddedHttp2Exception(cause) != null) {
642
643 onError(ctx, false, cause);
644 } else {
645 super.exceptionCaught(ctx, cause);
646 }
647 }
648
649
650
651
652
653
654
655
656 @Override
657 public void closeStreamLocal(Http2Stream stream, ChannelFuture future) {
658 switch (stream.state()) {
659 case HALF_CLOSED_LOCAL:
660 case OPEN:
661 stream.closeLocalSide();
662 break;
663 default:
664 closeStream(stream, future);
665 break;
666 }
667 }
668
669
670
671
672
673
674
675
676 @Override
677 public void closeStreamRemote(Http2Stream stream, ChannelFuture future) {
678 switch (stream.state()) {
679 case HALF_CLOSED_REMOTE:
680 case OPEN:
681 stream.closeRemoteSide();
682 break;
683 default:
684 closeStream(stream, future);
685 break;
686 }
687 }
688
689 @Override
690 public void closeStream(final Http2Stream stream, ChannelFuture future) {
691 if (future.isDone()) {
692 doCloseStream(stream, future);
693 } else {
694 future.addListener((ChannelFutureListener) future1 -> doCloseStream(stream, future1));
695 }
696 }
697
698
699
700
701 @Override
702 public void onError(ChannelHandlerContext ctx, boolean outbound, Throwable cause) {
703 Http2Exception embedded = getEmbeddedHttp2Exception(cause);
704 if (isStreamError(embedded)) {
705 onStreamError(ctx, outbound, cause, (StreamException) embedded);
706 } else if (embedded instanceof CompositeStreamException) {
707 CompositeStreamException compositException = (CompositeStreamException) embedded;
708
709
710
711
712
713
714 Set<Integer> handledStreamIds = new HashSet<Integer>();
715 for (StreamException streamException : compositException) {
716 if (handledStreamIds.add(streamException.streamId())) {
717 onStreamError(ctx, outbound, cause, streamException);
718 }
719 }
720 } else {
721 onConnectionError(ctx, outbound, cause, embedded);
722 }
723 ctx.flush();
724 }
725
726
727
728
729
730
731 protected boolean isGracefulShutdownComplete() {
732 return connection().numActiveStreams() == 0;
733 }
734
735
736
737
738
739
740
741
742
743
744
745 protected void onConnectionError(ChannelHandlerContext ctx, boolean outbound,
746 Throwable cause, Http2Exception http2Ex) {
747 if (http2Ex == null) {
748 http2Ex = new Http2Exception(INTERNAL_ERROR, cause.getMessage(), cause);
749 }
750
751 ChannelPromise promise = ctx.newPromise();
752 ChannelFuture future = goAway(ctx, http2Ex, ctx.newPromise());
753 if (http2Ex.shutdownHint() == Http2Exception.ShutdownHint.GRACEFUL_SHUTDOWN) {
754 doGracefulShutdown(ctx, future, promise);
755 } else {
756 future.addListener(newClosingChannelFutureListener(ctx, promise));
757 }
758 }
759
760
761
762
763
764
765
766
767
768
769 protected void onStreamError(ChannelHandlerContext ctx, boolean outbound,
770 @SuppressWarnings("unused") Throwable cause, StreamException http2Ex) {
771 final int streamId = http2Ex.streamId();
772 Http2Stream stream = connection().stream(streamId);
773
774
775 if (http2Ex instanceof Http2Exception.HeaderListSizeException &&
776 ((Http2Exception.HeaderListSizeException) http2Ex).duringDecode() &&
777 connection().isServer()) {
778
779
780
781
782
783
784
785 if (stream == null) {
786 try {
787 stream = encoder.connection().remote().createStream(streamId, true);
788 } catch (Http2Exception e) {
789 encoder().writeRstStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise());
790 return;
791 }
792 }
793
794
795 if (stream != null && !stream.isHeadersSent()) {
796 try {
797 handleServerHeaderDecodeSizeError(ctx, stream);
798 } catch (Throwable cause2) {
799 onError(ctx, outbound, connectionError(INTERNAL_ERROR, cause2, "Error DecodeSizeError"));
800 }
801 }
802 }
803
804 if (stream == null) {
805 if (!outbound || connection().local().mayHaveCreatedStream(streamId)) {
806 encoder().writeRstStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise());
807 }
808 } else {
809 encoder().writeRstStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise());
810 }
811 }
812
813
814
815
816
817
818
819
820 protected void handleServerHeaderDecodeSizeError(ChannelHandlerContext ctx, Http2Stream stream) {
821 encoder().writeHeaders(ctx, stream.id(), HEADERS_TOO_LARGE_HEADERS, 0, true, ctx.newPromise());
822 }
823
824 protected Http2FrameWriter frameWriter() {
825 return encoder().frameWriter();
826 }
827
828
829
830
831
832
833 private ChannelFuture resetUnknownStream(final ChannelHandlerContext ctx, int streamId, long errorCode,
834 ChannelPromise promise) {
835 ChannelFuture future = frameWriter().writeRstStream(ctx, streamId, errorCode, promise);
836 if (future.isDone()) {
837 closeConnectionOnError(ctx, future);
838 } else {
839 future.addListener((ChannelFutureListener) f -> closeConnectionOnError(ctx, f));
840 }
841 return future;
842 }
843
844 @Override
845 public ChannelFuture resetStream(final ChannelHandlerContext ctx, int streamId, long errorCode,
846 ChannelPromise promise) {
847 final Http2Stream stream = connection().stream(streamId);
848 if (stream == null) {
849 return resetUnknownStream(ctx, streamId, errorCode, promise.unvoid());
850 }
851
852 return resetStream(ctx, stream, errorCode, promise);
853 }
854
855 private ChannelFuture resetStream(final ChannelHandlerContext ctx, final Http2Stream stream,
856 long errorCode, ChannelPromise promise) {
857 promise = promise.unvoid();
858 if (stream.isResetSent()) {
859
860 return promise.setSuccess();
861 }
862
863
864
865
866
867 stream.resetSent();
868
869 final ChannelFuture future;
870
871
872 if (stream.state() == IDLE ||
873 connection().local().created(stream) && !stream.isHeadersSent() && !stream.isPushPromiseSent()) {
874 future = promise.setSuccess();
875 } else {
876 future = frameWriter().writeRstStream(ctx, stream.id(), errorCode, promise);
877 }
878 if (future.isDone()) {
879 processRstStreamWriteResult(ctx, stream, future);
880 } else {
881 future.addListener((ChannelFutureListener) f -> processRstStreamWriteResult(ctx, stream, f));
882 }
883
884 return future;
885 }
886
887 @Override
888 public ChannelFuture goAway(final ChannelHandlerContext ctx, final int lastStreamId, final long errorCode,
889 final ByteBuf debugData, ChannelPromise promise) {
890 promise = promise.unvoid();
891 final Http2Connection connection = connection();
892 try {
893 if (!connection.goAwaySent(lastStreamId, errorCode, debugData)) {
894 debugData.release();
895 promise.trySuccess();
896 return promise;
897 }
898 } catch (Throwable cause) {
899 debugData.release();
900 promise.tryFailure(cause);
901 return promise;
902 }
903
904
905
906 debugData.retain();
907 ChannelFuture future = frameWriter().writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
908
909 if (future.isDone()) {
910 processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future);
911 } else {
912 future.addListener((ChannelFutureListener) f ->
913 processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, f));
914 }
915
916 return future;
917 }
918
919
920
921
922
923 private void checkCloseConnection(ChannelFuture future) {
924
925
926 if (closeListener != null && isGracefulShutdownComplete()) {
927 ChannelFutureListener closeListener = this.closeListener;
928
929
930 this.closeListener = null;
931 try {
932 closeListener.operationComplete(future);
933 } catch (Exception e) {
934 throw new IllegalStateException("Close listener threw an unexpected exception", e);
935 }
936 }
937 }
938
939
940
941
942
943 private ChannelFuture goAway(ChannelHandlerContext ctx, Http2Exception cause, ChannelPromise promise) {
944 long errorCode = cause != null ? cause.error().code() : NO_ERROR.code();
945 int lastKnownStream;
946 if (cause != null && cause.shutdownHint() == Http2Exception.ShutdownHint.HARD_SHUTDOWN) {
947
948
949
950
951 lastKnownStream = Integer.MAX_VALUE;
952 } else {
953 lastKnownStream = connection().remote().lastStreamCreated();
954 }
955 return goAway(ctx, lastKnownStream, errorCode, Http2CodecUtil.toByteBuf(ctx, cause), promise);
956 }
957
958 private void processRstStreamWriteResult(ChannelHandlerContext ctx, Http2Stream stream, ChannelFuture future) {
959 if (future.isSuccess()) {
960 closeStream(stream, future);
961 } else {
962
963 onConnectionError(ctx, true, future.cause(), null);
964 }
965 }
966
967 private void closeConnectionOnError(ChannelHandlerContext ctx, ChannelFuture future) {
968 if (!future.isSuccess()) {
969 onConnectionError(ctx, true, future.cause(), null);
970 }
971 }
972
973 private void doCloseStream(final Http2Stream stream, ChannelFuture future) {
974 stream.close();
975 checkCloseConnection(future);
976 }
977
978
979
980
981 private static ByteBuf clientPrefaceString(Http2Connection connection) {
982 return connection.isServer() ? connectionPrefaceBuf() : null;
983 }
984
985 private static void processGoAwayWriteResult(final ChannelHandlerContext ctx, final int lastStreamId,
986 final long errorCode, final ByteBuf debugData, ChannelFuture future) {
987 try {
988 if (future.isSuccess()) {
989 if (errorCode != NO_ERROR.code()) {
990 if (logger.isDebugEnabled()) {
991 logger.debug("{} Sent GOAWAY: lastStreamId '{}', errorCode '{}', " +
992 "debugData '{}'. Forcing shutdown of the connection.",
993 ctx.channel(), lastStreamId, errorCode, debugData.toString(UTF_8));
994 }
995 ctx.close();
996 }
997 } else {
998 if (logger.isDebugEnabled()) {
999 logger.debug("{} Sending GOAWAY failed: lastStreamId '{}', errorCode '{}', " +
1000 "debugData '{}'. Forcing shutdown of the connection.",
1001 ctx.channel(), lastStreamId, errorCode, debugData.toString(UTF_8), future.cause());
1002 }
1003 ctx.close();
1004 }
1005 } finally {
1006
1007 debugData.release();
1008 }
1009 }
1010
1011
1012
1013
1014 private static final class ClosingChannelFutureListener implements ChannelFutureListener {
1015 private final ChannelHandlerContext ctx;
1016 private final ChannelPromise promise;
1017 private final Future<?> timeoutTask;
1018 private boolean closed;
1019
1020 ClosingChannelFutureListener(ChannelHandlerContext ctx, ChannelPromise promise) {
1021 this.ctx = ctx;
1022 this.promise = promise;
1023 timeoutTask = null;
1024 }
1025
1026 ClosingChannelFutureListener(final ChannelHandlerContext ctx, final ChannelPromise promise,
1027 long timeout, TimeUnit unit) {
1028 this.ctx = ctx;
1029 this.promise = promise;
1030 timeoutTask = ctx.executor().schedule(new Runnable() {
1031 @Override
1032 public void run() {
1033 doClose();
1034 }
1035 }, timeout, unit);
1036 }
1037
1038 @Override
1039 public void operationComplete(ChannelFuture sentGoAwayFuture) {
1040 if (timeoutTask != null) {
1041 timeoutTask.cancel(false);
1042 }
1043 doClose();
1044 }
1045
1046 private void doClose() {
1047
1048
1049 if (closed) {
1050
1051 assert timeoutTask != null;
1052 return;
1053 }
1054 closed = true;
1055 if (promise == null) {
1056 ctx.close();
1057 } else {
1058 ctx.close(promise);
1059 }
1060 }
1061 }
1062
1063 private final class PrefaceSendListener implements ChannelFutureListener {
1064 private final ChannelHandlerContext ctx;
1065 private final ChannelPromise promise;
1066
1067 PrefaceSendListener(ChannelHandlerContext ctx, ChannelPromise promise) {
1068 this.ctx = ctx;
1069 this.promise = promise;
1070 }
1071
1072 @Override
1073 public void operationComplete(ChannelFuture f) {
1074 if (f.isSuccess()) {
1075 try {
1076 if (byteDecoder != null) {
1077 byteDecoder.sendPrefaceIfNeeded(ctx);
1078 }
1079 } catch (Throwable e) {
1080 promise.setFailure(e);
1081 return;
1082 }
1083 promise.setSuccess();
1084 } else {
1085 promise.setFailure(f.cause());
1086 }
1087 }
1088 }
1089 }