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