From b586731eac51a75593e742a7a50145e3d458c989 Mon Sep 17 00:00:00 2001 From: Daniel Rusznyak Date: Sun, 18 Jul 2021 19:51:16 +0100 Subject: [PATCH 01/15] Add Sentry.Tunnel to add official tunnel middleware --- Sentry.sln | 44 ++++++--- src/Sentry.Tunnel/Sentry.Tunnel.csproj | 27 ++++++ .../Sentry.Tunnel.csproj.DotSettings | 2 + src/Sentry.Tunnel/SentryTunnelMiddleware.cs | 81 ++++++++++++++++ ...ryTunnelingApplicationBuilderExtensions.cs | 26 +++++ test/Sentry.Tunnel.Tests/IntegrationsTests.cs | 95 +++++++++++++++++++ .../MockHttpMessageHandler.cs | 41 ++++++++ .../Sentry.Tunnel.Tests.csproj | 47 +++++++++ 8 files changed, 348 insertions(+), 15 deletions(-) create mode 100644 src/Sentry.Tunnel/Sentry.Tunnel.csproj create mode 100644 src/Sentry.Tunnel/Sentry.Tunnel.csproj.DotSettings create mode 100644 src/Sentry.Tunnel/SentryTunnelMiddleware.cs create mode 100644 src/Sentry.Tunnel/SentryTunnelingApplicationBuilderExtensions.cs create mode 100644 test/Sentry.Tunnel.Tests/IntegrationsTests.cs create mode 100644 test/Sentry.Tunnel.Tests/MockHttpMessageHandler.cs create mode 100644 test/Sentry.Tunnel.Tests/Sentry.Tunnel.Tests.csproj diff --git a/Sentry.sln b/Sentry.sln index b73bd9ddee..bf10ee2477 100644 --- a/Sentry.sln +++ b/Sentry.sln @@ -101,13 +101,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{F267 EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{E1800E08-72E6-45AE-91EC-69B4FDBA7555}" -ProjectSection(SolutionItems) = preProject - .github\workflows\build.yml = .github\workflows\build.yml - .github\workflows\codeql-analysis.yml = .github\workflows\codeql-analysis.yml - .github\workflows\danger.yml = .github\workflows\danger.yml - .github\workflows\docs.yml = .github\workflows\docs.yml - .github\workflows\vulnerabilities.yml = .github\workflows\vulnerabilities.yml -EndProjectSection + ProjectSection(SolutionItems) = preProject + .github\workflows\build.yml = .github\workflows\build.yml + .github\workflows\codeql-analysis.yml = .github\workflows\codeql-analysis.yml + .github\workflows\danger.yml = .github\workflows\danger.yml + .github\workflows\docs.yml = .github\workflows\docs.yml + .github\workflows\vulnerabilities.yml = .github\workflows\vulnerabilities.yml + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.AspNetCore.Grpc", "src\Sentry.AspNetCore.Grpc\Sentry.AspNetCore.Grpc.csproj", "{720811C8-29C7-4368-86F2-D61DF415AC2A}" EndProject @@ -115,21 +115,25 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.AspNetCore.Grpc.Test EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.AspNetCore.Grpc", "samples\Sentry.Samples.AspNetCore.Grpc\Sentry.Samples.AspNetCore.Grpc.csproj", "{21599C29-C3D4-4DAC-A2D6-6C194600478F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.AspNetCore.Blazor.Wasm", "samples\Sentry.Samples.AspNetCore.Blazor.Wasm\Sentry.Samples.AspNetCore.Blazor.Wasm.csproj", "{73FDCE53-75D1-4DCC-933A-8AB93A0E86EA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.AspNetCore.Blazor.Wasm", "samples\Sentry.Samples.AspNetCore.Blazor.Wasm\Sentry.Samples.AspNetCore.Blazor.Wasm.csproj", "{73FDCE53-75D1-4DCC-933A-8AB93A0E86EA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.Aws.Lambda.AspNetCoreServer", "samples\Sentry.Samples.Aws.Lambda.AspNetCoreServer\Sentry.Samples.Aws.Lambda.AspNetCoreServer.csproj", "{274CEDC2-2129-469D-B269-649EFA2EF5E0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.EntityFramework", "src\Sentry.EntityFramework\Sentry.EntityFramework.csproj", "{8B38F62E-0DD5-486F-96F5-2025AFB9B491}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Aws.Lambda.AspNetCoreServer", "samples\Sentry.Samples.Aws.Lambda.AspNetCoreServer\Sentry.Samples.Aws.Lambda.AspNetCoreServer.csproj", "{274CEDC2-2129-469D-B269-649EFA2EF5E0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.EntityFramework.Tests", "test\Sentry.EntityFramework.Tests\Sentry.EntityFramework.Tests.csproj", "{840B220E-68EC-4ECB-AEA1-67B0151F17FC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.EntityFramework", "src\Sentry.EntityFramework\Sentry.EntityFramework.csproj", "{8B38F62E-0DD5-486F-96F5-2025AFB9B491}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.EntityFramework", "samples\Sentry.Samples.EntityFramework\Sentry.Samples.EntityFramework.csproj", "{8E4BA4C7-413C-4668-8F41-32F484FFB7AA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.EntityFramework.Tests", "test\Sentry.EntityFramework.Tests\Sentry.EntityFramework.Tests.csproj", "{840B220E-68EC-4ECB-AEA1-67B0151F17FC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Samples.Google.Cloud.Functions", "samples\Sentry.Samples.Google.Cloud.Functions\Sentry.Samples.Google.Cloud.Functions.csproj", "{88269A52-A0BA-41B2-8DF3-505B66B17243}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.EntityFramework", "samples\Sentry.Samples.EntityFramework\Sentry.Samples.EntityFramework.csproj", "{8E4BA4C7-413C-4668-8F41-32F484FFB7AA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Google.Cloud.Functions", "src\Sentry.Google.Cloud.Functions\Sentry.Google.Cloud.Functions.csproj", "{D1DB7B31-EC6B-430B-B6B0-2849BAE41AC1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Google.Cloud.Functions", "samples\Sentry.Samples.Google.Cloud.Functions\Sentry.Samples.Google.Cloud.Functions.csproj", "{88269A52-A0BA-41B2-8DF3-505B66B17243}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Google.Cloud.Functions.Tests", "test\Sentry.Google.Cloud.Functions.Tests\Sentry.Google.Cloud.Functions.Tests.csproj", "{066522A4-8380-4D29-8DD0-973B1EDF0B39}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Google.Cloud.Functions", "src\Sentry.Google.Cloud.Functions\Sentry.Google.Cloud.Functions.csproj", "{D1DB7B31-EC6B-430B-B6B0-2849BAE41AC1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Tunnel", "src\Sentry.Tunnel\Sentry.Tunnel.csproj", "{D2F8BF0E-7749-4C92-A4F1-7B96A9878458}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Google.Cloud.Functions.Tests", "test\Sentry.Google.Cloud.Functions.Tests\Sentry.Google.Cloud.Functions.Tests.csproj", "{066522A4-8380-4D29-8DD0-973B1EDF0B39}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentry.Tunnel.Tests", "test\Sentry.Tunnel.Tests\Sentry.Tunnel.Tests.csproj", "{BB54EF08-2FA1-498B-827B-32D905FB0F9F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -289,6 +293,14 @@ Global {066522A4-8380-4D29-8DD0-973B1EDF0B39}.Debug|Any CPU.Build.0 = Debug|Any CPU {066522A4-8380-4D29-8DD0-973B1EDF0B39}.Release|Any CPU.ActiveCfg = Release|Any CPU {066522A4-8380-4D29-8DD0-973B1EDF0B39}.Release|Any CPU.Build.0 = Release|Any CPU + {D2F8BF0E-7749-4C92-A4F1-7B96A9878458}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D2F8BF0E-7749-4C92-A4F1-7B96A9878458}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D2F8BF0E-7749-4C92-A4F1-7B96A9878458}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D2F8BF0E-7749-4C92-A4F1-7B96A9878458}.Release|Any CPU.Build.0 = Release|Any CPU + {BB54EF08-2FA1-498B-827B-32D905FB0F9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BB54EF08-2FA1-498B-827B-32D905FB0F9F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BB54EF08-2FA1-498B-827B-32D905FB0F9F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BB54EF08-2FA1-498B-827B-32D905FB0F9F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -335,6 +347,8 @@ Global {88269A52-A0BA-41B2-8DF3-505B66B17243} = {77454495-55EE-4B40-A089-71B9E8F82E89} {D1DB7B31-EC6B-430B-B6B0-2849BAE41AC1} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB} {066522A4-8380-4D29-8DD0-973B1EDF0B39} = {83263231-1A2A-4733-B759-EEFF14E8C5D5} + {D2F8BF0E-7749-4C92-A4F1-7B96A9878458} = {AF6AF4C7-8AA2-4D59-8064-2D79560904EB} + {BB54EF08-2FA1-498B-827B-32D905FB0F9F} = {83263231-1A2A-4733-B759-EEFF14E8C5D5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0C652B1A-DF72-4EE5-A98B-194FE2C054F6} diff --git a/src/Sentry.Tunnel/Sentry.Tunnel.csproj b/src/Sentry.Tunnel/Sentry.Tunnel.csproj new file mode 100644 index 0000000000..31643f15f4 --- /dev/null +++ b/src/Sentry.Tunnel/Sentry.Tunnel.csproj @@ -0,0 +1,27 @@ + + + + net5.0;netcoreapp3.0;netstandard2.0 + $(PackageTags);AspNetCore;MVC + Sentry.Tunnel + Sentry.Tunnel + Sentry.Tunnel + Official Tunnel middleware for Sentry. + + + + + + + + + + + + + + + + + + diff --git a/src/Sentry.Tunnel/Sentry.Tunnel.csproj.DotSettings b/src/Sentry.Tunnel/Sentry.Tunnel.csproj.DotSettings new file mode 100644 index 0000000000..195c46069e --- /dev/null +++ b/src/Sentry.Tunnel/Sentry.Tunnel.csproj.DotSettings @@ -0,0 +1,2 @@ + + Latest \ No newline at end of file diff --git a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs new file mode 100644 index 0000000000..a5ddfa89d5 --- /dev/null +++ b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace Sentry.Tunnel +{ + public class SentryTunnelMiddleware : IMiddleware + { + private readonly string[] _allowedHosts; + + public SentryTunnelMiddleware(string[] allowedHosts) + { + _allowedHosts = new[] {"sentry.io"}.Concat(allowedHosts).ToArray(); + } + + public async Task InvokeAsync(HttpContext context, RequestDelegate next) + { + if (context.Request.Method == "OPTIONS") + { + context.Response.Headers.Add("Access-Control-Allow-Origin", new[] {(string) context.Request.Headers["Origin"]}); + context.Response.Headers.Add("Access-Control-Allow-Headers", new[] {"Origin, X-Requested-With, Content-Type, Accept"}); + context.Response.Headers.Add("Access-Control-Allow-Methods", new[] {"POST, OPTIONS"}); + context.Response.Headers.Add("Access-Control-Allow-Credentials", new[] {"true"}); + context.Response.StatusCode = 200; + return; + } + + var httpClientFactory = context.RequestServices.GetRequiredService(); + var client = httpClientFactory.CreateClient("SentryTunnel"); + var ms = new MemoryStream(); + await context.Request.Body.CopyToAsync(ms); + ms.Position = 0; + using (var reader = new StreamReader(ms)) + { + var header = await reader.ReadLineAsync(); + if (string.IsNullOrWhiteSpace(header)) + { + context.Response.StatusCode = StatusCodes.Status400BadRequest; + return; + } + + try + { + var headerJson = JsonSerializer.Deserialize>(header); + if (headerJson == null) + { + context.Response.StatusCode = StatusCodes.Status400BadRequest; + await context.Response.WriteAsync("Invalid DSN JSON supplied"); + return; + } + if (headerJson.TryGetValue("dsn", out var dsnString) && Uri.TryCreate(dsnString.ToString(), UriKind.Absolute, out var dsn) && _allowedHosts.Contains(dsn.Host)) + { + var projectId = dsn.AbsolutePath.Trim('/'); + ms.Position = 0; + var responseMessage = await client.PostAsync($"https://{dsn.Host}/api/{projectId}/envelope/", + new StreamContent(ms)); + context.Response.Headers["content-type"] = "application/json"; + context.Response.StatusCode = StatusCodes.Status200OK; + await responseMessage.Content.CopyToAsync(context.Response.Body); + } + } + catch(JsonException) + { + context.Response.StatusCode = StatusCodes.Status400BadRequest; + await context.Response.WriteAsync("Invalid DSN JSON supplied"); + } + catch(ArgumentNullException) + { + context.Response.StatusCode = StatusCodes.Status400BadRequest; + await context.Response.WriteAsync("Received empty body"); + } + } + } + } +} diff --git a/src/Sentry.Tunnel/SentryTunnelingApplicationBuilderExtensions.cs b/src/Sentry.Tunnel/SentryTunnelingApplicationBuilderExtensions.cs new file mode 100644 index 0000000000..d118bb32be --- /dev/null +++ b/src/Sentry.Tunnel/SentryTunnelingApplicationBuilderExtensions.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +namespace Sentry.Tunnel +{ + public static class SentryTunnelingApplicationBuilderExtensions + { + /// + /// Adds and configures the Sentry tunneling middleware + /// + /// + /// The extra hostnames to be allowed for the tunneling. sentry.io is allowed by default; add your own Sentry domain if you use a self-hosted Sentry or Relay. + public static void AddSentryTunneling(this IServiceCollection services, params string[] hostnames) + { + services.AddScoped((s) => new SentryTunnelMiddleware(hostnames)); + } + + public static void UseSentryTunneling(this IApplicationBuilder builder, string path = "/tunnel") + { + builder.Map(path, applicationBuilder => + { + applicationBuilder.UseMiddleware(); + }); + } + } +} \ No newline at end of file diff --git a/test/Sentry.Tunnel.Tests/IntegrationsTests.cs b/test/Sentry.Tunnel.Tests/IntegrationsTests.cs new file mode 100644 index 0000000000..fed1108d8d --- /dev/null +++ b/test/Sentry.Tunnel.Tests/IntegrationsTests.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using NSubstitute; +using Xunit; + +namespace Sentry.Tunnel.Tests +{ + [Collection("SentryTunnelCollection")] + public partial class IntegrationsTests + { + private readonly TestServer _server; + private HttpClient _httpClient; + private MockHttpMessageHandler _httpMessageHander; + + public IntegrationsTests() + { + var builder = new WebHostBuilder() + .ConfigureServices(s => + { + s.AddSentryTunneling("sentry.mywebsite.com"); + _httpMessageHander = new MockHttpMessageHandler("{}", HttpStatusCode.OK); + _httpClient = new HttpClient(_httpMessageHander); + var factory = Substitute.For(); + factory.CreateClient(Arg.Any()).Returns(_httpClient); + s.AddSingleton(factory); + }) + .Configure(app => { app.UseSentryTunneling(); }); + _server = new TestServer(builder); + } + + [Fact] + public async Task TunnelMiddleware_CanForwardValidEnvelope() + { + var requestMessage = new HttpRequestMessage(new HttpMethod("POST"), "/tunnel"); + requestMessage.Content = new StringContent( + @"{""sent_at"":""2021-01-01T00:00:00.000Z"",""sdk"":{""name"":""sentry.javascript.browser"",""version"":""6.8.0""},""dsn"":""https://dns@sentry.io/1""} +{""type"":""session""} +{""sid"":""fda00e933162466c849962eaea0cfaff""}"); + var responseMessage = await _server.CreateClient().SendAsync(requestMessage); + + Assert.Equal(1, _httpMessageHander.NumberOfCalls); + } + + [Fact] + public async Task TunnelMiddleware_DoesNotForwardEnvelopeWithoutDsn() + { + var requestMessage = new HttpRequestMessage(new HttpMethod("POST"), "/tunnel"); + requestMessage.Content = new StringContent(@"{} +{""type"":""session""} +{""sid"":""fda00e933162466c849962eaea0cfaff""}"); + var responseMessage = await _server.CreateClient().SendAsync(requestMessage); + + Assert.Equal(0, _httpMessageHander.NumberOfCalls); + } + + [Fact] + public async Task TunnelMiddleware_DoesNotForwardEnvelopeToArbitraryHost() + { + { + var requestMessage = new HttpRequestMessage(new HttpMethod("POST"), "/tunnel"); + requestMessage.Content = new StringContent( + @"{""sent_at"":""2021-01-01T00:00:00.000Z"",""sdk"":{""name"":""sentry.javascript.browser"",""version"":""6.8.0""},""dsn"":""https://dns@evil.com/1""} +{""type"":""session""} +{""sid"":""fda00e933162466c849962eaea0cfaff""}"); + var responseMessage = await _server.CreateClient().SendAsync(requestMessage); + + Assert.Equal(0, _httpMessageHander.NumberOfCalls); + } + } + + [Fact] + public async Task TunnelMiddleware_CanForwardEnvelopeToWhiteListedHost() + { + { + var requestMessage = new HttpRequestMessage(new HttpMethod("POST"), "/tunnel"); + requestMessage.Content = new StringContent( + @"{""sent_at"":""2021-01-01T00:00:00.000Z"",""sdk"":{""name"":""sentry.javascript.browser"",""version"":""6.8.0""},""dsn"":""https://dns@sentry.mywebsite.com/1""} +{""type"":""session""} +{""sid"":""fda00e933162466c849962eaea0cfaff""}"); + var responseMessage = await _server.CreateClient().SendAsync(requestMessage); + + Assert.Equal(1, _httpMessageHander.NumberOfCalls); + } + } + } +} diff --git a/test/Sentry.Tunnel.Tests/MockHttpMessageHandler.cs b/test/Sentry.Tunnel.Tests/MockHttpMessageHandler.cs new file mode 100644 index 0000000000..1d8c27af1b --- /dev/null +++ b/test/Sentry.Tunnel.Tests/MockHttpMessageHandler.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Sentry.Tunnel.Tests +{ + public class MockHttpMessageHandler : HttpMessageHandler + { + private readonly string _response; + private readonly HttpStatusCode _statusCode; + + public string Input { get; private set; } + public int NumberOfCalls { get; private set; } + + public MockHttpMessageHandler(string response, HttpStatusCode statusCode) + { + _response = response; + _statusCode = statusCode; + } + + protected override async Task SendAsync(HttpRequestMessage request, + CancellationToken cancellationToken) + { + NumberOfCalls++; + if (request.Content != null) // Could be a GET-request without a body + { + Input = await request.Content.ReadAsStringAsync(); + } + return new HttpResponseMessage + { + StatusCode = _statusCode, + Content = new StringContent(_response) + }; + } + } +} diff --git a/test/Sentry.Tunnel.Tests/Sentry.Tunnel.Tests.csproj b/test/Sentry.Tunnel.Tests/Sentry.Tunnel.Tests.csproj new file mode 100644 index 0000000000..4726333b4e --- /dev/null +++ b/test/Sentry.Tunnel.Tests/Sentry.Tunnel.Tests.csproj @@ -0,0 +1,47 @@ + + + + net5.0;netcoreapp3.1;netcoreapp2.1 + false + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 76afcca0b56641d6b6ed212f868011a688cf2aba Mon Sep 17 00:00:00 2001 From: Daniel Rusznyak Date: Mon, 19 Jul 2021 16:59:17 +0100 Subject: [PATCH 02/15] Add Tunnel version user agent --- src/Sentry.Tunnel/SentryTunnelMiddleware.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs index a5ddfa89d5..85a9f1c68c 100644 --- a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs +++ b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs @@ -3,10 +3,13 @@ using System.IO; using System.Linq; using System.Net.Http; +using System.Net.Http.Headers; +using System.Reflection; using System.Text.Json; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Net.Http.Headers; namespace Sentry.Tunnel { @@ -33,6 +36,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) var httpClientFactory = context.RequestServices.GetRequiredService(); var client = httpClientFactory.CreateClient("SentryTunnel"); + client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("Sentry.NET Tunnel", GetType().Assembly.GetCustomAttribute()?.InformationalVersion)); var ms = new MemoryStream(); await context.Request.Body.CopyToAsync(ms); ms.Position = 0; From 88b441390e35451187404acc3fb11b4b61d9720d Mon Sep 17 00:00:00 2001 From: kanadaj Date: Wed, 21 Jul 2021 19:23:43 +0100 Subject: [PATCH 03/15] Update src/Sentry.Tunnel/SentryTunnelMiddleware.cs Co-authored-by: Bruno Garcia --- src/Sentry.Tunnel/SentryTunnelMiddleware.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs index 85a9f1c68c..647740baa0 100644 --- a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs +++ b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs @@ -13,6 +13,10 @@ namespace Sentry.Tunnel { + // + // Middleware that can forward Sentry envelopes. + // + // public class SentryTunnelMiddleware : IMiddleware { private readonly string[] _allowedHosts; From abcdc4dcd2b1b2e249a6bf95a8472564d0f3d4df Mon Sep 17 00:00:00 2001 From: Daniel Rusznyak Date: Wed, 21 Jul 2021 19:40:43 +0100 Subject: [PATCH 04/15] Remove .NET testing stuff, cache the assembly version --- src/Sentry.Tunnel/Sentry.Tunnel.csproj.DotSettings | 2 -- src/Sentry.Tunnel/SentryTunnelMiddleware.cs | 4 +++- test/Sentry.Tunnel.Tests/Sentry.Tunnel.Tests.csproj | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 src/Sentry.Tunnel/Sentry.Tunnel.csproj.DotSettings diff --git a/src/Sentry.Tunnel/Sentry.Tunnel.csproj.DotSettings b/src/Sentry.Tunnel/Sentry.Tunnel.csproj.DotSettings deleted file mode 100644 index 195c46069e..0000000000 --- a/src/Sentry.Tunnel/Sentry.Tunnel.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - Latest \ No newline at end of file diff --git a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs index 85a9f1c68c..b9daeb7008 100644 --- a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs +++ b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs @@ -16,6 +16,8 @@ namespace Sentry.Tunnel public class SentryTunnelMiddleware : IMiddleware { private readonly string[] _allowedHosts; + private string? _version; + private string Version => _version ??= (GetType().Assembly.GetCustomAttribute()?.InformationalVersion ?? string.Empty); public SentryTunnelMiddleware(string[] allowedHosts) { @@ -36,7 +38,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) var httpClientFactory = context.RequestServices.GetRequiredService(); var client = httpClientFactory.CreateClient("SentryTunnel"); - client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("Sentry.NET Tunnel", GetType().Assembly.GetCustomAttribute()?.InformationalVersion)); + client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("Sentry.NET Tunnel", Version)); var ms = new MemoryStream(); await context.Request.Body.CopyToAsync(ms); ms.Position = 0; diff --git a/test/Sentry.Tunnel.Tests/Sentry.Tunnel.Tests.csproj b/test/Sentry.Tunnel.Tests/Sentry.Tunnel.Tests.csproj index 4726333b4e..a7ead4854f 100644 --- a/test/Sentry.Tunnel.Tests/Sentry.Tunnel.Tests.csproj +++ b/test/Sentry.Tunnel.Tests/Sentry.Tunnel.Tests.csproj @@ -13,7 +13,7 @@ - + @@ -28,7 +28,7 @@ - + From 743f4d5c0a7cf95c8dd3832a55c3ee8facbd199d Mon Sep 17 00:00:00 2001 From: Daniel Rusznyak Date: Wed, 21 Jul 2021 19:41:34 +0100 Subject: [PATCH 05/15] Add trailing newline --- .../SentryTunnelingApplicationBuilderExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Sentry.Tunnel/SentryTunnelingApplicationBuilderExtensions.cs b/src/Sentry.Tunnel/SentryTunnelingApplicationBuilderExtensions.cs index d118bb32be..09c508a9e7 100644 --- a/src/Sentry.Tunnel/SentryTunnelingApplicationBuilderExtensions.cs +++ b/src/Sentry.Tunnel/SentryTunnelingApplicationBuilderExtensions.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; namespace Sentry.Tunnel @@ -23,4 +23,4 @@ public static void UseSentryTunneling(this IApplicationBuilder builder, string p }); } } -} \ No newline at end of file +} From 308b634a29d7fabe5f9cee4b7c90237e731a5bc8 Mon Sep 17 00:00:00 2001 From: kanadaj Date: Fri, 23 Jul 2021 21:53:15 +0100 Subject: [PATCH 06/15] Update src/Sentry.Tunnel/SentryTunnelMiddleware.cs Co-authored-by: LucasZF --- src/Sentry.Tunnel/SentryTunnelMiddleware.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs index e079f8e3d4..ce4876d0bb 100644 --- a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs +++ b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs @@ -42,7 +42,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) var httpClientFactory = context.RequestServices.GetRequiredService(); var client = httpClientFactory.CreateClient("SentryTunnel"); - client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("Sentry.NET Tunnel", Version)); + client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("Sentry.NET_Tunnel", Version)); var ms = new MemoryStream(); await context.Request.Body.CopyToAsync(ms); ms.Position = 0; From 4cdc48750b1aacc6ca8e31cc8f8a7a9eec37be68 Mon Sep 17 00:00:00 2001 From: kanadaj Date: Fri, 23 Jul 2021 21:53:32 +0100 Subject: [PATCH 07/15] Update src/Sentry.Tunnel/SentryTunnelMiddleware.cs Co-authored-by: LucasZF --- src/Sentry.Tunnel/SentryTunnelMiddleware.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs index ce4876d0bb..8c74efe568 100644 --- a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs +++ b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs @@ -61,7 +61,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) if (headerJson == null) { context.Response.StatusCode = StatusCodes.Status400BadRequest; - await context.Response.WriteAsync("Invalid DSN JSON supplied"); + await context.Response.WriteAsync("Invalid DSN JSON supplied").ConfigureAwait(false); return; } if (headerJson.TryGetValue("dsn", out var dsnString) && Uri.TryCreate(dsnString.ToString(), UriKind.Absolute, out var dsn) && _allowedHosts.Contains(dsn.Host)) From 5198f518b1521dd4a95b6ab9ceab7a31331c92bb Mon Sep 17 00:00:00 2001 From: kanadaj Date: Fri, 23 Jul 2021 21:53:37 +0100 Subject: [PATCH 08/15] Update src/Sentry.Tunnel/SentryTunnelMiddleware.cs Co-authored-by: LucasZF --- src/Sentry.Tunnel/SentryTunnelMiddleware.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs index 8c74efe568..cde0ac8ac4 100644 --- a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs +++ b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs @@ -69,7 +69,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) var projectId = dsn.AbsolutePath.Trim('/'); ms.Position = 0; var responseMessage = await client.PostAsync($"https://{dsn.Host}/api/{projectId}/envelope/", - new StreamContent(ms)); + new StreamContent(ms)).ConfigureAwait(false); context.Response.Headers["content-type"] = "application/json"; context.Response.StatusCode = StatusCodes.Status200OK; await responseMessage.Content.CopyToAsync(context.Response.Body); From 24d2db35c301fac4dbb8c2d8bb113335d4162d35 Mon Sep 17 00:00:00 2001 From: kanadaj Date: Fri, 23 Jul 2021 21:53:43 +0100 Subject: [PATCH 09/15] Update src/Sentry.Tunnel/SentryTunnelMiddleware.cs Co-authored-by: LucasZF --- src/Sentry.Tunnel/SentryTunnelMiddleware.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs index cde0ac8ac4..aff77c7318 100644 --- a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs +++ b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs @@ -48,7 +48,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) ms.Position = 0; using (var reader = new StreamReader(ms)) { - var header = await reader.ReadLineAsync(); + var header = await reader.ReadLineAsync().ConfigureAwait(false); if (string.IsNullOrWhiteSpace(header)) { context.Response.StatusCode = StatusCodes.Status400BadRequest; From 9703fea3b484bf7a85cc869c06ca2548c6751c67 Mon Sep 17 00:00:00 2001 From: kanadaj Date: Fri, 23 Jul 2021 21:53:51 +0100 Subject: [PATCH 10/15] Update src/Sentry.Tunnel/SentryTunnelMiddleware.cs Co-authored-by: LucasZF --- src/Sentry.Tunnel/SentryTunnelMiddleware.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs index aff77c7318..238a4311ba 100644 --- a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs +++ b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs @@ -72,7 +72,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) new StreamContent(ms)).ConfigureAwait(false); context.Response.Headers["content-type"] = "application/json"; context.Response.StatusCode = StatusCodes.Status200OK; - await responseMessage.Content.CopyToAsync(context.Response.Body); + await responseMessage.Content.CopyToAsync(context.Response.Body).ConfigureAwait(false); } } catch(JsonException) From ddabb3e243598e650cc9477a7eef313400eb31e9 Mon Sep 17 00:00:00 2001 From: kanadaj Date: Fri, 23 Jul 2021 21:54:02 +0100 Subject: [PATCH 11/15] Update src/Sentry.Tunnel/SentryTunnelMiddleware.cs Co-authored-by: LucasZF --- src/Sentry.Tunnel/SentryTunnelMiddleware.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs index 238a4311ba..a15682b04d 100644 --- a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs +++ b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs @@ -78,7 +78,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) catch(JsonException) { context.Response.StatusCode = StatusCodes.Status400BadRequest; - await context.Response.WriteAsync("Invalid DSN JSON supplied"); + await context.Response.WriteAsync("Invalid DSN JSON supplied").ConfigureAwait(false); } catch(ArgumentNullException) { From 81b2a5f16c673e209df5014919b80b995759e3cc Mon Sep 17 00:00:00 2001 From: kanadaj Date: Fri, 23 Jul 2021 21:54:08 +0100 Subject: [PATCH 12/15] Update src/Sentry.Tunnel/SentryTunnelMiddleware.cs Co-authored-by: LucasZF --- src/Sentry.Tunnel/SentryTunnelMiddleware.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs index a15682b04d..2f4ef1fcfd 100644 --- a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs +++ b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs @@ -83,7 +83,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) catch(ArgumentNullException) { context.Response.StatusCode = StatusCodes.Status400BadRequest; - await context.Response.WriteAsync("Received empty body"); + await context.Response.WriteAsync("Received empty body").ConfigureAwait(false); } } } From 9ec2346e8f6cecb7ea78c43876a6f3e0804538d3 Mon Sep 17 00:00:00 2001 From: kanadaj Date: Fri, 23 Jul 2021 21:54:18 +0100 Subject: [PATCH 13/15] Update src/Sentry.Tunnel/SentryTunnelMiddleware.cs Co-authored-by: LucasZF --- src/Sentry.Tunnel/SentryTunnelMiddleware.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs index 2f4ef1fcfd..9c84944d1e 100644 --- a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs +++ b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs @@ -13,10 +13,10 @@ namespace Sentry.Tunnel { - // - // Middleware that can forward Sentry envelopes. - // - // + /// + /// Middleware that can forward Sentry envelopes. + /// + /// public class SentryTunnelMiddleware : IMiddleware { private readonly string[] _allowedHosts; From f21d2115bbdb1ba17cdcf3540b1451439766a55e Mon Sep 17 00:00:00 2001 From: kanadaj Date: Fri, 23 Jul 2021 21:54:31 +0100 Subject: [PATCH 14/15] Update src/Sentry.Tunnel/SentryTunnelMiddleware.cs Co-authored-by: LucasZF --- src/Sentry.Tunnel/SentryTunnelMiddleware.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs index 9c84944d1e..2e408d1163 100644 --- a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs +++ b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs @@ -28,6 +28,7 @@ public SentryTunnelMiddleware(string[] allowedHosts) _allowedHosts = new[] {"sentry.io"}.Concat(allowedHosts).ToArray(); } + /// public async Task InvokeAsync(HttpContext context, RequestDelegate next) { if (context.Request.Method == "OPTIONS") From bdbdb58165794d8fe26db404040cdb82a0e7c8ac Mon Sep 17 00:00:00 2001 From: kanadaj Date: Fri, 23 Jul 2021 21:54:40 +0100 Subject: [PATCH 15/15] Update src/Sentry.Tunnel/SentryTunnelMiddleware.cs Co-authored-by: LucasZF --- src/Sentry.Tunnel/SentryTunnelMiddleware.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs index 2e408d1163..de3f7c42dc 100644 --- a/src/Sentry.Tunnel/SentryTunnelMiddleware.cs +++ b/src/Sentry.Tunnel/SentryTunnelMiddleware.cs @@ -23,6 +23,10 @@ public class SentryTunnelMiddleware : IMiddleware private string? _version; private string Version => _version ??= (GetType().Assembly.GetCustomAttribute()?.InformationalVersion ?? string.Empty); + /// + /// Middleware that can forward Sentry envelopes. + /// + /// public SentryTunnelMiddleware(string[] allowedHosts) { _allowedHosts = new[] {"sentry.io"}.Concat(allowedHosts).ToArray();