[
https://issues.apache.org/jira/browse/HTTPCORE-799?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Matthias updated HTTPCORE-799:
------------------------------
Affects Version/s: 5.4.3
Description:
h2. Description
Several repeatable async entity producers do not fully reset their
per-production state when {{releaseResources()}} is called before the current
production has completed.
h3. Buffered output
{{FileEntityProducer}}, {{PathEntityProducer}},
{{AbstractCharAsyncEntityProducer}}, and {{AbstractBinAsyncEntityProducer}} can
retain bytes that the data channel did not accept. Although the underlying
source is reset for a replay, the internal staging buffer still contains the
unwritten tail, so those stale bytes are emitted before the complete entity.
{{StringAsyncEntityProducer}} is affected through
{{AbstractCharAsyncEntityProducer}}. Repeatable custom binary producers can be
affected through {{AbstractBinAsyncEntityProducer}}.
For example, with content {{abcdef}}:
# The producer reads {{abcdef}} and the data channel accepts only {{abc}}.
# {{releaseResources()}} is called, simulating an interrupted request.
# The same repeatable producer is used again.
# The second production emits {{defabcdef}} instead of {{abcdef}}.
h3. Digest state
{{DigestingEntityProducer.releaseResources()}} delegates to the wrapped
producer but does not reset its {{MessageDigest}}. If the first production
writes {{abc}} before interruption and the replay writes {{abcdef}}, the replay
body can be correct while {{getDigest()}} and the {{digest}} trailer contain
the MD5 of {{abcabcdef}} ({{89091e2d8ada6ecb23f3d2b71fb1764b}}) instead of the
MD5 of {{abcdef}} ({{e80b5017098950fc58aad83c8c14978e}}).
h2. Context
This was exposed while testing an HTTP/2 authentication retry after a server
returned a {{401}} response and then reset the stream with
{{RST_STREAM(NO_ERROR)}}. The request producer was released mid-write and
reused for the retry. See
[HTTPCLIENT-2433|https://issues.apache.org/jira/browse/HTTPCLIENT-2433] for the
related HttpClient issue.
The HttpCore problem is not specific to HTTP/2 or authentication; it can occur
whenever a repeatable producer is released after a partial write and then
reused.
h2. Expected behavior
Calling {{releaseResources()}} should discard pending output and reset all
per-production digest state. A subsequent production should emit exactly the
original entity, and its digest should cover only that production.
h2. Actual behavior
Buffered but unwritten bytes can be prepended to the next production, and bytes
accepted during an interrupted production can be included in the replay's
digest.
h2. Affected code
* {{org.apache.hc.core5.http.nio.entity.FileEntityProducer}}
* {{org.apache.hc.core5.http.nio.entity.PathEntityProducer}}
* {{org.apache.hc.core5.http.nio.entity.AbstractCharAsyncEntityProducer}}
(including {{StringAsyncEntityProducer}})
* {{org.apache.hc.core5.http.nio.entity.AbstractBinAsyncEntityProducer}} (for
repeatable subclasses)
* {{org.apache.hc.core5.http.nio.entity.DigestingEntityProducer}}
The problem is present in HttpCore 5.4.3, 5.5-beta2, and the current {{5.4.x}}
and {{master}} branches.
h2. Proposed fix
Clear the internal staging buffers when the file, path, character, and binary
producers are released. Reset {{DigestingEntityProducer}}'s {{MessageDigest}}
before releasing its wrapped producer, while preserving the completed
{{digest}} value returned by {{getDigest()}}.
Add regression tests that interrupt production after a partial channel write,
release the producer, and verify that the next production contains the original
entity exactly once and calculates its digest only from the replayed bytes.
Implementation:
[apache/httpcomponents-core#698|https://github.com/apache/httpcomponents-core/pull/698]
was:
h2. Description
{{FileEntityProducer}} and {{PathEntityProducer}} are repeatable, but they do
not fully reset their internal state when {{releaseResources()}} is called
before the current production has completed.
Both implementations close and clear the reference to the underlying file
channel and reset {{eof}}, but leave any unwritten bytes in their internal
{{ByteBuffer}}. On the next production, the file is reopened at position zero
while those stale bytes remain buffered. The stale suffix is therefore emitted
before the complete file.
For example, with file content {{abcdef}}:
# The producer reads {{abcdef}} and the data channel accepts only {{abc}}.
# {{releaseResources()}} is called, simulating an interrupted request.
# The same repeatable producer is used again.
# The second production emits {{defabcdef}} instead of {{abcdef}}.
This was exposed while testing an HTTP/2 authentication retry after the server
completed a {{401}} response and reset the request stream with
{{RST_STREAM(NO_ERROR)}}. That graceful reset releases the request producer
before the higher layer retries it. See
[HTTPCLIENT-2433|https://issues.apache.org/jira/browse/HTTPCLIENT-2433] for the
related HttpClient issue.
h2. Expected behavior
Calling {{releaseResources()}} should discard pending bytes and restore a
repeatable producer to the beginning of its source. A subsequent production
should emit exactly the original file.
h2. Actual behavior
Any bytes that were buffered but not accepted by the previous data channel are
prepended to the next production.
h2. Affected code
* {{org.apache.hc.core5.http.nio.entity.FileEntityProducer}}
* {{org.apache.hc.core5.http.nio.entity.PathEntityProducer}}
The problem is present in HttpCore 5.5-beta2 and current {{master}}.
h2. Proposed fix
Clear the internal {{ByteBuffer}} in each producer's {{releaseResources()}}
method. Add regression tests that interrupt production after a partial channel
write, release the producer, and verify that its next production contains the
original file exactly once.
Summary: Async entity producers retain stale state after
interrupted writes (was: FileEntityProducer and PathEntityProducer retain
stale buffered data after an interrupted write)
> Async entity producers retain stale state after interrupted writes
> ------------------------------------------------------------------
>
> Key: HTTPCORE-799
> URL: https://issues.apache.org/jira/browse/HTTPCORE-799
> Project: HttpComponents HttpCore
> Issue Type: Bug
> Components: HttpCore
> Affects Versions: 5.4.3, 5.5-beta2
> Reporter: Matthias
> Priority: Major
> Time Spent: 10m
> Remaining Estimate: 0h
>
> h2. Description
> Several repeatable async entity producers do not fully reset their
> per-production state when {{releaseResources()}} is called before the current
> production has completed.
> h3. Buffered output
> {{FileEntityProducer}}, {{PathEntityProducer}},
> {{AbstractCharAsyncEntityProducer}}, and {{AbstractBinAsyncEntityProducer}}
> can retain bytes that the data channel did not accept. Although the
> underlying source is reset for a replay, the internal staging buffer still
> contains the unwritten tail, so those stale bytes are emitted before the
> complete entity.
> {{StringAsyncEntityProducer}} is affected through
> {{AbstractCharAsyncEntityProducer}}. Repeatable custom binary producers can
> be affected through {{AbstractBinAsyncEntityProducer}}.
> For example, with content {{abcdef}}:
> # The producer reads {{abcdef}} and the data channel accepts only {{abc}}.
> # {{releaseResources()}} is called, simulating an interrupted request.
> # The same repeatable producer is used again.
> # The second production emits {{defabcdef}} instead of {{abcdef}}.
> h3. Digest state
> {{DigestingEntityProducer.releaseResources()}} delegates to the wrapped
> producer but does not reset its {{MessageDigest}}. If the first production
> writes {{abc}} before interruption and the replay writes {{abcdef}}, the
> replay body can be correct while {{getDigest()}} and the {{digest}} trailer
> contain the MD5 of {{abcabcdef}} ({{89091e2d8ada6ecb23f3d2b71fb1764b}})
> instead of the MD5 of {{abcdef}} ({{e80b5017098950fc58aad83c8c14978e}}).
> h2. Context
> This was exposed while testing an HTTP/2 authentication retry after a server
> returned a {{401}} response and then reset the stream with
> {{RST_STREAM(NO_ERROR)}}. The request producer was released mid-write and
> reused for the retry. See
> [HTTPCLIENT-2433|https://issues.apache.org/jira/browse/HTTPCLIENT-2433] for
> the related HttpClient issue.
> The HttpCore problem is not specific to HTTP/2 or authentication; it can
> occur whenever a repeatable producer is released after a partial write and
> then reused.
> h2. Expected behavior
> Calling {{releaseResources()}} should discard pending output and reset all
> per-production digest state. A subsequent production should emit exactly the
> original entity, and its digest should cover only that production.
> h2. Actual behavior
> Buffered but unwritten bytes can be prepended to the next production, and
> bytes accepted during an interrupted production can be included in the
> replay's digest.
> h2. Affected code
> * {{org.apache.hc.core5.http.nio.entity.FileEntityProducer}}
> * {{org.apache.hc.core5.http.nio.entity.PathEntityProducer}}
> * {{org.apache.hc.core5.http.nio.entity.AbstractCharAsyncEntityProducer}}
> (including {{StringAsyncEntityProducer}})
> * {{org.apache.hc.core5.http.nio.entity.AbstractBinAsyncEntityProducer}} (for
> repeatable subclasses)
> * {{org.apache.hc.core5.http.nio.entity.DigestingEntityProducer}}
> The problem is present in HttpCore 5.4.3, 5.5-beta2, and the current
> {{5.4.x}} and {{master}} branches.
> h2. Proposed fix
> Clear the internal staging buffers when the file, path, character, and binary
> producers are released. Reset {{DigestingEntityProducer}}'s {{MessageDigest}}
> before releasing its wrapped producer, while preserving the completed
> {{digest}} value returned by {{getDigest()}}.
> Add regression tests that interrupt production after a partial channel write,
> release the producer, and verify that the next production contains the
> original entity exactly once and calculates its digest only from the replayed
> bytes.
> Implementation:
> [apache/httpcomponents-core#698|https://github.com/apache/httpcomponents-core/pull/698]
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]