View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
5    * "License"); you may not use this file except in compliance with the License. You may obtain a
6    * copy of the License at:
7    *
8    * https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software distributed under the License
11   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing permissions and limitations under
13   * the License.
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   * Provides the default implementation for processing inbound frame events and delegates to a
59   * {@link Http2FrameListener}
60   * <p>
61   * This class will read HTTP/2 frames and delegate the events to a {@link Http2FrameListener}
62   * <p>
63   * This interface enforces inbound flow control functionality through
64   * {@link Http2LocalFlowController}
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      * Get the amount of time (in milliseconds) this endpoint will wait for all streams to be closed before closing
112      * the connection during the graceful shutdown process. Returns -1 if this connection is configured to wait
113      * indefinitely for all streams to close.
114      */
115     public long gracefulShutdownTimeoutMillis() {
116         return gracefulShutdownTimeoutMillis;
117     }
118 
119     /**
120      * Set the amount of time (in milliseconds) this endpoint will wait for all streams to be closed before closing
121      * the connection during the graceful shutdown process.
122      * @param gracefulShutdownTimeoutMillis the amount of time (in milliseconds) this endpoint will wait for all
123      * streams to be closed before closing the connection during the graceful shutdown process.
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      * Handles the client-side (cleartext) upgrade from HTTP to HTTP/2.
151      * Reserves local stream 1 for the HTTP/2 response.
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             // If the preface was not sent yet it most likely means the handler was not added to the pipeline before
159             // calling this method.
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         // Create a local stream used for the HTTP cleartext upgrade.
167         connection().local().createStream(HTTP_UPGRADE_STREAM_ID, true);
168     }
169 
170     /**
171      * Handles the server-side (cleartext) upgrade from HTTP to HTTP/2.
172      * @param settings the settings for the remote endpoint.
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             // If the preface was not sent yet it most likely means the handler was not added to the pipeline before
180             // calling this method.
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         // Apply the settings but no ACK is necessary.
188         encoder.remoteSettings(settings);
189 
190         // Create a stream in the half-closed state.
191         connection().remote().createStream(HTTP_UPGRADE_STREAM_ID, true);
192     }
193 
194     @Override
195     public void flush(ChannelHandlerContext ctx) {
196         if (inFlush) {
197             // Reentrant flush (e.g. from a synchronous writability change) must not recurse — that livelocks
198             // (#17256); the in-progress flush loop picks it up.
199             flushAgain = true;
200             return;
201         }
202         inFlush = true;
203         try {
204             do {
205                 flushAgain = false;
206                 // Trigger pending writes in the remote flow controller.
207                 encoder.flowController().writePendingBytes();
208                 ctx.flush();
209                 // Honor any flush re-requested while we were flushing (a resumed write, or a direct flush from
210                 // elsewhere in the pipeline) regardless of writability — flushing already-written data doesn't
211                 // need a writable channel.
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             // Stop at the first stream that still has a flow-controlled frame queued. Frame-based, so it counts
226             // zero-length frames (e.g. trailing headers) that carry no flow-control bytes.
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             // Connection has terminated, close the encoder and decoder.
245             encoder().close();
246             decoder().close();
247 
248             // We need to remove all streams (not just the active ones).
249             // See https://github.com/netty/netty/issues/4838.
250             connection().close(ctx.voidPromise());
251         }
252 
253         /**
254          * Determine if the HTTP/2 connection preface been sent.
255          */
256         public boolean prefaceSent() {
257             return true;
258         }
259 
260         /**
261          * Send the preface if needed.
262          *
263          * @param ctx           the {@link ChannelHandlerContext} to use.
264          * @throws Exception    thrown on error.
265          */
266         public void sendPrefaceIfNeeded(ChannelHandlerContext ctx) throws Exception {
267             // Noop by default.
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             // This handler was just added to the context. In case it was handled after
278             // the connection became active, send the connection preface now.
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                     // After the preface is read, it is time to hand over control to the post initialized decoder.
292                     byteDecoder = new FrameDecoder();
293                     byteDecoder.decode(ctx, in, out);
294                 }
295             } catch (Throwable e) {
296                 if (byteDecoder != null) {
297                     // Skip all bytes before we report the exception as
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             // The channel just became active - send the connection preface to the remote endpoint.
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          * Releases the {@code clientPrefaceString}. Any active streams will be left in the open.
318          */
319         @Override
320         public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
321             cleanup();
322         }
323 
324         /**
325          * Releases the {@code clientPrefaceString}. Any active streams will be left in the open.
326          */
327         private void cleanup() {
328             if (clientPrefaceString != null) {
329                 clientPrefaceString.release();
330                 clientPrefaceString = null;
331             }
332         }
333 
334         /**
335          * Decodes the client connection preface string from the input buffer.
336          *
337          * @return {@code true} if processing of the client preface string is complete. Since client preface strings can
338          *         only be received by servers, returns true immediately for client endpoints.
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             // If the input so far doesn't match the preface, break the connection.
349             if (bytesRead == 0 || !ByteBufUtil.equals(in, in.readerIndex(),
350                                                       clientPrefaceString, clientPrefaceString.readerIndex(),
351                                                       bytesRead)) {
352                 int maxSearch = 1024; // picked because 512 is too little, and 2048 too much
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                 // Entire preface has been read.
369                 clientPrefaceString.release();
370                 clientPrefaceString = null;
371                 return true;
372             }
373             return false;
374         }
375 
376         /**
377          * Peeks at that the next frame in the buffer and verifies that it is a non-ack {@code SETTINGS} frame.
378          *
379          * @param in the inbound buffer.
380          * @return {@code true} if the next frame is a non-ack {@code SETTINGS} frame, {@code false} if more
381          * data is required before we can determine the next frame type.
382          * @throws Http2Exception thrown if the next frame is NOT a non-ack {@code SETTINGS} frame.
383          */
384         private boolean verifyFirstFrameIsSettings(ByteBuf in) throws Http2Exception {
385             if (in.readableBytes() < 5) {
386                 // Need more data before we can see the frame type for the first frame.
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          * Sends the HTTP/2 connection preface upon establishment of the connection, if not already sent.
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                 // Clients must send the preface string as the first bytes on the connection.
419                 ctx.write(connectionPrefaceBuf()).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
420             }
421 
422             // Both client and server must send their initial settings.
423             encoder.writeSettings(ctx, initialSettings, ctx.newPromise()).addListener(
424                     ChannelFutureListener.CLOSE_ON_FAILURE);
425 
426             try {
427                 if (isClient) {
428                     // If this handler is extended by the user and we directly fire the userEvent from this context then
429                     // the user will not see the event. We should fire the event starting with this handler so this
430                     // class (and extending classes) have a chance to process the event.
431                     userEventTriggered(ctx, Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE);
432                 }
433             } finally {
434                 if (flushPreface) {
435                     // As we don't know if any channelReadComplete() events will be triggered at all we need to ensure
436                     // we also flush. Otherwise the remote peer might never see the preface / settings frame.
437                     // See https://github.com/netty/netty/issues/12089
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         // Initialize the encoder, decoder, flow controllers, and internal state.
458         encoder.lifecycleManager(this);
459         decoder.lifecycleManager(this);
460         encoder.flowController().channelHandlerContext(ctx);
461         decoder.flowController().channelHandlerContext(ctx);
462         byteDecoder = new PrefaceDecoder(ctx);
463         // Streams are not only closed via closeStream(...): the connection also closes them itself, for example
464         // when a GOAWAY is received or the channel becomes inactive. Check if a pending graceful shutdown can
465         // complete whenever a stream is closed, whatever the reason.
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         // Call super class first, as this may result in decode being called.
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             // Only flush on a writability change if the flow controller has frames queued. A toggle with
505             // nothing to write (e.g. SslHandler during setup) must not flush, or the flush -> writability ->
506             // flush cycle livelocks (#17256).
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         // Ensure we send the preface before we notify the bind promise as the user might try to write
524         // directly in the listener attached to the promise and we need to ensure the preface is always the first
525         // thing that is written.
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         // Ensure we send the preface before we notify the connect promise as the user might try to write
533         // directly in the listener attached to the promise and we need to ensure the preface is always the first
534         // thing that is written.
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         // Avoid NotYetConnectedException and avoid sending before connection preface
551         if (!ctx.channel().isActive() || !prefaceSent()) {
552             ctx.close(promise);
553             return;
554         }
555 
556         // If the user has already sent a GO_AWAY frame they may be attempting to do a graceful shutdown which requires
557         // sending multiple GO_AWAY frames. We should only send a GO_AWAY here if one has not already been sent. If
558         // a GO_AWAY has been sent we send a empty buffer just so we can wait to close until all other data has been
559         // flushed to the OS.
560         // https://github.com/netty/netty/issues/5307
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             // If there are no active streams, close immediately after the GO_AWAY write completes or the timeout
578             // elapsed.
579             future.addListener(listener);
580         } else {
581             // If there are active streams we should wait until they are all closed before closing the connection.
582 
583             // The ClosingChannelFutureListener will cascade promise completion. We need to always notify the
584             // new ClosingChannelFutureListener when the graceful close completes if the promise is not null.
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         // Trigger flush after read on the assumption that flush is cheap if there is nothing to write and that
621         // for flow-control the read may release window that causes data to be written that can now be flushed.
622         try {
623             // First call channelReadComplete0(...) as this may produce more data that we want to flush
624             channelReadComplete0(ctx);
625         } finally {
626             flush(ctx);
627         }
628     }
629 
630     final void channelReadComplete0(ChannelHandlerContext ctx) {
631         // Discard bytes of the cumulation buffer if needed.
632         discardSomeReadBytes();
633 
634         // Ensure we never stale the HTTP/2 Channel. Flow-control is enforced by HTTP/2.
635         //
636         // See https://tools.ietf.org/html/rfc7540#section-5.2.2
637         if (!ctx.channel().config().isAutoRead()) {
638             ctx.read();
639         }
640 
641         ctx.fireChannelReadComplete();
642     }
643 
644     /**
645      * Handles {@link Http2Exception} objects that were thrown from other handlers. Ignores all other exceptions.
646      */
647     @Override
648     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
649         if (getEmbeddedHttp2Exception(cause) != null) {
650             // Some exception in the causality chain is an Http2Exception - handle it.
651             onError(ctx, false, cause);
652         } else {
653             super.exceptionCaught(ctx, cause);
654         }
655     }
656 
657     /**
658      * Closes the local side of the given stream. If this causes the stream to be closed, adds a
659      * hook to close the channel after the given future completes.
660      *
661      * @param stream the stream to be half closed.
662      * @param future If closing, the future after which to close the channel.
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      * Closes the remote side of the given stream. If this causes the stream to be closed, adds a
679      * hook to close the channel after the given future completes.
680      *
681      * @param stream the stream to be half closed.
682      * @param future If closing, the future after which to close the channel.
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      * Central handler for all exceptions caught during HTTP/2 processing.
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             // Each contained StreamException is generally an independent error for a distinct stream (e.g.
722             // one per active stream that overflowed its flow-control window when SETTINGS_INITIAL_WINDOW_SIZE
723             // changed), so every affected stream must still be reset individually; otherwise it would be left
724             // open with a corrupted flow-control window. Should the composite ever contain more than one
725             // exception for the same stream id, only report the first one, per RFC 9113, Section 5.4's
726             // guidance to report at most one stream error per stream.
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      * Called by the graceful shutdown logic to determine when it is safe to close the connection. Returns {@code true}
741      * if the graceful shutdown has completed and the connection can be safely closed. This implementation just
742      * guarantees that there are no active streams. Subclasses may override to provide additional checks.
743      */
744     protected boolean isGracefulShutdownComplete() {
745         return connection().numActiveStreams() == 0;
746     }
747 
748     /**
749      * Handler for a connection error. Sends a GO_AWAY frame to the remote endpoint. Once all
750      * streams are closed, the connection is shut down.
751      *
752      * @param ctx the channel context
753      * @param outbound {@code true} if the error was caused by an outbound operation.
754      * @param cause the exception that was caught
755      * @param http2Ex the {@link Http2Exception} that is embedded in the causality chain. This may
756      *            be {@code null} if it's an unknown exception.
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      * Handler for a stream error. Sends a {@code RST_STREAM} frame to the remote endpoint and closes the
775      * stream.
776      *
777      * @param ctx the channel context
778      * @param outbound {@code true} if the error was caused by an outbound operation.
779      * @param cause the exception that was caught
780      * @param http2Ex the {@link StreamException} that is embedded in the causality chain.
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         //if this is caused by reading headers that are too large, send a header with status 431
788         if (http2Ex instanceof Http2Exception.HeaderListSizeException &&
789             ((Http2Exception.HeaderListSizeException) http2Ex).duringDecode() &&
790             connection().isServer()) {
791 
792             // NOTE We have to check to make sure that a stream exists before we send our reply.
793             // We likely always create the stream below as the stream isn't created until the
794             // header block is completely processed.
795 
796             // The case of a streamId referring to a stream which was already closed is handled
797             // by createStream and will land us in the catch block below
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             // ensure that we have not already sent headers on this stream
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      * Notifies client that this server has received headers that are larger than what it is
828      * willing to accept. Override to change behavior.
829      *
830      * @param ctx the channel context
831      * @param stream the Http2Stream on which the header was received
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      * Sends a {@code RST_STREAM} frame even if we don't know about the stream. This error condition is most likely
843      * triggered by the first frame of a stream being invalid. That is, there was an error reading the frame before
844      * we could create a new stream.
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             // Don't write a RST_STREAM frame if we have already written one.
878             return promise.setSuccess();
879         }
880         // Synchronously set the resetSent flag to prevent any subsequent calls
881         // from resulting in multiple reset frames being sent.
882         //
883         // This needs to be done before we notify the promise as the promise may have a listener attached that
884         // call resetStream(...) again.
885         stream.resetSent();
886 
887         final ChannelFuture future;
888         // If the remote peer is not aware of the steam, then we are not allowed to send a RST_STREAM
889         // https://tools.ietf.org/html/rfc7540#section-6.4.
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         // Need to retain before we write the buffer because if we do it after the refCnt could already be 0 and
928         // result in an IllegalRefCountException.
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      * Closes the connection if the graceful shutdown process has completed.
948      * @param future Represents the status that will be passed to the {@link #closeListener}.
949      */
950     private void checkCloseConnection(ChannelFuture future) {
951         // If this connection is closing and the graceful shutdown has completed, close the connection
952         // once this operation completes.
953         if (closeListener != null && isGracefulShutdownComplete()) {
954             ChannelFutureListener closeListener = this.closeListener;
955             // This method could be called multiple times
956             // and we don't want to notify the closeListener multiple times.
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      * Close the remote endpoint with a {@code GO_AWAY} frame. Does <strong>not</strong> flush
968      * immediately, this is the responsibility of the caller.
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             // The hard shutdown could have been triggered during header processing, before updating
975             // lastStreamCreated(). Specifically, any connection errors encountered by Http2FrameReader or HPACK
976             // decoding will fail to update the last known stream. So we must be pessimistic.
977             // https://github.com/netty/netty/issues/10670
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             // The connection will be closed and so no need to change the resetSent flag to false.
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      * Returns the client preface string if this is a client connection, otherwise returns {@code null}.
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             // We're done with the debug data now.
1034             debugData.release();
1035         }
1036     }
1037 
1038     /**
1039      * Closes the channel when the future completes.
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             // We need to guard against multiple calls as the timeout may trigger close() first and then it will be
1075             // triggered again because of operationComplete(...) is called.
1076             if (closed) {
1077                 // This only happens if we also scheduled a timeout task.
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 }