@Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { try { super.userEventTriggered(ctx, evt); } finally { if (evt instanceof IdleStateEvent && ((IdleStateEvent) evt).state() == IdleState.ALL_IDLE) { connection.handleIdle(); } } }
@Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { super.handlerAdded(ctx); chctx = ctx; }
/** * Central handler for all exceptions caught during HTTP/2 processing. */ @Override public void onError(ChannelHandlerContext ctx, boolean outbound, Throwable cause) { Http2Exception embedded = getEmbeddedHttp2Exception(cause); if (isStreamError(embedded)) { onStreamError(ctx, outbound, cause, (StreamException) embedded); } else if (embedded instanceof CompositeStreamException) { CompositeStreamException compositException = (CompositeStreamException) embedded; for (StreamException streamException : compositException) { onStreamError(ctx, outbound, cause, streamException); } } else { onConnectionError(ctx, outbound, cause, embedded); } ctx.flush(); }
private void processRstStreamWriteResult(ChannelHandlerContext ctx, Http2Stream stream, ChannelFuture future) { if (future.isSuccess()) { closeStream(stream, future); } else { // The connection will be closed and so no need to change the resetSent flag to false. onConnectionError(ctx, true, future.cause(), null); } }
@Override public ChannelFuture resetStream(final ChannelHandlerContext ctx, int streamId, long errorCode, ChannelPromise promise) { final Http2Stream stream = connection().stream(streamId); if (stream == null) { return resetUnknownStream(ctx, streamId, errorCode, promise.unvoid()); } return resetStream(ctx, stream, errorCode, promise); }
@Override protected void onConnectionError(final ChannelHandlerContext context, final boolean isOutbound, final Throwable cause, final Http2Exception http2Exception) { this.connectionErrorCause = http2Exception != null ? http2Exception : cause; super.onConnectionError(context, isOutbound, cause, http2Exception); }
@Override protected void onStreamError(ChannelHandlerContext ctx, boolean outbound, Throwable cause, Http2Exception.StreamException http2Ex) { connection.getContext().executeFromIO(v -> { connection.onStreamError(http2Ex.streamId(), http2Ex); }); // Default behavior reset stream super.onStreamError(ctx, outbound, cause, http2Ex); }
/** * Sends the HTTP/2 connection preface upon establishment of the connection, if not already sent. */ private void sendPreface(ChannelHandlerContext ctx) throws Exception { if (prefaceSent || !ctx.channel().isActive()) { return; } prefaceSent = true; final boolean isClient = !connection().isServer(); if (isClient) { // Clients must send the preface string as the first bytes on the connection. ctx.write(connectionPrefaceBuf()).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); } // Both client and server must send their initial settings. encoder.writeSettings(ctx, initialSettings, ctx.newPromise()).addListener( ChannelFutureListener.CLOSE_ON_FAILURE); if (isClient) { // If this handler is extended by the user and we directly fire the userEvent from this context then // the user will not see the event. We should fire the event starting with this handler so this class // (and extending classes) have a chance to process the event. userEventTriggered(ctx, Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE); } } }
public void channelInactive(ChannelHandlerContext ctx) throws Exception { // Connection has terminated, close the encoder and decoder. encoder().close(); decoder().close(); // We need to remove all streams (not just the active ones). // See https://github.com/netty/netty/issues/4838. connection().close(ctx.voidPromise()); }
@Override protected final boolean isGracefulShutdownComplete() { return super.isGracefulShutdownComplete() && numBufferedStreams == 0; }
@SuppressWarnings("unused") Throwable cause, StreamException http2Ex) { final int streamId = http2Ex.streamId(); Http2Stream stream = connection().stream(streamId); connection().isServer()) { stream = encoder.connection().remote().createStream(streamId, true); } catch (Http2Exception e) { resetUnknownStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise()); return; handleServerHeaderDecodeSizeError(ctx, stream); } catch (Throwable cause2) { onError(ctx, outbound, connectionError(INTERNAL_ERROR, cause2, "Error DecodeSizeError")); if (!outbound || connection().local().mayHaveCreatedStream(streamId)) { resetUnknownStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise()); resetStream(ctx, stream, http2Ex.error().code(), ctx.newPromise());
protected Http2FrameWriter frameWriter() { return encoder().frameWriter(); }
private Http2ConnectionHandler newHttp2ConnectionHandler(ChannelPipeline pipeline) { final Http2Connection conn = new DefaultHttp2Connection(true); conn.addListener(new Http2GoAwayListener(pipeline.channel())); try (Http2FrameReader reader = new DefaultHttp2FrameReader(true)) { Http2FrameWriter writer = new DefaultHttp2FrameWriter(); Http2ConnectionEncoder encoder = new DefaultHttp2ConnectionEncoder(conn, writer); Http2ConnectionDecoder decoder = new DefaultHttp2ConnectionDecoder(conn, encoder, reader); final Http2ConnectionHandler handler = new Http2ServerConnectionHandler(decoder, encoder, new Http2Settings()); // Setup post build options final Http2RequestDecoder listener = new Http2RequestDecoder(config, pipeline.channel(), handler.encoder()); handler.connection().addListener(listener); handler.decoder().frameListener(listener); handler.gracefulShutdownTimeoutMillis(config.idleTimeoutMillis()); return handler; } }
@Override public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { try { decoder.decodeFrame(ctx, in, out); } catch (Throwable e) { onError(ctx, false, e); } } }
@Override public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { promise = promise.unvoid(); // Avoid NotYetConnectedException if (!ctx.channel().isActive()) { ctx.close(promise); return; } // If the user has already sent a GO_AWAY frame they may be attempting to do a graceful shutdown which requires // sending multiple GO_AWAY frames. We should only send a GO_AWAY here if one has not already been sent. If // a GO_AWAY has been sent we send a empty buffer just so we can wait to close until all other data has been // flushed to the OS. // https://github.com/netty/netty/issues/5307 final ChannelFuture future = connection().goAwaySent() ? ctx.write(EMPTY_BUFFER) : goAway(ctx, null); ctx.flush(); doGracefulShutdown(ctx, future, promise); }
private T buildFromCodec(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) { final T handler; try { // Call the abstract build method handler = build(decoder, encoder, initialSettings); } catch (Throwable t) { encoder.close(); decoder.close(); throw new IllegalStateException("failed to build a Http2ConnectionHandler", t); } // Setup post build options handler.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis); if (handler.decoder().frameListener() == null) { handler.decoder().frameListener(frameListener); } return handler; }
final ByteBuf debugData, ChannelPromise promise) { promise = promise.unvoid(); final Http2Connection connection = connection(); try { if (!connection.goAwaySent(lastStreamId, errorCode, debugData)) { ChannelFuture future = frameWriter().writeGoAway(ctx, lastStreamId, errorCode, debugData, promise); processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future); } else { future.addListener(new ChannelFutureListener() {
connection().local().created(stream) && !stream.isHeadersSent() && !stream.isPushPromiseSent()) { future = promise.setSuccess(); } else { future = frameWriter().writeRstStream(ctx, stream.id(), errorCode, promise); processRstStreamWriteResult(ctx, stream, future); } else { future.addListener(new ChannelFutureListener() {
/** * Handles the client-side (cleartext) upgrade from HTTP to HTTP/2. * Reserves local stream 1 for the HTTP/2 response. */ public void onHttpClientUpgrade() throws Http2Exception { if (connection().isServer()) { throw connectionError(PROTOCOL_ERROR, "Client-side HTTP upgrade requested for a server"); } if (!prefaceSent()) { // If the preface was not sent yet it most likely means the handler was not added to the pipeline before // calling this method. throw connectionError(INTERNAL_ERROR, "HTTP upgrade must occur after preface was sent"); } if (decoder.prefaceReceived()) { throw connectionError(PROTOCOL_ERROR, "HTTP upgrade must occur before HTTP/2 preface is received"); } // Create a local stream used for the HTTP cleartext upgrade. connection().local().createStream(HTTP_UPGRADE_STREAM_ID, true); }
/** * Close the remote endpoint with with a {@code GO_AWAY} frame. Does <strong>not</strong> flush * immediately, this is the responsibility of the caller. */ private ChannelFuture goAway(ChannelHandlerContext ctx, Http2Exception cause) { long errorCode = cause != null ? cause.error().code() : NO_ERROR.code(); int lastKnownStream = connection().remote().lastStreamCreated(); return goAway(ctx, lastKnownStream, errorCode, Http2CodecUtil.toByteBuf(ctx, cause), ctx.newPromise()); }