if (evt == Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE) { tryExpandConnectionFlowControlWindow(connection()); } else if (evt instanceof UpgradeEvent) { UpgradeEvent upgrade = (UpgradeEvent) evt; try { onUpgradeEvent(ctx, upgrade.retain()); Http2Stream stream = connection().stream(HTTP_UPGRADE_STREAM_ID); if (stream.getProperty(streamKey) == null) { onStreamActive0(stream); stream.setProperty(upgradeKey, true); InboundHttpToHttp2Adapter.handle( ctx, connection(), decoder().frameListener(), upgrade.upgradeRequest().retain()); } finally { upgrade.release();
if (msg instanceof Http2DataFrame) { Http2DataFrame dataFrame = (Http2DataFrame) msg; encoder().writeData(ctx, dataFrame.stream().id(), dataFrame.content(), dataFrame.padding(), dataFrame.isEndStream(), promise); } else if (msg instanceof Http2HeadersFrame) { writeHeadersFrame(ctx, (Http2HeadersFrame) msg, promise); } else if (msg instanceof Http2PushPromiseFrame) { writePushPromiseFrame(ctx, (Http2PushPromiseFrame) msg, promise); } else if (msg instanceof Http2WindowUpdateFrame) { Http2WindowUpdateFrame frame = (Http2WindowUpdateFrame) msg; increaseInitialConnectionWindow(frame.windowSizeIncrement()); } else { consumeBytes(frameStream.id(), frame.windowSizeIncrement()); encoder().writeRstStream(ctx, rstFrame.stream().id(), rstFrame.errorCode(), promise); } else if (msg instanceof Http2PingFrame) { Http2PingFrame frame = (Http2PingFrame) msg; encoder().writePing(ctx, frame.ack(), frame.content(), promise); } else if (msg instanceof Http2SettingsFrame) { encoder().writeSettings(ctx, ((Http2SettingsFrame) msg).settings(), promise); } else if (msg instanceof Http2GoAwayFrame) { writeGoAwayFrame(ctx, (Http2GoAwayFrame) msg, promise); } else if (msg instanceof Http2UnknownFrame) { Http2UnknownFrame unknownFrame = (Http2UnknownFrame) msg; encoder().writeFrame(ctx, unknownFrame.frameType(), unknownFrame.stream().id(), unknownFrame.flags(), unknownFrame.content(), promise); } else if (!(msg instanceof Http2Frame)) {
private void tryExpandConnectionFlowControlWindow(Http2Connection connection) throws Http2Exception { if (initialFlowControlWindowSize != null) { // The window size in the settings explicitly excludes the connection window. So we manually manipulate the // connection window to accommodate more concurrent data per connection. Http2Stream connectionStream = connection.connectionStream(); Http2LocalFlowController localFlowController = connection.local().flowController(); final int delta = initialFlowControlWindowSize - localFlowController.initialWindowSize(connectionStream); // Only increase the connection window, don't decrease it. if (delta > 0) { // Double the delta just so a single stream can't exhaust the connection window. localFlowController.incrementWindowSize(connectionStream, Math.max(delta << 1, delta)); flush(ctx); } } }
@Override public final void handlerAdded(ChannelHandlerContext ctx) throws Exception { this.ctx = ctx; super.handlerAdded(ctx); handlerAdded0(ctx); // Must be after Http2ConnectionHandler does its initialization in handlerAdded above. // The server will not send a connection preface so we are good to send a window update. Http2Connection connection = connection(); if (connection.isServer()) { tryExpandConnectionFlowControlWindow(connection); } }
private void onStreamActive0(Http2Stream stream) { if (connection().local().isValidStreamId(stream.id())) { return; } DefaultHttp2FrameStream stream2 = newStream().setStreamAndProperty(streamKey, stream); onHttp2StreamStateChanged(ctx, stream2); }
@Override public void writabilityChanged(Http2Stream stream) { Http2FrameStream frameStream = stream.getProperty(streamKey); if (frameStream == null) { return; } onHttp2StreamWritabilityChanged( ctx, frameStream, connection().remote().flowController().isWritable(stream)); } }
/** * Exceptions for unknown streams, that is streams that have no {@link Http2FrameStream} object attached * are simply logged and replied to by sending a RST_STREAM frame. */ @Override protected final void onStreamError(ChannelHandlerContext ctx, boolean outbound, Throwable cause, Http2Exception.StreamException streamException) { int streamId = streamException.streamId(); Http2Stream connectionStream = connection().stream(streamId); if (connectionStream == null) { onHttp2UnknownStreamError(ctx, cause, streamException); // Write a RST_STREAM super.onStreamError(ctx, outbound, cause, streamException); return; } Http2FrameStream stream = connectionStream.getProperty(streamKey); if (stream == null) { LOG.warn("Stream exception thrown without stream object attached.", cause); // Write a RST_STREAM super.onStreamError(ctx, outbound, cause, streamException); return; } if (!outbound) { // We only forward non outbound errors as outbound errors will already be reflected by failing the promise. onHttp2FrameStreamException(ctx, new Http2FrameStreamException(stream, streamException.error(), cause)); } }
encoder().writeHeaders(ctx, headersFrame.stream().id(), headersFrame.headers(), headersFrame.padding(), headersFrame.isEndStream(), promise); } else { final DefaultHttp2FrameStream stream = (DefaultHttp2FrameStream) headersFrame.stream(); final Http2Connection connection = connection(); final int streamId = connection.local().incrementAndGetNextStreamId(); if (streamId < 0) { encoder().writeHeaders(ctx, streamId, headersFrame.headers(), headersFrame.padding(), headersFrame.isEndStream(), writePromise); if (writePromise.isDone()) { notifyHeaderWritePromise(writePromise, promise); } else { numBufferedStreams++;
private Http2FrameStream requireStream(int streamId) { Http2FrameStream stream = connection().stream(streamId).getProperty(streamKey); if (stream == null) { throw new IllegalStateException("Stream object required for identifier: " + streamId); } return stream; } }
private void writeGoAwayFrame(ChannelHandlerContext ctx, Http2GoAwayFrame frame, ChannelPromise promise) { if (frame.lastStreamId() > -1) { frame.release(); throw new IllegalArgumentException("Last stream id must not be set on GOAWAY frame"); } int lastStreamCreated = connection().remote().lastStreamCreated(); long lastStreamId = lastStreamCreated + ((long) frame.extraStreamIds()) * 2; // Check if the computation overflowed. if (lastStreamId > Integer.MAX_VALUE) { lastStreamId = Integer.MAX_VALUE; } goAway(ctx, (int) lastStreamId, frame.errorCode(), frame.content(), promise); }
private void writePushPromiseFrame(ChannelHandlerContext ctx, Http2PushPromiseFrame pushPromiseFrame, ChannelPromise promise) { if (isStreamIdValid(pushPromiseFrame.stream().id())) { encoder().writePushPromise(ctx, pushPromiseFrame.stream().id(), pushPromiseFrame.promisedStreamId(), pushPromiseFrame.headers(), pushPromiseFrame.padding(), promise); } else { // TODO promise.setFailure(new IllegalArgumentException("Push promise stream id invalid!")); } }
/** * Creates a new {@link Http2FrameStream} object. * * <p>This method is <em>thread-safe</em>. */ public final Http2FrameStream newStream() { Http2FrameCodec codec = frameCodec; if (codec == null) { throw new IllegalStateException(StringUtil.simpleClassName(Http2FrameCodec.class) + " not found." + " Has the handler been added to a pipeline?"); } return codec.newStream(); }
private void onStreamActive0(Http2Stream stream) { if (connection().local().isValidStreamId(stream.id())) { return; } DefaultHttp2FrameStream stream2 = newStream().setStreamAndProperty(streamKey, stream); onHttp2StreamStateChanged(ctx, stream2); }
@Override public final void handlerAdded(ChannelHandlerContext ctx) throws Exception { this.ctx = ctx; super.handlerAdded(ctx); handlerAdded0(ctx); // Must be after Http2ConnectionHandler does its initialization in handlerAdded above. // The server will not send a connection preface so we are good to send a window update. Http2Connection connection = connection(); if (connection.isServer()) { tryExpandConnectionFlowControlWindow(connection); } }
/** * Exceptions for unknown streams, that is streams that have no {@link Http2FrameStream} object attached * are simply logged and replied to by sending a RST_STREAM frame. */ @Override protected final void onStreamError(ChannelHandlerContext ctx, boolean outbound, Throwable cause, Http2Exception.StreamException streamException) { int streamId = streamException.streamId(); Http2Stream connectionStream = connection().stream(streamId); if (connectionStream == null) { onHttp2UnknownStreamError(ctx, cause, streamException); // Write a RST_STREAM super.onStreamError(ctx, outbound, cause, streamException); return; } Http2FrameStream stream = connectionStream.getProperty(streamKey); if (stream == null) { LOG.warn("Stream exception thrown without stream object attached.", cause); // Write a RST_STREAM super.onStreamError(ctx, outbound, cause, streamException); return; } if (!outbound) { // We only forward non outbound errors as outbound errors will already be reflected by failing the promise. onHttp2FrameStreamException(ctx, new Http2FrameStreamException(stream, streamException.error(), cause)); } }
encoder().writeHeaders(ctx, headersFrame.stream().id(), headersFrame.headers(), headersFrame.padding(), headersFrame.isEndStream(), promise); } else { final DefaultHttp2FrameStream stream = (DefaultHttp2FrameStream) headersFrame.stream(); final Http2Connection connection = connection(); final int streamId = connection.local().incrementAndGetNextStreamId(); if (streamId < 0) { encoder().writeHeaders(ctx, streamId, headersFrame.headers(), headersFrame.padding(), headersFrame.isEndStream(), writePromise); if (writePromise.isDone()) { notifyHeaderWritePromise(writePromise, promise); } else { numBufferedStreams++;
private void increaseInitialConnectionWindow(int deltaBytes) throws Http2Exception { // The LocalFlowController is responsible for detecting over/under flow. connection().local().flowController().incrementWindowSize(connection().connectionStream(), deltaBytes); }
@Override public void writabilityChanged(Http2Stream stream) { Http2FrameStream frameStream = stream.getProperty(streamKey); if (frameStream == null) { return; } onHttp2StreamWritabilityChanged( ctx, frameStream, connection().remote().flowController().isWritable(stream)); } }
private void writeGoAwayFrame(ChannelHandlerContext ctx, Http2GoAwayFrame frame, ChannelPromise promise) { if (frame.lastStreamId() > -1) { frame.release(); throw new IllegalArgumentException("Last stream id must not be set on GOAWAY frame"); } int lastStreamCreated = connection().remote().lastStreamCreated(); long lastStreamId = lastStreamCreated + ((long) frame.extraStreamIds()) * 2; // Check if the computation overflowed. if (lastStreamId > Integer.MAX_VALUE) { lastStreamId = Integer.MAX_VALUE; } goAway(ctx, (int) lastStreamId, frame.errorCode(), frame.content(), promise); }
private void writePushPromiseFrame(ChannelHandlerContext ctx, Http2PushPromiseFrame pushPromiseFrame, ChannelPromise promise) { if (isStreamIdValid(pushPromiseFrame.stream().id())) { encoder().writePushPromise(ctx, pushPromiseFrame.stream().id(), pushPromiseFrame.promisedStreamId(), pushPromiseFrame.headers(), pushPromiseFrame.padding(), promise); } else { // TODO promise.setFailure(new IllegalArgumentException("Push promise stream id invalid!")); } }