Skip to content

Commit e571ac1

Browse files
authored
summary: ASP.NET Core 6+ Browser Injection Bug Fixes
fix: Bypass gRPC requests in the browser injection middleware. Also added a configuration setting to disable ASP.NET Core 6+ browser injection; the setting enables browser injection by default but can be overridden if necessary.
1 parent 322a00e commit e571ac1

File tree

11 files changed

+99
-20
lines changed

11 files changed

+99
-20
lines changed

src/Agent/NewRelic/Agent/Core/Configuration/DefaultConfiguration.cs

+11-1
Original file line numberDiff line numberDiff line change
@@ -2085,7 +2085,6 @@ private void UpdateDiagnosticsAgentTimingSettings()
20852085
_diagnosticsCaptureAgentTimingFrequency = configFreq;
20862086
}
20872087

2088-
20892088
private bool? _forceSynchronousTimingCalculationHttpClient;
20902089
public bool ForceSynchronousTimingCalculationHttpClient
20912090
{
@@ -2097,6 +2096,17 @@ public bool ForceSynchronousTimingCalculationHttpClient
20972096
}
20982097
}
20992098

2099+
private bool? _enableAspNetCore6PlusBrowserInjection;
2100+
public bool EnableAspNetCore6PlusBrowserInjection
2101+
{
2102+
get
2103+
{
2104+
return _enableAspNetCore6PlusBrowserInjection.HasValue
2105+
? _enableAspNetCore6PlusBrowserInjection.Value
2106+
: (_enableAspNetCore6PlusBrowserInjection = TryGetAppSettingAsBoolWithDefault("EnableAspNetCore6PlusBrowserInjection", true)).Value;
2107+
}
2108+
}
2109+
21002110
private TimeSpan? _metricsHarvestCycleOverride = null;
21012111
public TimeSpan MetricsHarvestCycle
21022112
{

src/Agent/NewRelic/Agent/Core/Configuration/ReportedConfiguration.cs

+3
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,9 @@ public ReportedConfiguration(IConfiguration configuration)
575575
[JsonProperty("agent.force_synchronous_timing_calculation_for_http_client")]
576576
public bool ForceSynchronousTimingCalculationHttpClient => _configuration.ForceSynchronousTimingCalculationHttpClient;
577577

578+
[JsonProperty("agent.enable_asp_net_core_6plus_browser_injection")]
579+
public bool EnableAspNetCore6PlusBrowserInjection => _configuration.EnableAspNetCore6PlusBrowserInjection;
580+
578581
[JsonProperty("agent.exclude_new_relic_header")]
579582
public bool ExcludeNewrelicHeader => _configuration.ExcludeNewrelicHeader;
580583

src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Configuration/IConfiguration.cs

+1
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ public interface IConfiguration
182182
string ProcessHostDisplayName { get; }
183183
int DatabaseStatementCacheCapacity { get; }
184184
bool ForceSynchronousTimingCalculationHttpClient { get; }
185+
bool EnableAspNetCore6PlusBrowserInjection { get; }
185186
bool ExcludeNewrelicHeader { get; }
186187
bool ApplicationLoggingEnabled { get; }
187188
bool LogMetricsCollectorEnabled { get; }

src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AspNetCore6Plus/AddNewRelicStartupFilter.cs

+6-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@ public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
2929
return builder =>
3030
{
3131
builder.UseMiddleware<WrapPipelineMiddleware>(_agent);
32-
builder.UseMiddleware<BrowserInjectionMiddleware>(_agent);
32+
33+
// only inject the middleware if browser injection is enabled and the request is not a gRPC request.
34+
builder.UseWhen(
35+
context => _agent.Configuration.BrowserMonitoringAutoInstrument && _agent.Configuration.EnableAspNetCore6PlusBrowserInjection && context.Request.ContentType?.ToLower() != "application/grpc",
36+
b => b.UseMiddleware<BrowserInjectionMiddleware>(_agent));
37+
3338
next(builder);
3439
};
3540
}

src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AspNetCore6Plus/ResponseCompressionBodyOnWriteWrapper.cs

+14-7
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,23 @@ public AfterWrappedMethodDelegate BeforeWrappedMethod(InstrumentedMethodCall ins
4242
{
4343
return Delegates.GetDelegateFor(onSuccess: () =>
4444
{
45-
// Wrap _compressionStream and replace the current value with our wrapped version
46-
// check whether we've already wrapped the stream so we don't do it twice
47-
var currentCompressionStream = _compressionStreamFieldGetter.Invoke(instrumentedMethodCall.MethodCall.InvocationTarget);
48-
if (currentCompressionStream != null && currentCompressionStream.GetType() != typeof(BrowserInjectingStreamWrapper))
45+
var context = _contextFieldGetter.Invoke(instrumentedMethodCall.MethodCall.InvocationTarget);
46+
47+
// only wrap the compression stream if browser injection is enabled and the request is not a gRPC request.
48+
if (agent.Configuration.BrowserMonitoringAutoInstrument && agent.Configuration.EnableAspNetCore6PlusBrowserInjection && context.Request.ContentType?.ToLower() != "application/grpc")
4949
{
50-
var context = _contextFieldGetter.Invoke(instrumentedMethodCall.MethodCall.InvocationTarget);
50+
// Wrap _compressionStream and replace the current value with our wrapped version
51+
// check whether we've already wrapped the stream so we don't do it twice
52+
var currentCompressionStream = _compressionStreamFieldGetter.Invoke(instrumentedMethodCall.MethodCall.InvocationTarget);
53+
54+
if (currentCompressionStream != null && currentCompressionStream.GetType() != typeof(BrowserInjectingStreamWrapper))
55+
{
5156

52-
var responseWrapper = new BrowserInjectingStreamWrapper(agent, currentCompressionStream, context);
57+
var responseWrapper = new BrowserInjectingStreamWrapper(agent, currentCompressionStream, context);
5358

54-
_compressionStreamFieldSetter.Invoke(instrumentedMethodCall.MethodCall.InvocationTarget, responseWrapper);
59+
_compressionStreamFieldSetter.Invoke(instrumentedMethodCall.MethodCall.InvocationTarget,
60+
responseWrapper);
61+
}
5562
}
5663
});
5764
}

tests/Agent/IntegrationTests/IntegrationTestHelpers/NewRelicConfigModifier.cs

+9
Original file line numberDiff line numberDiff line change
@@ -405,5 +405,14 @@ public NewRelicConfigModifier ConfigureFasterUpdateLoadedModulesCycle(int second
405405
CommonUtils.SetConfigAppSetting(_configFilePath, "OverrideUpdateLoadedModulesCycle", seconds.ToString(), "urn:newrelic-config");
406406
return this;
407407
}
408+
409+
public NewRelicConfigModifier EnableAspNetCore6PlusBrowserInjection(bool enableBrowserInjection)
410+
{
411+
CommonUtils.ModifyOrCreateXmlNodeInNewRelicConfig(_configFilePath, new[] { "configuration" }, "appSettings", string.Empty);
412+
CommonUtils.ModifyOrCreateXmlNodeInNewRelicConfig(_configFilePath, new[] { "configuration", "appSettings" }, "add", string.Empty);
413+
CommonUtils.ModifyOrCreateXmlAttributeInNewRelicConfig(_configFilePath, new[] { "configuration", "appSettings", "add"}, "key", "EnableAspNetCore6PlusBrowserInjection");
414+
CommonUtils.ModifyOrCreateXmlAttributeInNewRelicConfig(_configFilePath, new[] { "configuration", "appSettings", "add"}, "value", $"{enableBrowserInjection}");
415+
return this;
416+
}
408417
}
409418
}

tests/Agent/IntegrationTests/IntegrationTests/AgentFeatures/BrowserAgentAutoInjection6Plus.cs

+38-8
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@ public abstract class BrowserAgentAutoInjection6PlusBase : NewRelicIntegrationTe
1515
private string _htmlContent;
1616
private string _staticContent;
1717
private string _fooContent;
18+
private bool _browserInjectionEnabled;
1819

1920
protected BrowserAgentAutoInjection6PlusBase(BasicAspNetCoreRazorApplicationFixture fixture,
20-
ITestOutputHelper output, bool enableResponseCompression, string loaderType = "rum")
21+
ITestOutputHelper output, bool enableBrowserInjection, bool enableResponseCompression, string loaderType = "rum")
2122
: base(fixture)
2223
{
24+
_browserInjectionEnabled = enableBrowserInjection;
25+
2326
_fixture = fixture;
2427
_fixture.TestLogger = output;
2528
_fixture.Actions
@@ -31,6 +34,7 @@ protected BrowserAgentAutoInjection6PlusBase(BasicAspNetCoreRazorApplicationFixt
3134
var configModifier = new NewRelicConfigModifier(configPath);
3235
configModifier.AutoInstrumentBrowserMonitoring(true);
3336
configModifier.BrowserMonitoringEnableAttributes(true);
37+
configModifier.EnableAspNetCore6PlusBrowserInjection(enableBrowserInjection);
3438

3539
configModifier.BrowserMonitoringLoader(loaderType);
3640
},
@@ -65,9 +69,18 @@ public void Test()
6569
var jsAgentFromStaticContent = JavaScriptAgent.GetJavaScriptAgentScriptFromSource(_staticContent);
6670
var jsAgentFromFooContent = JavaScriptAgent.GetJavaScriptAgentScriptFromSource(_fooContent);
6771

68-
Assert.Equal(jsAgentFromConnectResponse, jsAgentFromHtmlContent);
69-
Assert.Equal(jsAgentFromConnectResponse, jsAgentFromStaticContent);
70-
Assert.Null(jsAgentFromFooContent);
72+
if (_browserInjectionEnabled)
73+
{
74+
Assert.Equal(jsAgentFromConnectResponse, jsAgentFromHtmlContent);
75+
Assert.Equal(jsAgentFromConnectResponse, jsAgentFromStaticContent);
76+
Assert.Null(jsAgentFromFooContent);
77+
}
78+
else
79+
{
80+
Assert.Null(jsAgentFromHtmlContent);
81+
Assert.Null(jsAgentFromStaticContent);
82+
Assert.Null(jsAgentFromFooContent);
83+
}
7184

7285
// verify that the browser injecting stream wrapper didn't catch an exception and disable itself
7386
var agentDisabledLogLine = _fixture.AgentLog.TryGetLogLine(AgentLogBase.ErrorLogLinePrefixRegex + "Unexpected exception. Browser injection will be disabled. *?");
@@ -79,7 +92,7 @@ public void Test()
7992
public class BrowserAgentAutoInjection6PlusRumUnCompressed : BrowserAgentAutoInjection6PlusBase
8093
{
8194
public BrowserAgentAutoInjection6PlusRumUnCompressed(BasicAspNetCoreRazorApplicationFixture fixture, ITestOutputHelper output)
82-
: base(fixture, output, false)
95+
: base(fixture, output, true, false)
8396
{
8497
}
8598
}
@@ -88,17 +101,34 @@ public BrowserAgentAutoInjection6PlusRumUnCompressed(BasicAspNetCoreRazorApplica
88101
public class BrowserAgentAutoInjection6PlusRumCompressed : BrowserAgentAutoInjection6PlusBase
89102
{
90103
public BrowserAgentAutoInjection6PlusRumCompressed(BasicAspNetCoreRazorApplicationFixture fixture, ITestOutputHelper output)
91-
: base(fixture, output, true)
104+
: base(fixture, output, true, true)
105+
{
106+
}
107+
}
108+
109+
[NetCoreTest]
110+
public class BrowserAgentAutoInjection6PlusInjectionDisabledRumUnCompressed : BrowserAgentAutoInjection6PlusBase
111+
{
112+
public BrowserAgentAutoInjection6PlusInjectionDisabledRumUnCompressed(BasicAspNetCoreRazorApplicationFixture fixture, ITestOutputHelper output)
113+
: base(fixture, output, true, false)
92114
{
93115
}
94116
}
95117

118+
[NetCoreTest]
119+
public class BrowserAgentAutoInjection6PlusInjectionDisabledRumCompressed : BrowserAgentAutoInjection6PlusBase
120+
{
121+
public BrowserAgentAutoInjection6PlusInjectionDisabledRumCompressed(BasicAspNetCoreRazorApplicationFixture fixture, ITestOutputHelper output)
122+
: base(fixture, output, false, true)
123+
{
124+
}
125+
}
96126

97127
[NetCoreTest]
98128
public class BrowserAgentAutoInjection6PlusSpa : BrowserAgentAutoInjection6PlusBase
99129
{
100130
public BrowserAgentAutoInjection6PlusSpa(BasicAspNetCoreRazorApplicationFixture fixture, ITestOutputHelper output)
101-
: base(fixture, output, true, "spa")
131+
: base(fixture, output, true, true, "spa")
102132
{
103133
}
104134
}
@@ -107,7 +137,7 @@ public BrowserAgentAutoInjection6PlusSpa(BasicAspNetCoreRazorApplicationFixture
107137
public class BrowserAgentAutoInjection6PlusFull : BrowserAgentAutoInjection6PlusBase
108138
{
109139
public BrowserAgentAutoInjection6PlusFull(BasicAspNetCoreRazorApplicationFixture fixture, ITestOutputHelper output)
110-
: base(fixture, output, true, "full")
140+
: base(fixture, output, true, true, "full")
111141
{
112142
}
113143
}

tests/Agent/UnitTests/Core.UnitTest/Configuration/DefaultConfigurationTests.cs

+12
Original file line numberDiff line numberDiff line change
@@ -3241,6 +3241,18 @@ public bool AsyncHttpClientSegmentsDoNotCountTowardsParentExclusiveTimeTests(str
32413241
return defaultConfig.ForceSynchronousTimingCalculationHttpClient;
32423242
}
32433243

3244+
[TestCase(null, ExpectedResult = true)]
3245+
[TestCase("not a bool", ExpectedResult = true)]
3246+
[TestCase("false", ExpectedResult = false)]
3247+
[TestCase("true", ExpectedResult = true)]
3248+
public bool AspNetCore6PlusBrowserInjectionTests(string localConfigValue)
3249+
{
3250+
_localConfig.appSettings.Add(new configurationAdd { key = "EnableAspNetCore6PlusBrowserInjection", value = localConfigValue });
3251+
var defaultConfig = new TestableDefaultConfiguration(_environment, _localConfig, _serverConfig, _runTimeConfig, _securityPoliciesConfiguration, _processStatic, _httpRuntimeStatic, _configurationManagerStatic, _dnsStatic);
3252+
3253+
return defaultConfig.EnableAspNetCore6PlusBrowserInjection;
3254+
}
3255+
32443256
[TestCase("true", true, ExpectedResult = true)]
32453257
[TestCase("true", false, ExpectedResult = true)]
32463258
[TestCase("true", null, ExpectedResult = true)]

0 commit comments

Comments
 (0)