Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DelegatingDecompressorFrameListener doesn't support deferral of processed bytes #5375

Closed
npordash opened this issue Jun 8, 2016 · 5 comments
Assignees
Labels
Milestone

Comments

@npordash
Copy link

npordash commented Jun 8, 2016

Http2FrameListener.onDataRead states that reporting of processed bytes to the local flow controller can be deferred by returning something less than data + padding and then later doing something equivalent to connection().local().flowController().consumeBytes(Http2Stream, int).

This works fine until you start using a DelegatingDecompressorFrameListener because once you call consumeBytes you end up with the following exception:

io.netty.handler.codec.http2.Http2Exception: Error while returning bytes to flow control window
    at io.netty.handler.codec.http2.DelegatingDecompressorFrameListener$ConsumedBytesConverter.consumeBytes(DelegatingDecompressorFrameListener.java:361)
...
Caused by: java.lang.IllegalArgumentException: processed bytes cannot be negative
    at io.netty.handler.codec.http2.DelegatingDecompressorFrameListener$Http2Decompressor.incrementProcessedBytes(DelegatingDecompressorFrameListener.java:409)
    at io.netty.handler.codec.http2.DelegatingDecompressorFrameListener$Http2Decompressor.consumeProcessedBytes(DelegatingDecompressorFrameListener.java:445)
    at io.netty.handler.codec.http2.DelegatingDecompressorFrameListener$ConsumedBytesConverter.consumeBytes(DelegatingDecompressorFrameListener.java:349)

As far as I can tell the reason for this is because Http2Decompressor is expecting incrementProcessedBytes to be called prior to consumeProcessedBytes and you can't consume more than it has been incremented, which makes sense, but DelegatingDecompressorFrameListener.onDataRead only calls incrementProcessedBytes after all calls to onDataRead returns and only increments the processed bytes based on what was accumulated from the onDataRead calls which effectively means you need to fully consume the buffer in onDataRead.

I tested this with both 4.1.0.Final and 4.1.1.Final.

@Scottmitch
Copy link
Member

Scottmitch commented Jun 8, 2016

Thanks for reporting @npordash! Are you in a situation where listener.onDataRead is called multiple times in DelegatingDecompressorFrameListener, and you are trying to call consumeBytes(..) for data which was not returned by previous calls to Http2FrameListener.onDataRead?

@Scottmitch Scottmitch self-assigned this Jun 8, 2016
@Scottmitch
Copy link
Member

I tested this with both 4.1.0.Final and 4.1.1.Final.

Do you have a unit test which you would be willing to contribute? I have a PR but it would be nice to verify against a unit test if you already have one.

@npordash
Copy link
Author

npordash commented Jun 9, 2016

Thanks for the quick response!

I'm not in a situation where onDataRead is being called multiple times, at least I have yet to observe that happening. The exception gets thrown on the first data frame received.

My use-case is that I'm exposing inbound data frames as an rx.Observable<ByteBuf> and I only want to report the processing of the bytes when a ByteBuf has been sent to a subscriber of the Observable. It's possible that the ByteBuf is sent to a subscriber inside the call to onDataRead if the processing is synchronous which means that the call to consumeBytes might happen before onDataRead returns as well.

Unfortunately, I do not have an existing unit test for this yet. I stumbled across this while I was prototyping which involved manual testing with nghttp to send data to a netty http2 server 😦

@Scottmitch Scottmitch added this to the 4.1.2.Final milestone Jun 9, 2016
@Scottmitch
Copy link
Member

which means that the call to consumeBytes might happen before onDataRead returns as well.

As long as you are not double releasing the bytes you should be OK. I think I found the issue and I'll ask you to verify with the PR when its ready.

@npordash
Copy link
Author

npordash commented Jun 9, 2016

Awesome, thanks again!

Scottmitch added a commit to Scottmitch/netty that referenced this issue Jun 20, 2016
Motivation:
If a single DATA frame ends up being decompressed into multiple frames by DelegatingDecompressorFrameListener the flow control accounting is delayed until all frames have been decompressed. However it is possible the user may want to return bytes to the flow controller which were not included in the onDataRead return value. In this case the amount of processed bytes has not been incremented and will lead to negative value for processed bytes.

Modifications:
- Http2Decompressor.incrementProcessedBytes should be called each time onDataRead is called to ensure all bytes are accounted for at the correct time

Result:
Fixes netty#5375
liuzhengyang pushed a commit to liuzhengyang/netty that referenced this issue Sep 10, 2017
Motivation:
If a single DATA frame ends up being decompressed into multiple frames by DelegatingDecompressorFrameListener the flow control accounting is delayed until all frames have been decompressed. However it is possible the user may want to return bytes to the flow controller which were not included in the onDataRead return value. In this case the amount of processed bytes has not been incremented and will lead to negative value for processed bytes.

Modifications:
- Http2Decompressor.incrementProcessedBytes should be called each time onDataRead is called to ensure all bytes are accounted for at the correct time

Result:
Fixes netty#5375
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants