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