Repository: netty/netty
File: codec-http2/src/main/java/io/netty/handler/codec/http2/AbstractHttp2StreamChannel.java (Java)

Code before:
        @Override
        public void write(Object msg, final ChannelPromise promise) {
            // After this point its not possible to cancel a write anymore.
            if (!promise.setUncancellable()) {
                ReferenceCountUtil.release(msg);
                return;
            }

            if (!isActive() ||
                    // Once the outbound side was closed we should not allow header / data frames
                    outboundClosed && (msg instanceof Http2HeadersFrame || msg instanceof Http2DataFrame)) {
                ReferenceCountUtil.release(msg);
                promise.setFailure(new ClosedChannelException());
                return;
            }

            try {
                if (msg instanceof Http2StreamFrame) {
                    Http2StreamFrame frame = validateStreamFrame((Http2StreamFrame) msg).stream(stream());
                    if (msg instanceof Http2WindowUpdateFrame) {
                        Http2WindowUpdateFrame updateFrame = (Http2WindowUpdateFrame) msg;
                        if (!config.autoFlowControl) {
                            ReferenceCountUtil.release(msg);
                            promise.setFailure(new UnsupportedOperationException(
                                    Http2StreamChannelOption.AUTO_WRITE_WINDOW_UPDATE_FRAME + " is set to false"));
                        }
                        try {
                            ObjectUtil.checkInRange(updateFrame.windowSizeIncrement(), 0,
                                    flowControlledBytes, "windowSizeIncrement");
                        } catch (RuntimeException e) {
                            ReferenceCountUtil.release(updateFrame);
                            promise.setFailure(e);
                            return;
                        }
                        flowControlledBytes -= updateFrame.windowSizeIncrement();
                        if (parentContext().isRemoved()) {
                            ReferenceCountUtil.release(msg);
                            promise.setFailure(new ClosedChannelException());
                            return;
                        }
                        writeWindowUpdateFrame(updateFrame);
                    } else {
                        writeHttp2StreamFrame(frame, promise);
                    }
                } else {
                    String msgStr = msg.toString();
                    ReferenceCountUtil.release(msg);
                    promise.setFailure(new IllegalArgumentException(
                            "Message must be an " + StringUtil.simpleClassName(Http2StreamFrame.class) +
                                    ": " + msgStr));
                }

Review comment (bug):
```suggestion
                        if (config.autoFlowControl) {
                            ReferenceCountUtil.release(msg);
                            promise.setFailure(new UnsupportedOperationException(
                                    Http2StreamChannelOption.AUTO_WRITE_WINDOW_UPDATE_FRAME + " is set to false"));
                            return;
                        }
```

Code after:
            // See https://github.com/netty/netty/issues/9663
            if (future.isDone()) {
                windowUpdateFrameWriteComplete(future, AbstractHttp2StreamChannel.this);
            } else {
                future.addListener(windowUpdateFrameWriteListener);
            }
            return future;
        }

        @Override
        public void write(Object msg, final ChannelPromise promise) {
            // After this point its not possible to cancel a write anymore.
            if (!promise.setUncancellable()) {
                ReferenceCountUtil.release(msg);
                return;
            }

            if (!isActive() ||
                    // Once the outbound side was closed we should not allow header / data frames
                    outboundClosed && (msg instanceof Http2HeadersFrame || msg instanceof Http2DataFrame)) {
                ReferenceCountUtil.release(msg);
                promise.setFailure(new ClosedChannelException());
                return;
            }

            try {
                if (msg instanceof Http2StreamFrame) {
                    Http2StreamFrame frame = validateStreamFrame((Http2StreamFrame) msg).stream(stream());
                    if (msg instanceof Http2WindowUpdateFrame) {
                        Http2WindowUpdateFrame updateFrame = (Http2WindowUpdateFrame) msg;
                        if (config.autoStreamFlowControl) {
                            ReferenceCountUtil.release(msg);
                            promise.setFailure(new UnsupportedOperationException(
                                    Http2StreamChannelOption.AUTO_STREAM_FLOW_CONTROL + " is set to false"));
                            return;
                        }
                        try {
                            ObjectUtil.checkInRange(updateFrame.windowSizeIncrement(), 0,
                                    flowControlledBytes, "windowSizeIncrement");
                        } catch (RuntimeException e) {
                            ReferenceCountUtil.release(updateFrame);
                            promise.setFailure(e);
                            return;
                        }
                        flowControlledBytes -= updateFrame.windowSizeIncrement();
                        if (parentContext().isRemoved()) {
                            ReferenceCountUtil.release(msg);
                            promise.setFailure(new ClosedChannelException());
                            return;
                        }
                        ChannelFuture f = writeWindowUpdateFrame(updateFrame);