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