fix: prevent concurrent HttpContent matching from corrupting the stream position - #875
Conversation
…am position HttpContent caches the stream returned by ReadAsStream, so the string and binary content matchers share it when a setup and a verification match the same request concurrently (e.g. a polling verification with Within while the request is still being matched against the setups). Interleaved save/restore of the stream position could leave it at the end, after which every match read an empty body and the verification never succeeded. The read and position reset are now done under a lock on the stream.
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The locking directly addresses the stream-position race and is covered by focused regression tests.
Review effort: Balanced
Findings: None
What changed in this PR
Prevents concurrent HTTP body matchers from corrupting a shared cached stream position.
Changes:
- Synchronizes string and binary stream reads and position restoration.
- Adds concurrent verification regression tests for both matcher types.
| File | Description |
|---|---|
Source/Mockolate/Web/ItExtensions.HttpContent.cs |
Locks shared content streams during matching. |
Tests/Mockolate.Tests/Web/ItExtensionsTests.IsHttpContentTests.WithStringTests.cs |
Tests concurrent string matching. |
Tests/Mockolate.Tests/Web/ItExtensionsTests.IsHttpContentTests.WithBytesTests.cs |
Tests concurrent binary matching. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
🚀 Benchmark ResultsDetails
Details
Details
Details
Details
Details
|
Stream derives from MarshalByRefObject, which SonarCloud flags as an unsafe lock target. The read stream is cached per HttpContent, so locking on the content instance protects the same shared stream.
|
…ching from corrupting the stream position (#875) by Valentin Breuß
…ching from corrupting the stream position (#875) by Valentin Breuß
|
This is addressed in release v3.5.2. |




HttpContent caches the stream returned by ReadAsStream, so the string and binary content matchers share it when a setup and a verification match the same request concurrently (e.g. a polling verification with Within while the request is still being matched against the setups). Interleaved save/restore of the stream position could leave it at the end, after which every match read an empty body and the verification never succeeded. The read and position reset are now done under a lock on the HttpContent.