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(new Http2StreamVisitor() {
228 @Override
229 public boolean visit(Http2Stream stream) {
230 return !flowController.hasFlowControlled(stream);
231 }
232 }) != null;
233 } catch (Http2Exception e) {
234 return false;
235 }
236 }
237
238 private abstract class BaseDecoder {
239 public abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;
240 public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { }
241 public void channelActive(ChannelHandlerContext ctx) throws Exception { }
242
243 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
244
245 encoder().close();
246 decoder().close();
247
248
249
250 connection().close(ctx.voidPromise());
251 }
252
253
254
255
256 public boolean prefaceSent() {
257 return true;
258 }
259
260
261
262
263
264
265
266 public void sendPrefaceIfNeeded(ChannelHandlerContext ctx) throws Exception {
267
268 }
269 }
270
271 private final class PrefaceDecoder extends BaseDecoder {
272 private ByteBuf clientPrefaceString;
273 private boolean prefaceSent;
274
275 PrefaceDecoder(ChannelHandlerContext ctx) throws Exception {
276 clientPrefaceString = clientPrefaceString(encoder.connection());
277
278
279 sendPrefaceIfNeeded(ctx);
280 }
281
282 @Override
283 public boolean prefaceSent() {
284 return prefaceSent;
285 }
286
287 @Override
288 public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
289 try {
290 if (ctx.channel().isActive() && readClientPrefaceString(in) && verifyFirstFrameIsSettings(in)) {
291
292 byteDecoder = new FrameDecoder();
293 byteDecoder.decode(ctx, in, out);
294 }
295 } catch (Throwable e) {
296 if (byteDecoder != null) {
297
298 in.skipBytes(in.readableBytes());
299 }
300 onError(ctx, false, e);
301 }
302 }
303
304 @Override
305 public void channelActive(ChannelHandlerContext ctx) throws Exception {
306
307 sendPrefaceIfNeeded(ctx);
308 }
309
310 @Override
311 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
312 cleanup();
313 super.channelInactive(ctx);
314 }
315
316
317
318
319 @Override
320 public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
321 cleanup();
322 }
323
324
325
326
327 private void cleanup() {
328 if (clientPrefaceString != null) {
329 clientPrefaceString.release();
330 clientPrefaceString = null;
331 }
332 }
333
334
335
336
337
338
339
340 private boolean readClientPrefaceString(ByteBuf in) throws Http2Exception {
341 if (clientPrefaceString == null) {
342 return true;
343 }
344
345 int prefaceRemaining = clientPrefaceString.readableBytes();
346 int bytesRead = min(in.readableBytes(), prefaceRemaining);
347
348
349 if (bytesRead == 0 || !ByteBufUtil.equals(in, in.readerIndex(),
350 clientPrefaceString, clientPrefaceString.readerIndex(),
351 bytesRead)) {
352 int maxSearch = 1024;
353 int http1Index =
354 ByteBufUtil.indexOf(HTTP_1_X_BUF, in.slice(in.readerIndex(), min(in.readableBytes(), maxSearch)));
355 if (http1Index != -1) {
356 String chunk = in.toString(in.readerIndex(), http1Index - in.readerIndex(), CharsetUtil.US_ASCII);
357 throw connectionError(PROTOCOL_ERROR, "Unexpected HTTP/1.x request: %s", chunk);
358 }
359 String receivedBytes = hexDump(in, in.readerIndex(),
360 min(in.readableBytes(), clientPrefaceString.readableBytes()));
361 throw connectionError(PROTOCOL_ERROR, "HTTP/2 client preface string missing or corrupt. " +
362 "Hex dump for received bytes: %s", receivedBytes);
363 }
364 in.skipBytes(bytesRead);
365 clientPrefaceString.skipBytes(bytesRead);
366
367 if (!clientPrefaceString.isReadable()) {
368
369 clientPrefaceString.release();
370 clientPrefaceString = null;
371 return true;
372 }
373 return false;
374 }
375
376
377
378
379
380
381
382
383
384 private boolean verifyFirstFrameIsSettings(ByteBuf in) throws Http2Exception {
385 if (in.readableBytes() < 5) {
386
387 return false;
388 }
389
390 short frameType = in.getUnsignedByte(in.readerIndex() + 3);
391 if (frameType != SETTINGS) {
392 throw connectionError(PROTOCOL_ERROR, "First received frame was not SETTINGS. " +
393 "Hex dump for first 5 bytes: %s",
394 hexDump(in, in.readerIndex(), 5));
395 }
396 short flags = in.getUnsignedByte(in.readerIndex() + 4);
397 if ((flags & Http2Flags.ACK) != 0) {
398 throw connectionError(PROTOCOL_ERROR, "First received frame was SETTINGS frame but had ACK flag set. " +
399 "Hex dump for first 5 bytes: %s",
400 hexDump(in, in.readerIndex(), 5));
401 }
402 return true;
403 }
404
405
406
407
408 @Override
409 public void sendPrefaceIfNeeded(ChannelHandlerContext ctx) throws Exception {
410 if (prefaceSent || !ctx.channel().isActive()) {
411 return;
412 }
413
414 prefaceSent = true;
415
416 final boolean isClient = !connection().isServer();
417 if (isClient) {
418
419 ctx.write(connectionPrefaceBuf()).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
420 }
421
422
423 encoder.writeSettings(ctx, initialSettings, ctx.newPromise()).addListener(
424 ChannelFutureListener.CLOSE_ON_FAILURE);
425
426 try {
427 if (isClient) {
428
429
430
431 userEventTriggered(ctx, Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE);
432 }
433 } finally {
434 if (flushPreface) {
435
436
437
438 ctx.flush();
439 }
440 }
441 }
442 }
443
444 private final class FrameDecoder extends BaseDecoder {
445 @Override
446 public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
447 try {
448 decoder.decodeFrame(ctx, in, out);
449 } catch (Throwable e) {
450 onError(ctx, false, e);
451 }
452 }
453 }
454
455 @Override
456 public void handlerAdded(final ChannelHandlerContext ctx) throws Exception {
457
458 encoder.lifecycleManager(this);
459 decoder.lifecycleManager(this);
460 encoder.flowController().channelHandlerContext(ctx);
461 decoder.flowController().channelHandlerContext(ctx);
462 byteDecoder = new PrefaceDecoder(ctx);
463
464
465
466 connection().addListener(new Http2ConnectionAdapter() {
467 @Override
468 public void onStreamClosed(Http2Stream stream) {
469 checkCloseConnection(ctx.newSucceededFuture());
470 }
471 });
472 }
473
474 @Override
475 protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
476 if (byteDecoder != null) {
477 byteDecoder.handlerRemoved(ctx);
478 byteDecoder = null;
479 }
480 }
481
482 @Override
483 public void channelActive(ChannelHandlerContext ctx) throws Exception {
484 if (byteDecoder == null) {
485 byteDecoder = new PrefaceDecoder(ctx);
486 }
487 byteDecoder.channelActive(ctx);
488 super.channelActive(ctx);
489 }
490
491 @Override
492 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
493
494 super.channelInactive(ctx);
495 if (byteDecoder != null) {
496 byteDecoder.channelInactive(ctx);
497 byteDecoder = null;
498 }
499 }
500
501 @Override
502 public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
503 try {
504
505
506
507 if (ctx.channel().isWritable() && hasPendingData()) {
508 flush(ctx);
509 }
510 encoder.flowController().channelWritabilityChanged();
511 } finally {
512 super.channelWritabilityChanged(ctx);
513 }
514 }
515
516 @Override
517 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
518 byteDecoder.decode(ctx, in, out);
519 }
520
521 @Override
522 public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
523
524
525
526 ctx.bind(localAddress, ctx.newPromise()).addListener(new PrefaceSendListener(ctx, promise));
527 }
528
529 @Override
530 public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
531 ChannelPromise promise) throws Exception {
532
533
534
535 ctx.connect(remoteAddress, localAddress, ctx.newPromise()).addListener(new PrefaceSendListener(ctx, promise));
536 }
537
538 @Override
539 public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
540 ctx.disconnect(promise);
541 }
542
543 @Override
544 public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
545 if (decoupleCloseAndGoAway) {
546 ctx.close(promise);
547 return;
548 }
549 promise = promise.unvoid();
550
551 if (!ctx.channel().isActive() || !prefaceSent()) {
552 ctx.close(promise);
553 return;
554 }
555
556
557
558
559
560
561 ChannelFuture f = connection().goAwaySent() ? ctx.write(EMPTY_BUFFER) : goAway(ctx, null, ctx.newPromise());
562 ctx.flush();
563 doGracefulShutdown(ctx, f, promise);
564 }
565
566 private ChannelFutureListener newClosingChannelFutureListener(
567 ChannelHandlerContext ctx, ChannelPromise promise) {
568 long gracefulShutdownTimeoutMillis = this.gracefulShutdownTimeoutMillis;
569 return gracefulShutdownTimeoutMillis < 0 ?
570 new ClosingChannelFutureListener(ctx, promise) :
571 new ClosingChannelFutureListener(ctx, promise, gracefulShutdownTimeoutMillis, MILLISECONDS);
572 }
573
574 private void doGracefulShutdown(ChannelHandlerContext ctx, ChannelFuture future, final ChannelPromise promise) {
575 final ChannelFutureListener listener = newClosingChannelFutureListener(ctx, promise);
576 if (isGracefulShutdownComplete()) {
577
578
579 future.addListener(listener);
580 } else {
581
582
583
584
585 if (closeListener == null) {
586 closeListener = listener;
587 } else if (promise != null) {
588 final ChannelFutureListener oldCloseListener = closeListener;
589 closeListener = new ChannelFutureListener() {
590 @Override
591 public void operationComplete(ChannelFuture future) throws Exception {
592 try {
593 oldCloseListener.operationComplete(future);
594 } finally {
595 listener.operationComplete(future);
596 }
597 }
598 };
599 }
600 }
601 }
602
603 @Override
604 public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
605 ctx.deregister(promise);
606 }
607
608 @Override
609 public void read(ChannelHandlerContext ctx) throws Exception {
610 ctx.read();
611 }
612
613 @Override
614 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
615 ctx.write(msg, promise);
616 }
617
618 @Override
619 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
620
621
622 try {
623
624 channelReadComplete0(ctx);
625 } finally {
626 flush(ctx);
627 }
628 }
629
630 final void channelReadComplete0(ChannelHandlerContext ctx) {
631
632 discardSomeReadBytes();
633
634
635
636
637 if (!ctx.channel().config().isAutoRead()) {
638 ctx.read();
639 }
640
641 ctx.fireChannelReadComplete();
642 }
643
644
645
646
647 @Override
648 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
649 if (getEmbeddedHttp2Exception(cause) != null) {
650
651 onError(ctx, false, cause);
652 } else {
653 super.exceptionCaught(ctx, cause);
654 }
655 }
656
657
658
659
660
661
662
663
664 @Override
665 public void closeStreamLocal(Http2Stream stream, ChannelFuture future) {
666 switch (stream.state()) {
667 case HALF_CLOSED_LOCAL:
668 case OPEN:
669 stream.closeLocalSide();
670 break;
671 default:
672 closeStream(stream, future);
673 break;
674 }
675 }
676
677
678
679
680
681
682
683
684 @Override
685 public void closeStreamRemote(Http2Stream stream, ChannelFuture future) {
686 switch (stream.state()) {
687 case HALF_CLOSED_REMOTE:
688 case OPEN:
689 stream.closeRemoteSide();
690 break;
691 default:
692 closeStream(stream, future);
693 break;
694 }
695 }
696
697 @Override
698 public void closeStream(final Http2Stream stream, ChannelFuture future) {
699 if (future.isDone()) {
700 doCloseStream(stream, future);
701 } else {
702 future.addListener(new ChannelFutureListener() {
703 @Override
704 public void operationComplete(ChannelFuture future) {
705 doCloseStream(stream, future);
706 }
707 });
708 }
709 }
710
711
712
713
714 @Override
715 public void onError(ChannelHandlerContext ctx, boolean outbound, Throwable cause) {
716 Http2Exception embedded = getEmbeddedHttp2Exception(cause);
717 if (isStreamError(embedded)) {
718 onStreamError(ctx, outbound, cause, (StreamException) embedded);
719 } else if (embedded instanceof CompositeStreamException) {
720 CompositeStreamException compositException = (CompositeStreamException) embedded;
721
722
723
724
725
726
727 Set<Integer> handledStreamIds = new HashSet<Integer>();
728 for (StreamException streamException : compositException) {
729 if (handledStreamIds.add(streamException.streamId())) {
730 onStreamError(ctx, outbound, cause, streamException);
731 }
732 }
733 } else {
734 onConnectionError(ctx, outbound, cause, embedded);
735 }
736 ctx.flush();
737 }
738
739
740
741
742
743
744 protected boolean isGracefulShutdownComplete() {
745 return connection().numActiveStreams() == 0;
746 }
747
748
749
750
751
752
753
754
755
756
757
758 protected void onConnectionError(ChannelHandlerContext ctx, boolean outbound,
759 Throwable cause, Http2Exception http2Ex) {
760 if (http2Ex == null) {
761 http2Ex = new Http2Exception(INTERNAL_ERROR, cause.getMessage(), cause);
762 }
763
764 ChannelPromise promise = ctx.newPromise();
765 ChannelFuture future = goAway(ctx, http2Ex, ctx.newPromise());
766 if (http2Ex.shutdownHint() == Http2Exception.ShutdownHint.GRACEFUL_SHUTDOWN) {
767 doGracefulShutdown(ctx, future, promise);
768 } else {
769 future.addListener(newClosingChannelFutureListener(ctx, promise));
770 }
771 }
772
773
774
775
776
777
778
779
780
781
782 protected void onStreamError(ChannelHandlerContext ctx, boolean outbound,
783 @SuppressWarnings("unused") Throwable cause, StreamException http2Ex) {
784 final int streamId = http2Ex.streamId();
785 Http2Stream stream = connection().stream(streamId);
786
787
788 if (http2Ex instanceof Http2Exception.HeaderListSizeException &&
789 ((Http2Exception.HeaderListSizeException) http2Ex).duringDecode() &&
790 connection().isServer()) {
791
792
793
794
795
796
797
798 if (stream == null) {
799 try {
800 stream = encoder.connection().remote().createStream(streamId, true);
801 } catch (Http2Exception e) {
802 encoder().writeRstStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise());
803 return;
804 }
805 }
806
807
808 if (stream != null && !stream.isHeadersSent()) {
809 try {
810 handleServerHeaderDecodeSizeError(ctx, stream);
811 } catch (Throwable cause2) {
812 onError(ctx, outbound, connectionError(INTERNAL_ERROR, cause2, "Error DecodeSizeError"));
813 }
814 }
815 }
816
817 if (stream == null) {
818 if (!outbound || connection().local().mayHaveCreatedStream(streamId)) {
819 encoder().writeRstStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise());
820 }
821 } else {
822 encoder().writeRstStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise());
823 }
824 }
825
826
827
828
829
830
831
832
833 protected void handleServerHeaderDecodeSizeError(ChannelHandlerContext ctx, Http2Stream stream) {
834 encoder().writeHeaders(ctx, stream.id(), HEADERS_TOO_LARGE_HEADERS, 0, true, ctx.newPromise());
835 }
836
837 protected Http2FrameWriter frameWriter() {
838 return encoder().frameWriter();
839 }
840
841
842
843
844
845
846 private ChannelFuture resetUnknownStream(final ChannelHandlerContext ctx, int streamId, long errorCode,
847 ChannelPromise promise) {
848 ChannelFuture future = frameWriter().writeRstStream(ctx, streamId, errorCode, promise);
849 if (future.isDone()) {
850 closeConnectionOnError(ctx, future);
851 } else {
852 future.addListener(new ChannelFutureListener() {
853 @Override
854 public void operationComplete(ChannelFuture future) throws Exception {
855 closeConnectionOnError(ctx, future);
856 }
857 });
858 }
859 return future;
860 }
861
862 @Override
863 public ChannelFuture resetStream(final ChannelHandlerContext ctx, int streamId, long errorCode,
864 ChannelPromise promise) {
865 final Http2Stream stream = connection().stream(streamId);
866 if (stream == null) {
867 return resetUnknownStream(ctx, streamId, errorCode, promise.unvoid());
868 }
869
870 return resetStream(ctx, stream, errorCode, promise);
871 }
872
873 private ChannelFuture resetStream(final ChannelHandlerContext ctx, final Http2Stream stream,
874 long errorCode, ChannelPromise promise) {
875 promise = promise.unvoid();
876 if (stream.isResetSent()) {
877
878 return promise.setSuccess();
879 }
880
881
882
883
884
885 stream.resetSent();
886
887 final ChannelFuture future;
888
889
890 if (stream.state() == IDLE ||
891 connection().local().created(stream) && !stream.isHeadersSent() && !stream.isPushPromiseSent()) {
892 future = promise.setSuccess();
893 } else {
894 future = frameWriter().writeRstStream(ctx, stream.id(), errorCode, promise);
895 }
896 if (future.isDone()) {
897 processRstStreamWriteResult(ctx, stream, future);
898 } else {
899 future.addListener(new ChannelFutureListener() {
900 @Override
901 public void operationComplete(ChannelFuture future) throws Exception {
902 processRstStreamWriteResult(ctx, stream, future);
903 }
904 });
905 }
906
907 return future;
908 }
909
910 @Override
911 public ChannelFuture goAway(final ChannelHandlerContext ctx, final int lastStreamId, final long errorCode,
912 final ByteBuf debugData, ChannelPromise promise) {
913 promise = promise.unvoid();
914 final Http2Connection connection = connection();
915 try {
916 if (!connection.goAwaySent(lastStreamId, errorCode, debugData)) {
917 debugData.release();
918 promise.trySuccess();
919 return promise;
920 }
921 } catch (Throwable cause) {
922 debugData.release();
923 promise.tryFailure(cause);
924 return promise;
925 }
926
927
928
929 debugData.retain();
930 ChannelFuture future = frameWriter().writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
931
932 if (future.isDone()) {
933 processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future);
934 } else {
935 future.addListener(new ChannelFutureListener() {
936 @Override
937 public void operationComplete(ChannelFuture future) throws Exception {
938 processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future);
939 }
940 });
941 }
942
943 return future;
944 }
945
946
947
948
949
950 private void checkCloseConnection(ChannelFuture future) {
951
952
953 if (closeListener != null && isGracefulShutdownComplete()) {
954 ChannelFutureListener closeListener = this.closeListener;
955
956
957 this.closeListener = null;
958 try {
959 closeListener.operationComplete(future);
960 } catch (Exception e) {
961 throw new IllegalStateException("Close listener threw an unexpected exception", e);
962 }
963 }
964 }
965
966
967
968
969
970 private ChannelFuture goAway(ChannelHandlerContext ctx, Http2Exception cause, ChannelPromise promise) {
971 long errorCode = cause != null ? cause.error().code() : NO_ERROR.code();
972 int lastKnownStream;
973 if (cause != null && cause.shutdownHint() == Http2Exception.ShutdownHint.HARD_SHUTDOWN) {
974
975
976
977
978 lastKnownStream = Integer.MAX_VALUE;
979 } else {
980 lastKnownStream = connection().remote().lastStreamCreated();
981 }
982 return goAway(ctx, lastKnownStream, errorCode, Http2CodecUtil.toByteBuf(ctx, cause), promise);
983 }
984
985 private void processRstStreamWriteResult(ChannelHandlerContext ctx, Http2Stream stream, ChannelFuture future) {
986 if (future.isSuccess()) {
987 closeStream(stream, future);
988 } else {
989
990 onConnectionError(ctx, true, future.cause(), null);
991 }
992 }
993
994 private void closeConnectionOnError(ChannelHandlerContext ctx, ChannelFuture future) {
995 if (!future.isSuccess()) {
996 onConnectionError(ctx, true, future.cause(), null);
997 }
998 }
999
1000 private void doCloseStream(final Http2Stream stream, ChannelFuture future) {
1001 stream.close();
1002 checkCloseConnection(future);
1003 }
1004
1005
1006
1007
1008 private static ByteBuf clientPrefaceString(Http2Connection connection) {
1009 return connection.isServer() ? connectionPrefaceBuf() : null;
1010 }
1011
1012 private static void processGoAwayWriteResult(final ChannelHandlerContext ctx, final int lastStreamId,
1013 final long errorCode, final ByteBuf debugData, ChannelFuture future) {
1014 try {
1015 if (future.isSuccess()) {
1016 if (errorCode != NO_ERROR.code()) {
1017 if (logger.isDebugEnabled()) {
1018 logger.debug("{} Sent GOAWAY: lastStreamId '{}', errorCode '{}', " +
1019 "debugData '{}'. Forcing shutdown of the connection.",
1020 ctx.channel(), lastStreamId, errorCode, debugData.toString(UTF_8));
1021 }
1022 ctx.close();
1023 }
1024 } else {
1025 if (logger.isDebugEnabled()) {
1026 logger.debug("{} Sending GOAWAY failed: lastStreamId '{}', errorCode '{}', " +
1027 "debugData '{}'. Forcing shutdown of the connection.",
1028 ctx.channel(), lastStreamId, errorCode, debugData.toString(UTF_8), future.cause());
1029 }
1030 ctx.close();
1031 }
1032 } finally {
1033
1034 debugData.release();
1035 }
1036 }
1037
1038
1039
1040
1041 private static final class ClosingChannelFutureListener implements ChannelFutureListener {
1042 private final ChannelHandlerContext ctx;
1043 private final ChannelPromise promise;
1044 private final Future<?> timeoutTask;
1045 private boolean closed;
1046
1047 ClosingChannelFutureListener(ChannelHandlerContext ctx, ChannelPromise promise) {
1048 this.ctx = ctx;
1049 this.promise = promise;
1050 timeoutTask = null;
1051 }
1052
1053 ClosingChannelFutureListener(final ChannelHandlerContext ctx, final ChannelPromise promise,
1054 long timeout, TimeUnit unit) {
1055 this.ctx = ctx;
1056 this.promise = promise;
1057 timeoutTask = ctx.executor().schedule(new Runnable() {
1058 @Override
1059 public void run() {
1060 doClose();
1061 }
1062 }, timeout, unit);
1063 }
1064
1065 @Override
1066 public void operationComplete(ChannelFuture sentGoAwayFuture) {
1067 if (timeoutTask != null) {
1068 timeoutTask.cancel(false);
1069 }
1070 doClose();
1071 }
1072
1073 private void doClose() {
1074
1075
1076 if (closed) {
1077
1078 assert timeoutTask != null;
1079 return;
1080 }
1081 closed = true;
1082 if (promise == null) {
1083 ctx.close();
1084 } else {
1085 ctx.close(promise);
1086 }
1087 }
1088 }
1089
1090 private final class PrefaceSendListener implements ChannelFutureListener {
1091 private final ChannelHandlerContext ctx;
1092 private final ChannelPromise promise;
1093
1094 PrefaceSendListener(ChannelHandlerContext ctx, ChannelPromise promise) {
1095 this.ctx = ctx;
1096 this.promise = promise;
1097 }
1098
1099 @Override
1100 public void operationComplete(ChannelFuture f) {
1101 if (f.isSuccess()) {
1102 try {
1103 if (byteDecoder != null) {
1104 byteDecoder.sendPrefaceIfNeeded(ctx);
1105 }
1106 } catch (Throwable e) {
1107 promise.setFailure(e);
1108 return;
1109 }
1110 promise.setSuccess();
1111 } else {
1112 promise.setFailure(f.cause());
1113 }
1114 }
1115 }
1116 }