Skip to content

Conversation

@Shane32
Copy link
Owner

@Shane32 Shane32 commented Nov 26, 2025

This prevents the possibility that System.Text.Json calls Flush and send an end-of-message mark when it should not.

Summary by CodeRabbit

  • Bug Fixes

    • Improved WebSocket message transmission with corrected end-of-message signaling for enhanced protocol compliance and connection reliability. Stream flush operations and end-of-message signaling are now properly separated.
  • Tests

    • Updated WebSocket tests to validate the new end-of-message handling behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

@Shane32 Shane32 self-assigned this Nov 26, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 26, 2025

📝 Walkthrough

Walkthrough

The changes refactor WebSocket stream message handling by separating the FlushAsync method—now a no-op—from a new explicit SendEndOfMessageAsync method that signals message completion. Call sites are updated to use the new method for proper end-of-message semantics.

Changes

Cohort / File(s) Summary
WebSocket Stream Refactoring
src/GraphQL.AspNetCore3/WebSockets/WebSocketWriterStream.cs
FlushAsync() replaced with no-op returning Task.CompletedTask; new public SendEndOfMessageAsync(CancellationToken) method added to send end-of-message on underlying WebSocket; new public Flush() no-op method added
WebSocket Connection Update
src/GraphQL.AspNetCore3/WebSockets/WebSocketConnection.cs
OnSendMessageAsync now calls SendEndOfMessageAsync() instead of FlushAsync() after writing message to stream
Test Updates
src/Tests/WebSockets/WebSocketWriterStreamTests.cs
Previous FlushAsync() test renamed to SendEndOfMessageAsync() with updated assertions; new FlushAsync() test added to verify no-op behavior

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Areas requiring attention:
    • Verify that SendEndOfMessageAsync correctly delegates to the underlying WebSocket's end-of-message semantics
    • Confirm that changing FlushAsync to a no-op doesn't break any external consumers relying on the old flushing behavior
    • Validate test coverage for both methods properly reflects their distinct behaviors

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately describes the main API change: renaming FlushAsync to SendEndOfMessageAsync across the WebSocket-related files, which is reflected in the actual code changes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fixflush

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dd56890 and ed836c4.

📒 Files selected for processing (3)
  • src/GraphQL.AspNetCore3/WebSockets/WebSocketConnection.cs (1 hunks)
  • src/GraphQL.AspNetCore3/WebSockets/WebSocketWriterStream.cs (1 hunks)
  • src/Tests/WebSockets/WebSocketWriterStreamTests.cs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/GraphQL.AspNetCore3/WebSockets/WebSocketWriterStream.cs (1)
src/GraphQL.AspNetCore3/WebSockets/WebSocketConnection.cs (8)
  • Task (82-162)
  • Task (165-166)
  • Task (169-174)
  • Task (177-183)
  • Task (193-204)
  • Task (212-213)
  • Task (221-225)
  • Task (233-234)
src/Tests/WebSockets/WebSocketWriterStreamTests.cs (2)
src/GraphQL.AspNetCore3/WebSockets/WebSocketConnection.cs (8)
  • Task (82-162)
  • Task (165-166)
  • Task (169-174)
  • Task (177-183)
  • Task (193-204)
  • Task (212-213)
  • Task (221-225)
  • Task (233-234)
src/GraphQL.AspNetCore3/WebSockets/WebSocketWriterStream.cs (3)
  • Task (12-13)
  • Task (22-22)
  • Task (26-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: test (windows-latest)
  • GitHub Check: test (ubuntu-latest)
🔇 Additional comments (5)
src/GraphQL.AspNetCore3/WebSockets/WebSocketWriterStream.cs (2)

22-24: Correct Stream semantics for Flush methods.

Making both FlushAsync and Flush no-ops aligns with proper Stream semantics, where flushing ensures buffered data is written but doesn't control protocol-level message boundaries. This prevents serializers that call FlushAsync from prematurely finalizing WebSocket messages.


26-27: Clean implementation of explicit end-of-message signaling.

The method correctly sends an empty buffer with endOfMessage = true to finalize the WebSocket message. Using Array.Empty<byte>() is efficient as it returns a cached singleton.

src/Tests/WebSockets/WebSocketWriterStreamTests.cs (2)

54-61: Thorough test coverage for SendEndOfMessageAsync.

The test correctly verifies that SendEndOfMessageAsync sends an empty buffer with endOfMessage = true to the underlying WebSocket, and that no other calls are made. The mock setup properly validates the expected behavior.


63-69: Proper test coverage for no-op FlushAsync.

The test correctly verifies that FlushAsync no longer makes any calls to the underlying WebSocket. The absence of mock setup and the VerifyNoOtherCalls assertion properly validate the no-op behavior.

src/GraphQL.AspNetCore3/WebSockets/WebSocketConnection.cs (1)

221-225: Verification confirms: change is comprehensive and handles all message serialization paths.

The search results show only one production code path uses _serializer.WriteAsync with _stream in WebSocketConnection.cs line 223 (the changed method). The other _serializer.WriteAsync call in GraphQLHttpMiddleware writes to an HTTP response stream, which has different semantics and doesn't require end-of-message signaling. No other code paths bypass the SendEndOfMessageAsync call, confirming this change properly addresses all WebSocket message boundaries.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Shane32 Shane32 changed the title Prevent serializer from finalizing partial websocket message Change FlushAsync to SendEndOfMessageAsync Nov 26, 2025
@github-actions
Copy link

Coverage Report

Totals Coverage
Statements: 93.77% ( 2061 / 2198 )
Methods: 80.93% ( 314 / 388 )

@coveralls
Copy link

Pull Request Test Coverage Report for Build 19690454137

Details

  • 4 of 4 (100.0%) changed or added relevant lines in 2 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.06%) to 91.732%

Totals Coverage Status
Change from base Build 15600896893: 0.06%
Covered Lines: 2061
Relevant Lines: 2198

💛 - Coveralls

@Shane32 Shane32 merged commit 7cd7d06 into master Nov 26, 2025
6 checks passed
@Shane32 Shane32 deleted the fixflush branch November 26, 2025 02:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants