Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 72 additions & 43 deletions src/Sentry/Internal/Http/HttpTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Sentry.Extensibility;
Expand All @@ -14,7 +15,10 @@

namespace Sentry.Internal.Http
{
internal class HttpTransport : ITransport
/// <summary>
/// Internal HTTP Transport logic implementation. This is only meant to be used by this and other Sentry SDKs.
/// </summary>
public class HttpTransport : ITransport
{
private readonly SentryOptions _options;
private readonly HttpClient _httpClient;
Expand All @@ -33,6 +37,9 @@ internal class HttpTransport : ITransport

internal const string DefaultErrorMessage = "No message";

/// <summary>
/// Creates the internal HTTP Transport with the given option and HttpClient implementation.
/// </summary>
public HttpTransport(SentryOptions options, HttpClient httpClient)
: this(options, httpClient, Environment.GetEnvironmentVariable)
{
Expand All @@ -46,9 +53,11 @@ internal HttpTransport(SentryOptions options, HttpClient httpClient,
_getEnvironmentVariable = getEnvironmentVariable;
}

private Envelope ProcessEnvelope(Envelope envelope, DateTimeOffset instant)
/// <summary>
/// Re-package the envelope, discarding items that don't fit the rate limit
/// </summary>
protected Envelope ProcessEnvelope(Envelope envelope, DateTimeOffset instant)
{
// Re-package envelope, discarding items that don't fit the rate limit
var envelopeItems = new List<EnvelopeItem>();
foreach (var envelopeItem in envelope.Items)
{
Expand Down Expand Up @@ -109,10 +118,20 @@ private Envelope ProcessEnvelope(Envelope envelope, DateTimeOffset instant)
}
}

if (envelopeItems.Count == 0)
{
_options.LogInfo(
"Envelope {0} was discarded because all contained items are rate-limited.",
envelope.TryGetEventId());
}

return new Envelope(envelope.Header, envelopeItems);
}

private void ExtractRateLimits(HttpResponseMessage response, DateTimeOffset instant)
/// <summary>
/// Update local rate limits based on the response from the server.
/// </summary>
protected void ExtractRateLimits(HttpResponseMessage response, DateTimeOffset instant)
{
if (!response.Headers.TryGetValues("X-Sentry-Rate-Limits", out var rateLimitHeaderValues))
{
Expand All @@ -135,6 +154,7 @@ private void ExtractRateLimits(HttpResponseMessage response, DateTimeOffset inst
}
}

/// <inheritdoc/>
public async Task SendEnvelopeAsync(Envelope envelope, CancellationToken cancellationToken = default)
{
var instant = DateTimeOffset.Now;
Expand All @@ -143,10 +163,6 @@ public async Task SendEnvelopeAsync(Envelope envelope, CancellationToken cancell
using var processedEnvelope = ProcessEnvelope(envelope, instant);
if (processedEnvelope.Items.Count == 0)
{
_options.LogInfo(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this log message was removed?

"Envelope {0} was discarded because all contained items are rate-limited.",
envelope.TryGetEventId());

return;
}

Expand All @@ -160,19 +176,16 @@ public async Task SendEnvelopeAsync(Envelope envelope, CancellationToken cancell
if (response.StatusCode != HttpStatusCode.OK)
{
await HandleFailureAsync(response, processedEnvelope, cancellationToken).ConfigureAwait(false);
return;
}

if (_options.DiagnosticLogger?.IsEnabled(SentryLevel.Debug) is true)
else if (_options.DiagnosticLogger?.IsEnabled(SentryLevel.Debug) is true)
{
_options.LogDebug("Envelope '{0}' sent successfully. Payload:\n{1}",
envelope.TryGetEventId(),
await envelope.SerializeToStringAsync(_options.DiagnosticLogger, cancellationToken).ConfigureAwait(false));
}
else
{
_options.LogInfo("Envelope '{0}' successfully received by Sentry.",
processedEnvelope.TryGetEventId());
_options.LogInfo("Envelope '{0}' successfully received by Sentry.", processedEnvelope.TryGetEventId());
}
}

Expand All @@ -182,42 +195,18 @@ private async Task HandleFailureAsync(
CancellationToken cancellationToken)
{
// Spare the overhead if level is not enabled
if (_options.DiagnosticLogger?.IsEnabled(SentryLevel.Error) is true &&
response.Content is { } content)
if (_options.DiagnosticLogger?.IsEnabled(SentryLevel.Error) is true && response.Content is { } content)
{
if (string.Equals(content.Headers.ContentType?.MediaType, "application/json",
StringComparison.OrdinalIgnoreCase))
{
var responseJson = await content.ReadAsJsonAsync(cancellationToken).ConfigureAwait(false);

var errorMessage =
responseJson.GetPropertyOrNull("detail")?.GetString()
?? DefaultErrorMessage;

var errorCauses =
responseJson.GetPropertyOrNull("causes")?.EnumerateArray().Select(j => j.GetString()).ToArray()
?? Array.Empty<string>();

_options.Log(
SentryLevel.Error,
"Sentry rejected the envelope {0}. Status code: {1}. Error detail: {2}. Error causes: {3}.",
null,
processedEnvelope.TryGetEventId(),
response.StatusCode,
errorMessage,
string.Join(", ", errorCauses));
LogFailure(response, processedEnvelope,
await content.ReadAsJsonAsync(cancellationToken).ConfigureAwait(false));
}
else
{
var responseString = await content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);

_options.Log(
SentryLevel.Error,
"Sentry rejected the envelope {0}. Status code: {1}. Error detail: {2}.",
null,
processedEnvelope.TryGetEventId(),
response.StatusCode,
responseString);
LogFailure(response, processedEnvelope,
await content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false));
}

// If debug level, dump the whole envelope to the logger
Expand Down Expand Up @@ -260,7 +249,47 @@ private async Task HandleFailureAsync(
}
}

internal HttpRequestMessage CreateRequest(Envelope envelope)
/// <summary>
/// Log failure response.
/// </summary>
protected void LogFailure(HttpResponseMessage response, Envelope processedEnvelope, JsonElement responseJson)
{
var errorMessage =
responseJson.GetPropertyOrNull("detail")?.GetString()
?? DefaultErrorMessage;

var errorCauses =
responseJson.GetPropertyOrNull("causes")?.EnumerateArray().Select(j => j.GetString()).ToArray()
?? Array.Empty<string>();

_options.Log(
SentryLevel.Error,
"Sentry rejected the envelope {0}. Status code: {1}. Error detail: {2}. Error causes: {3}.",
null,
processedEnvelope.TryGetEventId(),
response.StatusCode,
errorMessage,
string.Join(", ", errorCauses));
}

/// <summary>
/// Log failure response.
/// </summary>
protected void LogFailure(HttpResponseMessage response, Envelope processedEnvelope, string responseString)
{
_options.Log(
SentryLevel.Error,
"Sentry rejected the envelope {0}. Status code: {1}. Error detail: {2}.",
null,
processedEnvelope.TryGetEventId(),
response.StatusCode,
responseString);
}

/// <summary>
/// Create HTTP request for the envelope.
/// </summary>
protected internal HttpRequestMessage CreateRequest(Envelope envelope)
{
if (string.IsNullOrWhiteSpace(_options.Dsn))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,19 @@ namespace Sentry.Integrations
void Register(Sentry.IHub hub, Sentry.SentryOptions options);
}
}
namespace Sentry.Internal.Http
{
public class HttpTransport
{
public HttpTransport(Sentry.SentryOptions options, System.Net.Http.HttpClient httpClient) { }
protected System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { }
protected void ExtractRateLimits(System.Net.Http.HttpResponseMessage response, System.DateTimeOffset instant) { }
protected void LogFailure(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope processedEnvelope, string responseString) { }
protected void LogFailure(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope processedEnvelope, System.Text.Json.JsonElement responseJson) { }
protected Sentry.Protocol.Envelopes.Envelope ProcessEnvelope(Sentry.Protocol.Envelopes.Envelope envelope, System.DateTimeOffset instant) { }
public System.Threading.Tasks.Task SendEnvelopeAsync(Sentry.Protocol.Envelopes.Envelope envelope, System.Threading.CancellationToken cancellationToken = default) { }
}
}
namespace Sentry.PlatformAbstractions
{
public static class FrameworkInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,19 @@ namespace Sentry.Integrations
void Register(Sentry.IHub hub, Sentry.SentryOptions options);
}
}
namespace Sentry.Internal.Http
{
public class HttpTransport
{
public HttpTransport(Sentry.SentryOptions options, System.Net.Http.HttpClient httpClient) { }
protected System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { }
protected void ExtractRateLimits(System.Net.Http.HttpResponseMessage response, System.DateTimeOffset instant) { }
protected void LogFailure(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope processedEnvelope, string responseString) { }
protected void LogFailure(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope processedEnvelope, System.Text.Json.JsonElement responseJson) { }
protected Sentry.Protocol.Envelopes.Envelope ProcessEnvelope(Sentry.Protocol.Envelopes.Envelope envelope, System.DateTimeOffset instant) { }
public System.Threading.Tasks.Task SendEnvelopeAsync(Sentry.Protocol.Envelopes.Envelope envelope, System.Threading.CancellationToken cancellationToken = default) { }
}
}
namespace Sentry.PlatformAbstractions
{
public static class FrameworkInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,19 @@ namespace Sentry.Integrations
void Register(Sentry.IHub hub, Sentry.SentryOptions options);
}
}
namespace Sentry.Internal.Http
{
public class HttpTransport
{
public HttpTransport(Sentry.SentryOptions options, System.Net.Http.HttpClient httpClient) { }
protected System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { }
protected void ExtractRateLimits(System.Net.Http.HttpResponseMessage response, System.DateTimeOffset instant) { }
protected void LogFailure(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope processedEnvelope, string responseString) { }
protected void LogFailure(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope processedEnvelope, System.Text.Json.JsonElement responseJson) { }
protected Sentry.Protocol.Envelopes.Envelope ProcessEnvelope(Sentry.Protocol.Envelopes.Envelope envelope, System.DateTimeOffset instant) { }
public System.Threading.Tasks.Task SendEnvelopeAsync(Sentry.Protocol.Envelopes.Envelope envelope, System.Threading.CancellationToken cancellationToken = default) { }
}
}
namespace Sentry.PlatformAbstractions
{
public static class FrameworkInfo
Expand Down
13 changes: 13 additions & 0 deletions test/Sentry.Tests/ApiApprovalTests.Run.Core2_1.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,19 @@ namespace Sentry.Integrations
void Register(Sentry.IHub hub, Sentry.SentryOptions options);
}
}
namespace Sentry.Internal.Http
{
public class HttpTransport
{
public HttpTransport(Sentry.SentryOptions options, System.Net.Http.HttpClient httpClient) { }
protected System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { }
protected void ExtractRateLimits(System.Net.Http.HttpResponseMessage response, System.DateTimeOffset instant) { }
protected void LogFailure(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope processedEnvelope, string responseString) { }
protected void LogFailure(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope processedEnvelope, System.Text.Json.JsonElement responseJson) { }
protected Sentry.Protocol.Envelopes.Envelope ProcessEnvelope(Sentry.Protocol.Envelopes.Envelope envelope, System.DateTimeOffset instant) { }
public System.Threading.Tasks.Task SendEnvelopeAsync(Sentry.Protocol.Envelopes.Envelope envelope, System.Threading.CancellationToken cancellationToken = default) { }
}
}
namespace Sentry.PlatformAbstractions
{
public static class FrameworkInfo
Expand Down
13 changes: 13 additions & 0 deletions test/Sentry.Tests/ApiApprovalTests.Run.Core3_0.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,19 @@ namespace Sentry.Integrations
void Register(Sentry.IHub hub, Sentry.SentryOptions options);
}
}
namespace Sentry.Internal.Http
{
public class HttpTransport
{
public HttpTransport(Sentry.SentryOptions options, System.Net.Http.HttpClient httpClient) { }
protected System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { }
protected void ExtractRateLimits(System.Net.Http.HttpResponseMessage response, System.DateTimeOffset instant) { }
protected void LogFailure(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope processedEnvelope, string responseString) { }
protected void LogFailure(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope processedEnvelope, System.Text.Json.JsonElement responseJson) { }
protected Sentry.Protocol.Envelopes.Envelope ProcessEnvelope(Sentry.Protocol.Envelopes.Envelope envelope, System.DateTimeOffset instant) { }
public System.Threading.Tasks.Task SendEnvelopeAsync(Sentry.Protocol.Envelopes.Envelope envelope, System.Threading.CancellationToken cancellationToken = default) { }
}
}
namespace Sentry.PlatformAbstractions
{
public static class FrameworkInfo
Expand Down
13 changes: 13 additions & 0 deletions test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,19 @@ namespace Sentry.Integrations
void Register(Sentry.IHub hub, Sentry.SentryOptions options);
}
}
namespace Sentry.Internal.Http
{
public class HttpTransport
{
public HttpTransport(Sentry.SentryOptions options, System.Net.Http.HttpClient httpClient) { }
protected System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { }
protected void ExtractRateLimits(System.Net.Http.HttpResponseMessage response, System.DateTimeOffset instant) { }
protected void LogFailure(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope processedEnvelope, string responseString) { }
protected void LogFailure(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope processedEnvelope, System.Text.Json.JsonElement responseJson) { }
protected Sentry.Protocol.Envelopes.Envelope ProcessEnvelope(Sentry.Protocol.Envelopes.Envelope envelope, System.DateTimeOffset instant) { }
public System.Threading.Tasks.Task SendEnvelopeAsync(Sentry.Protocol.Envelopes.Envelope envelope, System.Threading.CancellationToken cancellationToken = default) { }
}
}
namespace Sentry.PlatformAbstractions
{
public static class FrameworkInfo
Expand Down
13 changes: 13 additions & 0 deletions test/Sentry.Tests/ApiApprovalTests.Run.DotNet4_6.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,19 @@ namespace Sentry.Integrations
void Register(Sentry.IHub hub, Sentry.SentryOptions options);
}
}
namespace Sentry.Internal.Http
{
public class HttpTransport
{
public HttpTransport(Sentry.SentryOptions options, System.Net.Http.HttpClient httpClient) { }
protected System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { }
protected void ExtractRateLimits(System.Net.Http.HttpResponseMessage response, System.DateTimeOffset instant) { }
protected void LogFailure(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope processedEnvelope, string responseString) { }
protected void LogFailure(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope processedEnvelope, System.Text.Json.JsonElement responseJson) { }
protected Sentry.Protocol.Envelopes.Envelope ProcessEnvelope(Sentry.Protocol.Envelopes.Envelope envelope, System.DateTimeOffset instant) { }
public System.Threading.Tasks.Task SendEnvelopeAsync(Sentry.Protocol.Envelopes.Envelope envelope, System.Threading.CancellationToken cancellationToken = default) { }
}
}
namespace Sentry.PlatformAbstractions
{
public static class FrameworkInfo
Expand Down
13 changes: 13 additions & 0 deletions test/Sentry.Tests/ApiApprovalTests.Run.DotNet5_0.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,19 @@ namespace Sentry.Integrations
void Register(Sentry.IHub hub, Sentry.SentryOptions options);
}
}
namespace Sentry.Internal.Http
{
public class HttpTransport
{
public HttpTransport(Sentry.SentryOptions options, System.Net.Http.HttpClient httpClient) { }
protected System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { }
protected void ExtractRateLimits(System.Net.Http.HttpResponseMessage response, System.DateTimeOffset instant) { }
protected void LogFailure(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope processedEnvelope, string responseString) { }
protected void LogFailure(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope processedEnvelope, System.Text.Json.JsonElement responseJson) { }
protected Sentry.Protocol.Envelopes.Envelope ProcessEnvelope(Sentry.Protocol.Envelopes.Envelope envelope, System.DateTimeOffset instant) { }
public System.Threading.Tasks.Task SendEnvelopeAsync(Sentry.Protocol.Envelopes.Envelope envelope, System.Threading.CancellationToken cancellationToken = default) { }
}
}
namespace Sentry.PlatformAbstractions
{
public static class FrameworkInfo
Expand Down
Loading