diff --git a/CHANGELOG.md b/CHANGELOG.md index b889cbc3f5..5d18578360 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Fixed baggage propagation when an exception is thrown from middleware ([#2487](https://github.com/getsentry/sentry-dotnet/pull/2487)) - Fix Durable Functions preventing orchestrators from completing ([#2491](https://github.com/getsentry/sentry-dotnet/pull/2491)) - Re-enable HubTests.FlushOnDispose_SendsEnvelope ([#2492](https://github.com/getsentry/sentry-dotnet/pull/2492)) +- Introduced `HttpMessageHandler` in favor of the now deprecated `HttpClientHandler` on the options. This allows the SDK to support NSUrlSessionHandler on iOS ([#2503](https://github.com/getsentry/sentry-dotnet/pull/2503)) - Fixed the SDK failing to report issues via Blazor WebAssembly due to a `PlatformNotSupportedException` ([#2506](https://github.com/getsentry/sentry-dotnet/pull/2506)) ### Dependencies diff --git a/samples/Sentry.Samples.Console.Customized/Program.cs b/samples/Sentry.Samples.Console.Customized/Program.cs index ae347ef6f4..b1af6291fd 100644 --- a/samples/Sentry.Samples.Console.Customized/Program.cs +++ b/samples/Sentry.Samples.Console.Customized/Program.cs @@ -90,8 +90,8 @@ await SentrySdk.ConfigureScopeAsync(async scope => // Using a proxy: o.HttpProxy = null; //new WebProxy("https://localhost:3128"); - // Example customizing the HttpClientHandlers created - o.CreateHttpClientHandler = () => new HttpClientHandler + // Example customizing the HttpMessageHandlers created + o.CreateHttpMessageHandler = () => new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, certificate, _, _) => !certificate.Archived diff --git a/samples/Sentry.Samples.Console.Profiling/Program.cs b/samples/Sentry.Samples.Console.Profiling/Program.cs index bf3fdf1dc1..5d3d1ee1c2 100644 --- a/samples/Sentry.Samples.Console.Profiling/Program.cs +++ b/samples/Sentry.Samples.Console.Profiling/Program.cs @@ -85,7 +85,7 @@ await SentrySdk.ConfigureScopeAsync(async scope => o.HttpProxy = null; //new WebProxy("https://localhost:3128"); // Example customizing the HttpClientHandlers created - o.CreateHttpClientHandler = () => new HttpClientHandler + o.CreateHttpMessageHandler = () => new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, certificate, _, _) => !certificate.Archived diff --git a/src/Sentry/Internal/Http/DefaultSentryHttpClientFactory.cs b/src/Sentry/Internal/Http/DefaultSentryHttpClientFactory.cs index 51ac29f6fc..f8577e5bd7 100644 --- a/src/Sentry/Internal/Http/DefaultSentryHttpClientFactory.cs +++ b/src/Sentry/Internal/Http/DefaultSentryHttpClientFactory.cs @@ -20,25 +20,77 @@ public HttpClient Create(SentryOptions options) throw new ArgumentNullException(nameof(options)); } - var httpClientHandler = options.CreateHttpClientHandler?.Invoke() ?? new HttpClientHandler(); - if (options.HttpProxy != null) + HttpMessageHandler handler = options.CreateHttpMessageHandler?.Invoke() ?? new HttpClientHandler(); + if (handler is HttpClientHandler httpClientHandler) { - httpClientHandler.Proxy = options.HttpProxy; - options.LogInfo("Using Proxy: {0}", options.HttpProxy); - } + if (options.HttpProxy != null) + { + httpClientHandler.Proxy = options.HttpProxy; + options.LogInfo("Using Proxy: {0}", options.HttpProxy); + } - // If the platform supports automatic decompression - if (SupportsAutomaticDecompression(httpClientHandler)) - { - // if the SDK is configured to accept compressed data - httpClientHandler.AutomaticDecompression = options.DecompressionMethods; + // If the platform supports automatic decompression + if (SupportsAutomaticDecompression(httpClientHandler)) + { + // if the SDK is configured to accept compressed data + httpClientHandler.AutomaticDecompression = options.DecompressionMethods; + } + else + { + options.LogWarning("No response compression supported by HttpClientHandler."); + } } - else +#if IOS + if (handler is System.Net.Http.NSUrlSessionHandler nsUrlSessionHandler) { - options.LogDebug("No response compression supported by HttpClientHandler."); - } + if (options.HttpProxy != null) + { + bool supportsProxy = false; + if (nsUrlSessionHandler.SupportsProxy) + { + supportsProxy = true; + try + { + // Code analysis reports this as error, since it is marked as unsupported. + // Being aware of that this code is meant to support this feature as soon as + // supports it. +#pragma warning disable CA1416 + nsUrlSessionHandler.Proxy = options.HttpProxy; +#pragma warning restore CA1416 + options.LogInfo("Using Proxy: {0}", options.HttpProxy); + } + catch (PlatformNotSupportedException) + { + supportsProxy = false; + } + } + if (!supportsProxy) + { + options.LogWarning("No proxy supported by NSUrlSessionHandler."); + } + } - HttpMessageHandler handler = httpClientHandler; + // If the platform supports automatic decompression + bool compressionSupported = false; + try + { + if (nsUrlSessionHandler.SupportsAutomaticDecompression) + { + // if the SDK is configured to accept compressed data + nsUrlSessionHandler.AutomaticDecompression = options.DecompressionMethods; + compressionSupported = true; + } + } + catch (PlatformNotSupportedException) + { + compressionSupported = false; + } + if (!compressionSupported) + { + options.LogInfo("No response compression supported by NSUrlSessionHandler."); + } + } +#endif if (options.RequestBodyCompressionLevel != CompressionLevel.NoCompression) { diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index fa99c1644d..7dbc14e97b 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -540,10 +540,30 @@ public int MaxCacheItems /// public IWebProxy? HttpProxy { get; set; } + /// + /// private field to hold the , since a typecheck or cast won't work here. + /// + private Func? _createClientHandler = null; + /// /// Creates the inner most . + /// Deprecated in favor of . + /// + [Obsolete("Use CreateHttpMessageHandler instead")] + public Func? CreateHttpClientHandler + { + get => _createClientHandler; + set + { + CreateHttpMessageHandler = value; + _createClientHandler = value; + } + } + + /// + /// Creates the inner most . /// - public Func? CreateHttpClientHandler { get; set; } + public Func? CreateHttpMessageHandler { get; set; } /// /// A callback invoked when a is created. diff --git a/test/Sentry.Google.Cloud.Functions.Tests/IntegrationTests.cs b/test/Sentry.Google.Cloud.Functions.Tests/IntegrationTests.cs index f4411ee92d..a073e84e74 100644 --- a/test/Sentry.Google.Cloud.Functions.Tests/IntegrationTests.cs +++ b/test/Sentry.Google.Cloud.Functions.Tests/IntegrationTests.cs @@ -34,7 +34,7 @@ async Task VerifyAsync(HttpRequestMessage message) { // So we can assert on the payload without the need to Gzip decompress o.RequestBodyCompressionLevel = CompressionLevel.NoCompression; - o.CreateHttpClientHandler = () => new CallbackHttpClientHandler(VerifyAsync); + o.CreateHttpMessageHandler = () => new CallbackHttpClientHandler(VerifyAsync); }); services.AddFunctionTarget(); }) diff --git a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs index 40d7e6e9cc..c6c47f0402 100644 --- a/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs +++ b/test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs @@ -131,7 +131,7 @@ async Task VerifyAsync(HttpRequestMessage message) FileSystem = fileSystem, // So we don't need to deal with gzip'ed payload RequestBodyCompressionLevel = CompressionLevel.NoCompression, - CreateHttpClientHandler = () => new CallbackHttpClientHandler(VerifyAsync), + CreateHttpMessageHandler = () => new CallbackHttpClientHandler(VerifyAsync), // Not to send some session envelope AutoSessionTracking = false, Debug = true, diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt index 8f01bbf7d5..7c4bb4c1d5 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt @@ -603,7 +603,9 @@ namespace Sentry public bool CaptureFailedRequests { get; set; } public System.Action? ConfigureClient { get; set; } public System.Func? CrashedLastRun { get; set; } + [System.Obsolete("Use CreateHttpMessageHandler instead")] public System.Func? CreateHttpClientHandler { get; set; } + public System.Func? CreateHttpMessageHandler { get; set; } public bool Debug { get; set; } public System.Net.DecompressionMethods DecompressionMethods { get; set; } public Sentry.DeduplicateMode DeduplicateMode { get; set; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 6cc1c4e4a6..e2a943d0d3 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -604,7 +604,9 @@ namespace Sentry public bool CaptureFailedRequests { get; set; } public System.Action? ConfigureClient { get; set; } public System.Func? CrashedLastRun { get; set; } + [System.Obsolete("Use CreateHttpMessageHandler instead")] public System.Func? CreateHttpClientHandler { get; set; } + public System.Func? CreateHttpMessageHandler { get; set; } public bool Debug { get; set; } public System.Net.DecompressionMethods DecompressionMethods { get; set; } public Sentry.DeduplicateMode DeduplicateMode { get; set; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 6cc1c4e4a6..e2a943d0d3 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -604,7 +604,9 @@ namespace Sentry public bool CaptureFailedRequests { get; set; } public System.Action? ConfigureClient { get; set; } public System.Func? CrashedLastRun { get; set; } + [System.Obsolete("Use CreateHttpMessageHandler instead")] public System.Func? CreateHttpClientHandler { get; set; } + public System.Func? CreateHttpMessageHandler { get; set; } public bool Debug { get; set; } public System.Net.DecompressionMethods DecompressionMethods { get; set; } public Sentry.DeduplicateMode DeduplicateMode { get; set; } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 5374f5f004..ac94a57bac 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -602,7 +602,9 @@ namespace Sentry public bool CaptureFailedRequests { get; set; } public System.Action? ConfigureClient { get; set; } public System.Func? CrashedLastRun { get; set; } + [System.Obsolete("Use CreateHttpMessageHandler instead")] public System.Func? CreateHttpClientHandler { get; set; } + public System.Func? CreateHttpMessageHandler { get; set; } public bool Debug { get; set; } public System.Net.DecompressionMethods DecompressionMethods { get; set; } public Sentry.DeduplicateMode DeduplicateMode { get; set; } diff --git a/test/Sentry.Tests/HubTests.cs b/test/Sentry.Tests/HubTests.cs index f1999225ae..8945b04b51 100644 --- a/test/Sentry.Tests/HubTests.cs +++ b/test/Sentry.Tests/HubTests.cs @@ -328,7 +328,7 @@ async Task VerifyAsync(HttpRequestMessage message) FileSystem = fileSystem, // So we don't need to deal with gzip'ed payload RequestBodyCompressionLevel = CompressionLevel.NoCompression, - CreateHttpClientHandler = () => new CallbackHttpClientHandler(VerifyAsync), + CreateHttpMessageHandler = () => new CallbackHttpClientHandler(VerifyAsync), // Not to send some session envelope AutoSessionTracking = false, Debug = true, diff --git a/test/Sentry.Tests/Internals/DefaultSentryHttpClientFactoryTests.cs b/test/Sentry.Tests/Internals/DefaultSentryHttpClientFactoryTests.cs index 1cf5b87c63..8e54d3b73a 100644 --- a/test/Sentry.Tests/Internals/DefaultSentryHttpClientFactoryTests.cs +++ b/test/Sentry.Tests/Internals/DefaultSentryHttpClientFactoryTests.cs @@ -98,7 +98,7 @@ public void Create_DefaultHeaders_AcceptJson() public void Create_ProvidedCreateHttpClientHandler_ReturnedHandlerUsed() { var handler = Substitute.For(); - _fixture.HttpOptions.CreateHttpClientHandler = () => handler; + _fixture.HttpOptions.CreateHttpMessageHandler = () => handler; var sut = Fixture.GetSut(); var client = sut.Create(_fixture.HttpOptions); @@ -109,7 +109,7 @@ public void Create_ProvidedCreateHttpClientHandler_ReturnedHandlerUsed() [Fact] public void Create_NullCreateHttpClientHandler_HttpClientHandlerUsed() { - _fixture.HttpOptions.CreateHttpClientHandler = null; + _fixture.HttpOptions.CreateHttpMessageHandler = null; var sut = Fixture.GetSut(); var client = sut.Create(_fixture.HttpOptions); @@ -120,7 +120,7 @@ public void Create_NullCreateHttpClientHandler_HttpClientHandlerUsed() [Fact] public void Create_NullReturnedCreateHttpClientHandler_HttpClientHandlerUsed() { - _fixture.HttpOptions.CreateHttpClientHandler = () => null; + _fixture.HttpOptions.CreateHttpMessageHandler = () => null; var sut = Fixture.GetSut(); var client = sut.Create(_fixture.HttpOptions); diff --git a/test/Sentry.Tests/SentryClientTests.cs b/test/Sentry.Tests/SentryClientTests.cs index fa9bd8aedc..abc5bb3123 100644 --- a/test/Sentry.Tests/SentryClientTests.cs +++ b/test/Sentry.Tests/SentryClientTests.cs @@ -1063,7 +1063,7 @@ public void Ctor_CreateHttpClientHandler_InvokedConfigureHandler() var invoked = false; _fixture.BackgroundWorker = null; _fixture.SentryOptions.Dsn = ValidDsn; - _fixture.SentryOptions.CreateHttpClientHandler = () => + _fixture.SentryOptions.CreateHttpMessageHandler = () => { invoked = true; return Substitute.For();