Skip to content
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

Use 'HttpMessageHandler' instead of 'HttpClientHandler' to support NSUrlSessionHandler on iOS #2503

Merged
merged 12 commits into from
Jul 25, 2023
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions samples/Sentry.Samples.Console.Customized/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ private static async Task Main()
// 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
Expand Down
2 changes: 1 addition & 1 deletion samples/Sentry.Samples.Console.Profiling/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ private static async Task Main()
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
Expand Down
80 changes: 66 additions & 14 deletions src/Sentry/Internal/Http/DefaultSentryHttpClientFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
// <see cref="T:System.Net.Http.NSUrlSessionHandler" /> 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)
{
Expand Down
22 changes: 21 additions & 1 deletion src/Sentry/SentryOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -540,10 +540,30 @@ public int MaxCacheItems
/// </summary>
public IWebProxy? HttpProxy { get; set; }

/// <summary>
/// private field to hold the <see cref="CreateHttpClientHandler"/>, since a typecheck or cast won't work here.
/// </summary>
private Func<HttpClientHandler>? _createClientHandler = null;

/// <summary>
/// Creates the inner most <see cref="HttpClientHandler"/>.
/// Deprecated in favor of <see cref="CreateHttpMessageHandler"/>.
/// </summary>
[Obsolete("Use CreateHttpMessageHandler instead")]
public Func<HttpClientHandler>? CreateHttpClientHandler
{
get => _createClientHandler;
set
{
CreateHttpMessageHandler = value;
_createClientHandler = value;
}
}

/// <summary>
/// Creates the inner most <see cref="HttpMessageHandler"/>.
/// </summary>
public Func<HttpClientHandler>? CreateHttpClientHandler { get; set; }
public Func<HttpMessageHandler>? CreateHttpMessageHandler { get; set; }

/// <summary>
/// A callback invoked when a <see cref="SentryClient"/> is created.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<FailingFunction>();
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,9 @@ namespace Sentry
public bool CaptureFailedRequests { get; set; }
public System.Action<System.Net.Http.HttpClient>? ConfigureClient { get; set; }
public System.Func<bool>? CrashedLastRun { get; set; }
[System.Obsolete("Use CreateHttpMessageHandler instead")]
public System.Func<System.Net.Http.HttpClientHandler>? CreateHttpClientHandler { get; set; }
public System.Func<System.Net.Http.HttpMessageHandler>? CreateHttpMessageHandler { get; set; }
public bool Debug { get; set; }
public System.Net.DecompressionMethods DecompressionMethods { get; set; }
public Sentry.DeduplicateMode DeduplicateMode { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,9 @@ namespace Sentry
public bool CaptureFailedRequests { get; set; }
public System.Action<System.Net.Http.HttpClient>? ConfigureClient { get; set; }
public System.Func<bool>? CrashedLastRun { get; set; }
[System.Obsolete("Use CreateHttpMessageHandler instead")]
public System.Func<System.Net.Http.HttpClientHandler>? CreateHttpClientHandler { get; set; }
public System.Func<System.Net.Http.HttpMessageHandler>? CreateHttpMessageHandler { get; set; }
public bool Debug { get; set; }
public System.Net.DecompressionMethods DecompressionMethods { get; set; }
public Sentry.DeduplicateMode DeduplicateMode { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,9 @@ namespace Sentry
public bool CaptureFailedRequests { get; set; }
public System.Action<System.Net.Http.HttpClient>? ConfigureClient { get; set; }
public System.Func<bool>? CrashedLastRun { get; set; }
[System.Obsolete("Use CreateHttpMessageHandler instead")]
public System.Func<System.Net.Http.HttpClientHandler>? CreateHttpClientHandler { get; set; }
public System.Func<System.Net.Http.HttpMessageHandler>? CreateHttpMessageHandler { get; set; }
public bool Debug { get; set; }
public System.Net.DecompressionMethods DecompressionMethods { get; set; }
public Sentry.DeduplicateMode DeduplicateMode { get; set; }
Expand Down
2 changes: 2 additions & 0 deletions test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,9 @@ namespace Sentry
public bool CaptureFailedRequests { get; set; }
public System.Action<System.Net.Http.HttpClient>? ConfigureClient { get; set; }
public System.Func<bool>? CrashedLastRun { get; set; }
[System.Obsolete("Use CreateHttpMessageHandler instead")]
public System.Func<System.Net.Http.HttpClientHandler>? CreateHttpClientHandler { get; set; }
public System.Func<System.Net.Http.HttpMessageHandler>? CreateHttpMessageHandler { get; set; }
public bool Debug { get; set; }
public System.Net.DecompressionMethods DecompressionMethods { get; set; }
public Sentry.DeduplicateMode DeduplicateMode { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion test/Sentry.Tests/HubTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public void Create_DefaultHeaders_AcceptJson()
public void Create_ProvidedCreateHttpClientHandler_ReturnedHandlerUsed()
{
var handler = Substitute.For<HttpClientHandler>();
_fixture.HttpOptions.CreateHttpClientHandler = () => handler;
_fixture.HttpOptions.CreateHttpMessageHandler = () => handler;
var sut = Fixture.GetSut();

var client = sut.Create(_fixture.HttpOptions);
Expand All @@ -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);
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion test/Sentry.Tests/SentryClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<HttpClientHandler>();
Expand Down