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(stream -> !flowController.hasFlowControlled(stream)) != null;
228         } catch (Http2Exception e) {
229             return false;
230         }
231     }
232 
233     private abstract class BaseDecoder {
234         public abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;
235         public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { }
236         public void channelActive(ChannelHandlerContext ctx) throws Exception { }
237 
238         public void channelInactive(ChannelHandlerContext ctx) throws Exception {
239             // Connection has terminated, close the encoder and decoder.
240             encoder().close();
241             decoder().close();
242 
243             // We need to remove all streams (not just the active ones).
244             // See https://github.com/netty/netty/issues/4838.
245             connection().close(ctx.voidPromise());
246         }
247 
248         /**
249          * Determine if the HTTP/2 connection preface been sent.
250          */
251         public boolean prefaceSent() {
252             return true;
253         }
254 
255         /**
256          * Send the preface if needed.
257          *
258          * @param ctx           the {@link ChannelHandlerContext} to use.
259          * @throws Exception    thrown on error.
260          */
261         public void sendPrefaceIfNeeded(ChannelHandlerContext ctx) throws Exception {
262             // Noop by default.
263         }
264     }
265 
266     private final class PrefaceDecoder extends BaseDecoder {
267         private ByteBuf clientPrefaceString;
268         private boolean prefaceSent;
269 
270         PrefaceDecoder(ChannelHandlerContext ctx) throws Exception {
271             clientPrefaceString = clientPrefaceString(encoder.connection());
272             // This handler was just added to the context. In case it was handled after
273             // the connection became active, send the connection preface now.
274             sendPrefaceIfNeeded(ctx);
275         }
276 
277         @Override
278         public boolean prefaceSent() {
279             return prefaceSent;
280         }
281 
282         @Override
283         public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
284             try {
285                 if (ctx.channel().isActive() && readClientPrefaceString(in) && verifyFirstFrameIsSettings(in)) {
286                     // After the preface is read, it is time to hand over control to the post initialized decoder.
287                     byteDecoder = new FrameDecoder();
288                     byteDecoder.decode(ctx, in, out);
289                 }
290             } catch (Throwable e) {
291                 if (byteDecoder != null) {
292                     // Skip all bytes before we report the exception as
293                     in.skipBytes(in.readableBytes());
294                 }
295                 onError(ctx, false, e);
296             }
297         }
298 
299         @Override
300         public void channelActive(ChannelHandlerContext ctx) throws Exception {
301             // The channel just became active - send the connection preface to the remote endpoint.
302             sendPrefaceIfNeeded(ctx);
303         }
304 
305         @Override
306         public void channelInactive(ChannelHandlerContext ctx) throws Exception {
307             cleanup();
308             super.channelInactive(ctx);
309         }
310 
311         /**
312          * Releases the {@code clientPrefaceString}. Any active streams will be left in the open.
313          */
314         @Override
315         public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
316             cleanup();
317         }
318 
319         /**
320          * Releases the {@code clientPrefaceString}. Any active streams will be left in the open.
321          */
322         private void cleanup() {
323             if (clientPrefaceString != null) {
324                 clientPrefaceString.release();
325                 clientPrefaceString = null;
326             }
327         }
328 
329         /**
330          * Decodes the client connection preface string from the input buffer.
331          *
332          * @return {@code true} if processing of the client preface string is complete. Since client preface strings can
333          *         only be received by servers, returns true immediately for client endpoints.
334          */
335         private boolean readClientPrefaceString(ByteBuf in) throws Http2Exception {
336             if (clientPrefaceString == null) {
337                 return true;
338             }
339 
340             int prefaceRemaining = clientPrefaceString.readableBytes();
341             int bytesRead = min(in.readableBytes(), prefaceRemaining);
342 
343             // If the input so far doesn't match the preface, break the connection.
344             if (bytesRead == 0 || !ByteBufUtil.equals(in, in.readerIndex(),
345                                                       clientPrefaceString, clientPrefaceString.readerIndex(),
346                                                       bytesRead)) {
347                 int maxSearch = 1024; // picked because 512 is too little, and 2048 too much
348                 int http1Index =
349                     ByteBufUtil.indexOf(HTTP_1_X_BUF, in.slice(in.readerIndex(), min(in.readableBytes(), maxSearch)));
350                 if (http1Index != -1) {
351                     String chunk = in.toString(in.readerIndex(), http1Index - in.readerIndex(), CharsetUtil.US_ASCII);
352                     throw connectionError(PROTOCOL_ERROR, "Unexpected HTTP/1.x request: %s", chunk);
353                 }
354                 String receivedBytes = hexDump(in, in.readerIndex(),
355                                                min(in.readableBytes(), clientPrefaceString.readableBytes()));
356                 throw connectionError(PROTOCOL_ERROR, "HTTP/2 client preface string missing or corrupt. " +
357                                                       "Hex dump for received bytes: %s", receivedBytes);
358             }
359             in.skipBytes(bytesRead);
360             clientPrefaceString.skipBytes(bytesRead);
361 
362             if (!clientPrefaceString.isReadable()) {
363                 // Entire preface has been read.
364                 clientPrefaceString.release();
365                 clientPrefaceString = null;
366                 return true;
367             }
368             return false;
369         }
370 
371         /**
372          * Peeks at that the next frame in the buffer and verifies that it is a non-ack {@code SETTINGS} frame.
373          *
374          * @param in the inbound buffer.
375          * @return {@code true} if the next frame is a non-ack {@code SETTINGS} frame, {@code false} if more
376          * data is required before we can determine the next frame type.
377          * @throws Http2Exception thrown if the next frame is NOT a non-ack {@code SETTINGS} frame.
378          */
379         private boolean verifyFirstFrameIsSettings(ByteBuf in) throws Http2Exception {
380             if (in.readableBytes() < 5) {
381                 // Need more data before we can see the frame type for the first frame.
382                 return false;
383             }
384 
385             short frameType = in.getUnsignedByte(in.readerIndex() + 3);
386             if (frameType != SETTINGS) {
387                 throw connectionError(PROTOCOL_ERROR, "First received frame was not SETTINGS. " +
388                                                       "Hex dump for first 5 bytes: %s",
389                                       hexDump(in, in.readerIndex(), 5));
390             }
391             short flags = in.getUnsignedByte(in.readerIndex() + 4);
392             if ((flags & Http2Flags.ACK) != 0) {
393                 throw connectionError(PROTOCOL_ERROR, "First received frame was SETTINGS frame but had ACK flag set. " +
394                         "Hex dump for first 5 bytes: %s",
395                         hexDump(in, in.readerIndex(), 5));
396             }
397             return true;
398         }
399 
400         /**
401          * Sends the HTTP/2 connection preface upon establishment of the connection, if not already sent.
402          */
403         @Override
404         public void sendPrefaceIfNeeded(ChannelHandlerContext ctx) throws Exception {
405             if (prefaceSent || !ctx.channel().isActive()) {
406                 return;
407             }
408 
409             prefaceSent = true;
410 
411             final boolean isClient = !connection().isServer();
412             if (isClient) {
413                 // Clients must send the preface string as the first bytes on the connection.
414                 ctx.write(connectionPrefaceBuf()).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
415             }
416 
417             // Both client and server must send their initial settings.
418             encoder.writeSettings(ctx, initialSettings, ctx.newPromise()).addListener(
419                     ChannelFutureListener.CLOSE_ON_FAILURE);
420 
421             try {
422                 if (isClient) {
423                     // If this handler is extended by the user and we directly fire the userEvent from this context then
424                     // the user will not see the event. We should fire the event starting with this handler so this
425                     // class (and extending classes) have a chance to process the event.
426                     userEventTriggered(ctx, Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE);
427                 }
428             } finally {
429                 if (flushPreface) {
430                     // As we don't know if any channelReadComplete() events will be triggered at all we need to ensure
431                     // we also flush. Otherwise the remote peer might never see the preface / settings frame.
432                     // See https://github.com/netty/netty/issues/12089
433                     ctx.flush();
434                 }
435             }
436         }
437     }
438 
439     private final class FrameDecoder extends BaseDecoder {
440         @Override
441         public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
442             try {
443                 decoder.decodeFrame(ctx, in, out);
444             } catch (Throwable e) {
445                 onError(ctx, false, e);
446             }
447         }
448     }
449 
450     @Override
451     public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
452         // Initialize the encoder, decoder, flow controllers, and internal state.
453         encoder.lifecycleManager(this);
454         decoder.lifecycleManager(this);
455         encoder.flowController().channelHandlerContext(ctx);
456         decoder.flowController().channelHandlerContext(ctx);
457         byteDecoder = new PrefaceDecoder(ctx);
458         // Streams are not only closed via closeStream(...): the connection also closes them itself, for example
459         // when a GOAWAY is received or the channel becomes inactive. Check if a pending graceful shutdown can
460         // complete whenever a stream is closed, whatever the reason.
461         connection().addListener(new Http2ConnectionAdapter() {
462             @Override
463             public void onStreamClosed(Http2Stream stream) {
464                 checkCloseConnection(ctx.newSucceededFuture());
465             }
466         });
467     }
468 
469     @Override
470     protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
471         if (byteDecoder != null) {
472             byteDecoder.handlerRemoved(ctx);
473             byteDecoder = null;
474         }
475     }
476 
477     @Override
478     public void channelActive(ChannelHandlerContext ctx) throws Exception {
479         if (byteDecoder == null) {
480             byteDecoder = new PrefaceDecoder(ctx);
481         }
482         byteDecoder.channelActive(ctx);
483         super.channelActive(ctx);
484     }
485 
486     @Override
487     public void channelInactive(ChannelHandlerContext ctx) throws Exception {
488         // Call super class first, as this may result in decode being called.
489         super.channelInactive(ctx);
490         if (byteDecoder != null) {
491             byteDecoder.channelInactive(ctx);
492             byteDecoder = null;
493         }
494     }
495 
496     @Override
497     public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
498         try {
499             // Only flush on a writability change if the flow controller has frames queued. A toggle with
500             // nothing to write (e.g. SslHandler during setup) must not flush, or the flush -> writability ->
501             // flush cycle livelocks (#17256).
502             if (ctx.channel().isWritable() && hasPendingData()) {
503                 flush(ctx);
504             }
505             encoder.flowController().channelWritabilityChanged();
506         } finally {
507             super.channelWritabilityChanged(ctx);
508         }
509     }
510 
511     @Override
512     protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
513         byteDecoder.decode(ctx, in, out);
514     }
515 
516     @Override
517     public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
518         // Ensure we send the preface before we notify the bind promise as the user might try to write
519         // directly in the listener attached to the promise and we need to ensure the preface is always the first
520         // thing that is written.
521         ctx.bind(localAddress, ctx.newPromise()).addListener(new PrefaceSendListener(ctx, promise));
522     }
523 
524     @Override
525     public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
526                         ChannelPromise promise) throws Exception {
527         // Ensure we send the preface before we notify the connect promise as the user might try to write
528         // directly in the listener attached to the promise and we need to ensure the preface is always the first
529         // thing that is written.
530         ctx.connect(remoteAddress, localAddress, ctx.newPromise()).addListener(new PrefaceSendListener(ctx, promise));
531     }
532 
533     @Override
534     public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
535         ctx.disconnect(promise);
536     }
537 
538     @Override
539     public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
540         if (decoupleCloseAndGoAway) {
541             ctx.close(promise);
542             return;
543         }
544         promise = promise.unvoid();
545         // Avoid NotYetConnectedException and avoid sending before connection preface
546         if (!ctx.channel().isActive() || !prefaceSent()) {
547             ctx.close(promise);
548             return;
549         }
550 
551         // If the user has already sent a GO_AWAY frame they may be attempting to do a graceful shutdown which requires
552         // sending multiple GO_AWAY frames. We should only send a GO_AWAY here if one has not already been sent. If
553         // a GO_AWAY has been sent we send a empty buffer just so we can wait to close until all other data has been
554         // flushed to the OS.
555         // https://github.com/netty/netty/issues/5307
556         ChannelFuture f = connection().goAwaySent() ? ctx.write(EMPTY_BUFFER) : goAway(ctx, null, ctx.newPromise());
557         ctx.flush();
558         doGracefulShutdown(ctx, f, promise);
559     }
560 
561     private ChannelFutureListener newClosingChannelFutureListener(
562             ChannelHandlerContext ctx, ChannelPromise promise) {
563         long gracefulShutdownTimeoutMillis = this.gracefulShutdownTimeoutMillis;
564         return gracefulShutdownTimeoutMillis < 0 ?
565                 new ClosingChannelFutureListener(ctx, promise) :
566                 new ClosingChannelFutureListener(ctx, promise, gracefulShutdownTimeoutMillis, MILLISECONDS);
567     }
568 
569     private void doGracefulShutdown(ChannelHandlerContext ctx, ChannelFuture future, final ChannelPromise promise) {
570         final ChannelFutureListener listener = newClosingChannelFutureListener(ctx, promise);
571         if (isGracefulShutdownComplete()) {
572             // If there are no active streams, close immediately after the GO_AWAY write completes or the timeout
573             // elapsed.
574             future.addListener(listener);
575         } else {
576             // If there are active streams we should wait until they are all closed before closing the connection.
577 
578             // The ClosingChannelFutureListener will cascade promise completion. We need to always notify the
579             // new ClosingChannelFutureListener when the graceful close completes if the promise is not null.
580             if (closeListener == null) {
581                 closeListener = listener;
582             } else if (promise != null) {
583                 final ChannelFutureListener oldCloseListener = closeListener;
584                 closeListener = future1 -> {
585                     try {
586                         oldCloseListener.operationComplete(future1);
587                     } finally {
588                         listener.operationComplete(future1);
589                     }
590                 };
591             }
592         }
593     }
594 
595     @Override
596     public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
597         ctx.deregister(promise);
598     }
599 
600     @Override
601     public void read(ChannelHandlerContext ctx) throws Exception {
602         ctx.read();
603     }
604 
605     @Override
606     public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
607         ctx.write(msg, promise);
608     }
609 
610     @Override
611     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
612         // Trigger flush after read on the assumption that flush is cheap if there is nothing to write and that
613         // for flow-control the read may release window that causes data to be written that can now be flushed.
614         try {
615             // First call channelReadComplete0(...) as this may produce more data that we want to flush
616             channelReadComplete0(ctx);
617         } finally {
618             flush(ctx);
619         }
620     }
621 
622     final void channelReadComplete0(ChannelHandlerContext ctx) {
623         // Discard bytes of the cumulation buffer if needed.
624         discardSomeReadBytes();
625 
626         // Ensure we never stale the HTTP/2 Channel. Flow-control is enforced by HTTP/2.
627         //
628         // See https://tools.ietf.org/html/rfc7540#section-5.2.2
629         if (!ctx.channel().config().isAutoRead()) {
630             ctx.read();
631         }
632 
633         ctx.fireChannelReadComplete();
634     }
635 
636     /**
637      * Handles {@link Http2Exception} objects that were thrown from other handlers. Ignores all other exceptions.
638      */
639     @Override
640     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
641         if (getEmbeddedHttp2Exception(cause) != null) {
642             // Some exception in the causality chain is an Http2Exception - handle it.
643             onError(ctx, false, cause);
644         } else {
645             super.exceptionCaught(ctx, cause);
646         }
647     }
648 
649     /**
650      * Closes the local side of the given stream. If this causes the stream to be closed, adds a
651      * hook to close the channel after the given future completes.
652      *
653      * @param stream the stream to be half closed.
654      * @param future If closing, the future after which to close the channel.
655      */
656     @Override
657     public void closeStreamLocal(Http2Stream stream, ChannelFuture future) {
658         switch (stream.state()) {
659             case HALF_CLOSED_LOCAL:
660             case OPEN:
661                 stream.closeLocalSide();
662                 break;
663             default:
664                 closeStream(stream, future);
665                 break;
666         }
667     }
668 
669     /**
670      * Closes the remote side of the given stream. If this causes the stream to be closed, adds a
671      * hook to close the channel after the given future completes.
672      *
673      * @param stream the stream to be half closed.
674      * @param future If closing, the future after which to close the channel.
675      */
676     @Override
677     public void closeStreamRemote(Http2Stream stream, ChannelFuture future) {
678         switch (stream.state()) {
679             case HALF_CLOSED_REMOTE:
680             case OPEN:
681                 stream.closeRemoteSide();
682                 break;
683             default:
684                 closeStream(stream, future);
685                 break;
686         }
687     }
688 
689     @Override
690     public void closeStream(final Http2Stream stream, ChannelFuture future) {
691         if (future.isDone()) {
692             doCloseStream(stream, future);
693         } else {
694             future.addListener((ChannelFutureListener) future1 -> doCloseStream(stream, future1));
695         }
696     }
697 
698     /**
699      * Central handler for all exceptions caught during HTTP/2 processing.
700      */
701     @Override
702     public void onError(ChannelHandlerContext ctx, boolean outbound, Throwable cause) {
703         Http2Exception embedded = getEmbeddedHttp2Exception(cause);
704         if (isStreamError(embedded)) {
705             onStreamError(ctx, outbound, cause, (StreamException) embedded);
706         } else if (embedded instanceof CompositeStreamException) {
707             CompositeStreamException compositException = (CompositeStreamException) embedded;
708             // Each contained StreamException is generally an independent error for a distinct stream (e.g.
709             // one per active stream that overflowed its flow-control window when SETTINGS_INITIAL_WINDOW_SIZE
710             // changed), so every affected stream must still be reset individually; otherwise it would be left
711             // open with a corrupted flow-control window. Should the composite ever contain more than one
712             // exception for the same stream id, only report the first one, per RFC 9113, Section 5.4's
713             // guidance to report at most one stream error per stream.
714             Set<Integer> handledStreamIds = new HashSet<Integer>();
715             for (StreamException streamException : compositException) {
716                 if (handledStreamIds.add(streamException.streamId())) {
717                     onStreamError(ctx, outbound, cause, streamException);
718                 }
719             }
720         } else {
721             onConnectionError(ctx, outbound, cause, embedded);
722         }
723         ctx.flush();
724     }
725 
726     /**
727      * Called by the graceful shutdown logic to determine when it is safe to close the connection. Returns {@code true}
728      * if the graceful shutdown has completed and the connection can be safely closed. This implementation just
729      * guarantees that there are no active streams. Subclasses may override to provide additional checks.
730      */
731     protected boolean isGracefulShutdownComplete() {
732         return connection().numActiveStreams() == 0;
733     }
734 
735     /**
736      * Handler for a connection error. Sends a GO_AWAY frame to the remote endpoint. Once all
737      * streams are closed, the connection is shut down.
738      *
739      * @param ctx the channel context
740      * @param outbound {@code true} if the error was caused by an outbound operation.
741      * @param cause the exception that was caught
742      * @param http2Ex the {@link Http2Exception} that is embedded in the causality chain. This may
743      *            be {@code null} if it's an unknown exception.
744      */
745     protected void onConnectionError(ChannelHandlerContext ctx, boolean outbound,
746                                      Throwable cause, Http2Exception http2Ex) {
747         if (http2Ex == null) {
748             http2Ex = new Http2Exception(INTERNAL_ERROR, cause.getMessage(), cause);
749         }
750 
751         ChannelPromise promise = ctx.newPromise();
752         ChannelFuture future = goAway(ctx, http2Ex, ctx.newPromise());
753         if (http2Ex.shutdownHint() == Http2Exception.ShutdownHint.GRACEFUL_SHUTDOWN) {
754             doGracefulShutdown(ctx, future, promise);
755         } else {
756             future.addListener(newClosingChannelFutureListener(ctx, promise));
757         }
758     }
759 
760     /**
761      * Handler for a stream error. Sends a {@code RST_STREAM} frame to the remote endpoint and closes the
762      * stream.
763      *
764      * @param ctx the channel context
765      * @param outbound {@code true} if the error was caused by an outbound operation.
766      * @param cause the exception that was caught
767      * @param http2Ex the {@link StreamException} that is embedded in the causality chain.
768      */
769     protected void onStreamError(ChannelHandlerContext ctx, boolean outbound,
770                                  @SuppressWarnings("unused") Throwable cause, StreamException http2Ex) {
771         final int streamId = http2Ex.streamId();
772         Http2Stream stream = connection().stream(streamId);
773 
774         //if this is caused by reading headers that are too large, send a header with status 431
775         if (http2Ex instanceof Http2Exception.HeaderListSizeException &&
776             ((Http2Exception.HeaderListSizeException) http2Ex).duringDecode() &&
777             connection().isServer()) {
778 
779             // NOTE We have to check to make sure that a stream exists before we send our reply.
780             // We likely always create the stream below as the stream isn't created until the
781             // header block is completely processed.
782 
783             // The case of a streamId referring to a stream which was already closed is handled
784             // by createStream and will land us in the catch block below
785             if (stream == null) {
786                 try {
787                     stream = encoder.connection().remote().createStream(streamId, true);
788                 } catch (Http2Exception e) {
789                     encoder().writeRstStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise());
790                     return;
791                 }
792             }
793 
794             // ensure that we have not already sent headers on this stream
795             if (stream != null && !stream.isHeadersSent()) {
796                 try {
797                     handleServerHeaderDecodeSizeError(ctx, stream);
798                 } catch (Throwable cause2) {
799                     onError(ctx, outbound, connectionError(INTERNAL_ERROR, cause2, "Error DecodeSizeError"));
800                 }
801             }
802         }
803 
804         if (stream == null) {
805             if (!outbound || connection().local().mayHaveCreatedStream(streamId)) {
806                 encoder().writeRstStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise());
807             }
808         } else {
809             encoder().writeRstStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise());
810         }
811     }
812 
813     /**
814      * Notifies client that this server has received headers that are larger than what it is
815      * willing to accept. Override to change behavior.
816      *
817      * @param ctx the channel context
818      * @param stream the Http2Stream on which the header was received
819      */
820     protected void handleServerHeaderDecodeSizeError(ChannelHandlerContext ctx, Http2Stream stream) {
821         encoder().writeHeaders(ctx, stream.id(), HEADERS_TOO_LARGE_HEADERS, 0, true, ctx.newPromise());
822     }
823 
824     protected Http2FrameWriter frameWriter() {
825         return encoder().frameWriter();
826     }
827 
828     /**
829      * Sends a {@code RST_STREAM} frame even if we don't know about the stream. This error condition is most likely
830      * triggered by the first frame of a stream being invalid. That is, there was an error reading the frame before
831      * we could create a new stream.
832      */
833     private ChannelFuture resetUnknownStream(final ChannelHandlerContext ctx, int streamId, long errorCode,
834                                              ChannelPromise promise) {
835         ChannelFuture future = frameWriter().writeRstStream(ctx, streamId, errorCode, promise);
836         if (future.isDone()) {
837             closeConnectionOnError(ctx, future);
838         } else {
839             future.addListener((ChannelFutureListener) f -> closeConnectionOnError(ctx, f));
840         }
841         return future;
842     }
843 
844     @Override
845     public ChannelFuture resetStream(final ChannelHandlerContext ctx, int streamId, long errorCode,
846                                      ChannelPromise promise) {
847         final Http2Stream stream = connection().stream(streamId);
848         if (stream == null) {
849             return resetUnknownStream(ctx, streamId, errorCode, promise.unvoid());
850         }
851 
852        return resetStream(ctx, stream, errorCode, promise);
853     }
854 
855     private ChannelFuture resetStream(final ChannelHandlerContext ctx, final Http2Stream stream,
856                                       long errorCode, ChannelPromise promise) {
857         promise = promise.unvoid();
858         if (stream.isResetSent()) {
859             // Don't write a RST_STREAM frame if we have already written one.
860             return promise.setSuccess();
861         }
862         // Synchronously set the resetSent flag to prevent any subsequent calls
863         // from resulting in multiple reset frames being sent.
864         //
865         // This needs to be done before we notify the promise as the promise may have a listener attached that
866         // call resetStream(...) again.
867         stream.resetSent();
868 
869         final ChannelFuture future;
870         // If the remote peer is not aware of the steam, then we are not allowed to send a RST_STREAM
871         // https://tools.ietf.org/html/rfc7540#section-6.4.
872         if (stream.state() == IDLE ||
873             connection().local().created(stream) && !stream.isHeadersSent() && !stream.isPushPromiseSent()) {
874             future = promise.setSuccess();
875         } else {
876             future = frameWriter().writeRstStream(ctx, stream.id(), errorCode, promise);
877         }
878         if (future.isDone()) {
879             processRstStreamWriteResult(ctx, stream, future);
880         } else {
881             future.addListener((ChannelFutureListener) f -> processRstStreamWriteResult(ctx, stream, f));
882         }
883 
884         return future;
885     }
886 
887     @Override
888     public ChannelFuture goAway(final ChannelHandlerContext ctx, final int lastStreamId, final long errorCode,
889                                 final ByteBuf debugData, ChannelPromise promise) {
890         promise = promise.unvoid();
891         final Http2Connection connection = connection();
892         try {
893             if (!connection.goAwaySent(lastStreamId, errorCode, debugData)) {
894                 debugData.release();
895                 promise.trySuccess();
896                 return promise;
897             }
898         } catch (Throwable cause) {
899             debugData.release();
900             promise.tryFailure(cause);
901             return promise;
902         }
903 
904         // Need to retain before we write the buffer because if we do it after the refCnt could already be 0 and
905         // result in an IllegalRefCountException.
906         debugData.retain();
907         ChannelFuture future = frameWriter().writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
908 
909         if (future.isDone()) {
910             processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future);
911         } else {
912             future.addListener((ChannelFutureListener) f ->
913                     processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, f));
914         }
915 
916         return future;
917     }
918 
919     /**
920      * Closes the connection if the graceful shutdown process has completed.
921      * @param future Represents the status that will be passed to the {@link #closeListener}.
922      */
923     private void checkCloseConnection(ChannelFuture future) {
924         // If this connection is closing and the graceful shutdown has completed, close the connection
925         // once this operation completes.
926         if (closeListener != null && isGracefulShutdownComplete()) {
927             ChannelFutureListener closeListener = this.closeListener;
928             // This method could be called multiple times
929             // and we don't want to notify the closeListener multiple times.
930             this.closeListener = null;
931             try {
932                 closeListener.operationComplete(future);
933             } catch (Exception e) {
934                 throw new IllegalStateException("Close listener threw an unexpected exception", e);
935             }
936         }
937     }
938 
939     /**
940      * Close the remote endpoint with a {@code GO_AWAY} frame. Does <strong>not</strong> flush
941      * immediately, this is the responsibility of the caller.
942      */
943     private ChannelFuture goAway(ChannelHandlerContext ctx, Http2Exception cause, ChannelPromise promise) {
944         long errorCode = cause != null ? cause.error().code() : NO_ERROR.code();
945         int lastKnownStream;
946         if (cause != null && cause.shutdownHint() == Http2Exception.ShutdownHint.HARD_SHUTDOWN) {
947             // The hard shutdown could have been triggered during header processing, before updating
948             // lastStreamCreated(). Specifically, any connection errors encountered by Http2FrameReader or HPACK
949             // decoding will fail to update the last known stream. So we must be pessimistic.
950             // https://github.com/netty/netty/issues/10670
951             lastKnownStream = Integer.MAX_VALUE;
952         } else {
953             lastKnownStream = connection().remote().lastStreamCreated();
954         }
955         return goAway(ctx, lastKnownStream, errorCode, Http2CodecUtil.toByteBuf(ctx, cause), promise);
956     }
957 
958     private void processRstStreamWriteResult(ChannelHandlerContext ctx, Http2Stream stream, ChannelFuture future) {
959         if (future.isSuccess()) {
960             closeStream(stream, future);
961         } else {
962             // The connection will be closed and so no need to change the resetSent flag to false.
963             onConnectionError(ctx, true, future.cause(), null);
964         }
965     }
966 
967     private void closeConnectionOnError(ChannelHandlerContext ctx, ChannelFuture future) {
968         if (!future.isSuccess()) {
969             onConnectionError(ctx, true, future.cause(), null);
970         }
971     }
972 
973     private void doCloseStream(final Http2Stream stream, ChannelFuture future) {
974         stream.close();
975         checkCloseConnection(future);
976     }
977 
978     /**
979      * Returns the client preface string if this is a client connection, otherwise returns {@code null}.
980      */
981     private static ByteBuf clientPrefaceString(Http2Connection connection) {
982         return connection.isServer() ? connectionPrefaceBuf() : null;
983     }
984 
985     private static void processGoAwayWriteResult(final ChannelHandlerContext ctx, final int lastStreamId,
986                                                  final long errorCode, final ByteBuf debugData, ChannelFuture future) {
987         try {
988             if (future.isSuccess()) {
989                 if (errorCode != NO_ERROR.code()) {
990                     if (logger.isDebugEnabled()) {
991                         logger.debug("{} Sent GOAWAY: lastStreamId '{}', errorCode '{}', " +
992                                      "debugData '{}'. Forcing shutdown of the connection.",
993                                      ctx.channel(), lastStreamId, errorCode, debugData.toString(UTF_8));
994                     }
995                     ctx.close();
996                 }
997             } else {
998                 if (logger.isDebugEnabled()) {
999                     logger.debug("{} Sending GOAWAY failed: lastStreamId '{}', errorCode '{}', " +
1000                                  "debugData '{}'. Forcing shutdown of the connection.",
1001                                  ctx.channel(), lastStreamId, errorCode, debugData.toString(UTF_8), future.cause());
1002                 }
1003                 ctx.close();
1004             }
1005         } finally {
1006             // We're done with the debug data now.
1007             debugData.release();
1008         }
1009     }
1010 
1011     /**
1012      * Closes the channel when the future completes.
1013      */
1014     private static final class ClosingChannelFutureListener implements ChannelFutureListener {
1015         private final ChannelHandlerContext ctx;
1016         private final ChannelPromise promise;
1017         private final Future<?> timeoutTask;
1018         private boolean closed;
1019 
1020         ClosingChannelFutureListener(ChannelHandlerContext ctx, ChannelPromise promise) {
1021             this.ctx = ctx;
1022             this.promise = promise;
1023             timeoutTask = null;
1024         }
1025 
1026         ClosingChannelFutureListener(final ChannelHandlerContext ctx, final ChannelPromise promise,
1027                                      long timeout, TimeUnit unit) {
1028             this.ctx = ctx;
1029             this.promise = promise;
1030             timeoutTask = ctx.executor().schedule(new Runnable() {
1031                 @Override
1032                 public void run() {
1033                     doClose();
1034                 }
1035             }, timeout, unit);
1036         }
1037 
1038         @Override
1039         public void operationComplete(ChannelFuture sentGoAwayFuture) {
1040             if (timeoutTask != null) {
1041                 timeoutTask.cancel(false);
1042             }
1043             doClose();
1044         }
1045 
1046         private void doClose() {
1047             // We need to guard against multiple calls as the timeout may trigger close() first and then it will be
1048             // triggered again because of operationComplete(...) is called.
1049             if (closed) {
1050                 // This only happens if we also scheduled a timeout task.
1051                 assert timeoutTask != null;
1052                 return;
1053             }
1054             closed = true;
1055             if (promise == null) {
1056                 ctx.close();
1057             } else {
1058                 ctx.close(promise);
1059             }
1060         }
1061     }
1062 
1063     private final class PrefaceSendListener implements ChannelFutureListener {
1064         private final ChannelHandlerContext ctx;
1065         private final ChannelPromise promise;
1066 
1067         PrefaceSendListener(ChannelHandlerContext ctx, ChannelPromise promise) {
1068             this.ctx = ctx;
1069             this.promise = promise;
1070         }
1071 
1072         @Override
1073         public void operationComplete(ChannelFuture f) {
1074             if (f.isSuccess()) {
1075                 try {
1076                     if (byteDecoder != null) {
1077                         byteDecoder.sendPrefaceIfNeeded(ctx);
1078                     }
1079                 } catch (Throwable e) {
1080                     promise.setFailure(e);
1081                     return;
1082                 }
1083                 promise.setSuccess();
1084             } else {
1085                 promise.setFailure(f.cause());
1086             }
1087         }
1088     }
1089 }