-
Notifications
You must be signed in to change notification settings - Fork 10.6k
Merging internal commits for release/9.0 #65731
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
Changes from all commits
44ceb52
17b23f0
e36ce5f
b903e50
7a44dff
43f55d4
1cb0db3
4105418
9c05c35
e66df1b
dd09153
5772ffd
a8ed809
ccb435d
b69355e
cbf21d5
5d3ebab
7e484d6
bdfee04
06d9e25
7713049
9177db0
07210d6
464963a
d838d54
d1ce7df
afb8213
3d77c0d
e790ef0
6ea9bf5
0e8f848
baa6b29
abf90c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -205,7 +205,7 @@ internal ValueTask WriteAsync(HubMessage message, bool ignoreAbort, Cancellation | |||||
| // The write didn't complete synchronously so await completion | ||||||
| if (!task.IsCompletedSuccessfully) | ||||||
| { | ||||||
| return new ValueTask(CompleteWriteAsync(task)); | ||||||
| return new ValueTask(CompleteWriteAsync(task, cancellationToken)); | ||||||
| } | ||||||
| else | ||||||
| { | ||||||
|
|
@@ -250,7 +250,7 @@ public virtual ValueTask WriteAsync(SerializedHubMessage message, CancellationTo | |||||
| // The write didn't complete synchronously so await completion | ||||||
| if (!task.IsCompletedSuccessfully) | ||||||
| { | ||||||
| return new ValueTask(CompleteWriteAsync(task)); | ||||||
| return new ValueTask(CompleteWriteAsync(task, cancellationToken)); | ||||||
| } | ||||||
| else | ||||||
| { | ||||||
|
|
@@ -271,7 +271,30 @@ private ValueTask<FlushResult> WriteCore(HubMessage message, CancellationToken c | |||||
| { | ||||||
| if (UsingStatefulReconnect()) | ||||||
| { | ||||||
| return _messageBuffer.WriteAsync(message, cancellationToken); | ||||||
| return WriteAsync(_messageBuffer, this, message, cancellationToken); | ||||||
|
|
||||||
| static async ValueTask<FlushResult> WriteAsync(MessageBuffer messageBuffer, HubConnectionContext hubConnectionContext, | ||||||
| HubMessage message, CancellationToken cancellationToken) | ||||||
| { | ||||||
| CancellationTokenSource? cts = null; | ||||||
| var connectionToken = hubConnectionContext.ConnectionAborted; | ||||||
| if (message is CloseMessage) | ||||||
| { | ||||||
| // If it's a CloseMessage, we might already have triggered the ConnectionAborted token | ||||||
| // We would like to successfully send the CloseMessage for graceful close which means we can't use the ConnectionAborted token, | ||||||
| // but we need to make sure we don't get blocked by backpressure or anything, so we use a short-lived token. | ||||||
| cts = new CancellationTokenSource(TimeSpan.FromSeconds(5), hubConnectionContext._timeProvider); | ||||||
| connectionToken = cts.Token; | ||||||
| } | ||||||
|
|
||||||
| using var __ = cts; | ||||||
|
||||||
| using var __ = cts; | |
| using var ctsDisposable = cts; |
Copilot
AI
Mar 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CompleteWriteAsync filters out OperationCanceledException only when the caller-provided cancellationToken is canceled, but in the stateful reconnect path MessageBuffer.WriteAsync is awaited with a linked token (ConnectionAborted/timeout + caller token). If the linked token is canceled (e.g., connection abort/backpressure) while the caller token is not, the OCE will be treated as a write failure (CloseException set, FailedWritingMessage logged, AbortAllowReconnect called). Consider extending the filter to also ignore OCE when the connection is aborting (e.g., ConnectionAborted.IsCancellationRequested / _connectionAbortedTokenSource.IsCancellationRequested), or otherwise ensure the same token that can cancel MessageBuffer.WriteAsync is used for this decision.
Copilot
AI
Mar 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same cancellation-token mismatch as in CompleteWriteAsync: WriteSlowAsync catches/logs OperationCanceledException unless the caller-provided cancellationToken is canceled, but stateful reconnect writes can be canceled via a linked token (e.g., ConnectionAborted) even when the caller token isn't. This can incorrectly set CloseException/log FailedWritingMessage during normal abort/timeout. Consider excluding OCE when the connection is aborting in addition to when cancellationToken.IsCancellationRequested.
| catch (Exception ex) when (ex is not OperationCanceledException || !cancellationToken.IsCancellationRequested) | |
| catch (Exception ex) when (ex is not OperationCanceledException || (!cancellationToken.IsCancellationRequested && !_connectionAborted)) |
Copilot
AI
Mar 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same issue as the HubMessage WriteSlowAsync overload: this catch filter only treats OperationCanceledException as non-fatal when the caller-provided token is canceled, but stateful reconnect writes can be canceled by ConnectionAborted/linked tokens. Consider also excluding OCE when the connection is aborting to avoid incorrectly setting CloseException/logging FailedWritingMessage during normal shutdown.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as the server-side change:
using var __ = cts;is a new pattern here and makes the disposal intent less clear. Consider a more descriptive name or explicitcts?.Dispose()in afinallyblock.