diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs index df4d468886c957..72f1f88e1e1c3d 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs @@ -165,9 +165,10 @@ await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false) _innerHandler.Send(request, cancellationToken); return response; } - catch (OperationCanceledException) + catch (OperationCanceledException ex) { taskStatus = TaskStatus.Canceled; + exception = ex; // we'll report task status in HttpRequestOut.Stop throw; diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs index 6748b5780f7f27..6fe645ce6b4513 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs @@ -830,6 +830,63 @@ static async Task RunTest(string useVersion, string testAsync, string failureTyp } } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public async Task SendAsync_OperationCanceledException_RecordsActivitiesWithCorrectErrorInfo() + { + await RemoteExecutor.Invoke(RunTest, UseVersion.ToString(), TestAsync.ToString()).DisposeAsync(); + static async Task RunTest(string useVersion, string testAsync) + { + TaskCompletionSource activityStopTcs = new(TaskCreationOptions.RunContinuationsAsynchronously); + Activity? activity = null; + + using ActivityListener listener = new ActivityListener() + { + ShouldListenTo = s => s.Name is "System.Net.Http", + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllData, + ActivityStopped = a => + { + activity = a; + Assert.Equal(ActivityStatusCode.Error, a.Status); + ActivityAssert.HasTag(a, "error.type", typeof(TaskCanceledException).FullName); + ActivityEvent evt = a.Events.Single(e => e.Name == "exception"); + Dictionary tags = evt.Tags.ToDictionary(t => t.Key, t => t.Value); + Assert.Contains("exception.type", tags.Keys); + Assert.Contains("exception.message", tags.Keys); + Assert.Contains("exception.stacktrace", tags.Keys); + Assert.Equal(typeof(TaskCanceledException).FullName, tags["exception.type"]); + + activityStopTcs.SetResult(); + } + }; + ActivitySource.AddActivityListener(listener); + + var cts = new CancellationTokenSource(); + + await GetFactoryForVersion(useVersion).CreateClientAndServerAsync( + async uri => + { + Version version = Version.Parse(useVersion); + if (version != HttpVersion30) + { + uri = new Uri($"{uri.Scheme}://localhost:{uri.Port}"); + } + + await Assert.ThrowsAsync(() => GetAsync(useVersion, testAsync, uri, cts.Token)); + }, + async server => + { + await server.AcceptConnectionAsync(async connection => + { + cts.Cancel(); + + await activityStopTcs.Task; + }); + }); + + Assert.NotNull(activity); + } + } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public async Task SendAsync_ExpectedDiagnosticSourceActivityLogging_InvalidBaggage() {