7
7
using System . Threading . Tasks ;
8
8
using Microsoft . AspNetCore . Http ;
9
9
using NewRelic . Agent . Api ;
10
+ using NewRelic . Agent . Extensions . Logging ;
10
11
11
12
namespace NewRelic . Providers . Wrapper . AspNetCore6Plus
12
13
{
@@ -29,11 +30,16 @@ public BrowserInjectingStreamWrapper(IAgent agent, Stream baseStream, HttpContex
29
30
CanWrite = true ;
30
31
}
31
32
33
+ /// <summary>
34
+ /// Flag gets set to true if we've captured an exception and need to disable browser injection
35
+ /// </summary>
36
+ public static bool Disabled { get ; set ; }
37
+
32
38
public override Task FlushAsync ( CancellationToken cancellationToken )
33
39
{
34
- if ( ! _isContentLengthSet && IsHtmlResponse ( ) )
40
+ if ( ! Disabled && ! _isContentLengthSet && IsHtmlResponse ( ) )
35
41
{
36
- _context . Response . Headers . ContentLength = null ;
42
+ _context . Response . ContentLength = null ;
37
43
_isContentLengthSet = true ;
38
44
}
39
45
@@ -50,7 +56,8 @@ public override void SetLength(long value)
50
56
{
51
57
_baseStream . SetLength ( value ) ;
52
58
53
- IsHtmlResponse ( forceReCheck : true ) ;
59
+ if ( ! Disabled )
60
+ IsHtmlResponse ( forceReCheck : true ) ;
54
61
}
55
62
56
63
public override void Write ( ReadOnlySpan < byte > buffer ) => _baseStream . Write ( buffer ) ;
@@ -62,13 +69,19 @@ public override void Write(byte[] buffer, int offset, int count)
62
69
{
63
70
// pass through without modification if we're already in the middle of injecting
64
71
// don't inject if the response isn't an HTML response
65
- if ( ! CurrentlyInjecting ( ) && IsHtmlResponse ( ) )
72
+ if ( ! Disabled && ! CurrentlyInjecting ( ) && IsHtmlResponse ( ) )
66
73
{
67
- // Set a flag on the context to indicate we're in the middle of injecting - prevents multiple recursions when response compression is in use
68
- StartInjecting ( ) ;
69
- _agent . TryInjectBrowserScriptAsync ( _context . Response . ContentType , _context . Request . Path . Value , buffer , _baseStream )
70
- . GetAwaiter ( ) . GetResult ( ) ;
71
- FinishInjecting ( ) ;
74
+ try
75
+ {
76
+ // Set a flag on the context to indicate we're in the middle of injecting - prevents multiple recursions when response compression is in use
77
+ StartInjecting ( ) ;
78
+ _agent . TryInjectBrowserScriptAsync ( _context . Response . ContentType , _context . Request . Path . Value , buffer , _baseStream )
79
+ . GetAwaiter ( ) . GetResult ( ) ;
80
+ }
81
+ finally
82
+ {
83
+ FinishInjecting ( ) ;
84
+ }
72
85
73
86
return ;
74
87
}
@@ -82,12 +95,18 @@ public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, Cancella
82
95
{
83
96
// pass through without modification if we're already in the middle of injecting
84
97
// don't inject if the response isn't an HTML response
85
- if ( ! CurrentlyInjecting ( ) && IsHtmlResponse ( ) )
98
+ if ( ! Disabled && ! CurrentlyInjecting ( ) && IsHtmlResponse ( ) )
86
99
{
87
- // Set a flag on the context to indicate we're in the middle of injecting - prevents multiple recursions when response compression is in use
88
- StartInjecting ( ) ;
89
- await _agent . TryInjectBrowserScriptAsync ( _context . Response . ContentType , _context . Request . Path . Value , buffer . ToArray ( ) , _baseStream ) ;
90
- FinishInjecting ( ) ;
100
+ try
101
+ {
102
+ // Set a flag on the context to indicate we're in the middle of injecting - prevents multiple recursions when response compression is in use
103
+ StartInjecting ( ) ;
104
+ await _agent . TryInjectBrowserScriptAsync ( _context . Response . ContentType , _context . Request . Path . Value , buffer . ToArray ( ) , _baseStream ) ;
105
+ }
106
+ finally
107
+ {
108
+ FinishInjecting ( ) ;
109
+ }
91
110
92
111
return ;
93
112
}
@@ -98,9 +117,9 @@ public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, Cancella
98
117
99
118
private const string InjectingRUM = "InjectingRUM" ;
100
119
101
- private void FinishInjecting ( ) => _context . Items . Remove ( InjectingRUM ) ;
102
- private void StartInjecting ( ) => _context . Items . Add ( InjectingRUM , null ) ;
103
- private bool CurrentlyInjecting ( ) => _context . Items . ContainsKey ( InjectingRUM ) ;
120
+ private void FinishInjecting ( ) => _context ? . Items . Remove ( InjectingRUM ) ;
121
+ private void StartInjecting ( ) => _context ? . Items . Add ( InjectingRUM , null ) ;
122
+ private bool CurrentlyInjecting ( ) => _context ? . Items . ContainsKey ( InjectingRUM ) ?? false ;
104
123
105
124
public override async ValueTask DisposeAsync ( )
106
125
{
@@ -120,41 +139,55 @@ public override async ValueTask DisposeAsync()
120
139
121
140
private bool IsHtmlResponse ( bool forceReCheck = false )
122
141
{
123
- if ( ! forceReCheck && _isHtmlResponse != null )
124
- return _isHtmlResponse . Value ;
125
-
126
- // we need to check if the active request is still valid
127
- // this can fail if we're in the middle of an error response
128
- // or url rewrite in which case we can't intercept
129
- if ( _context ? . Response == null )
130
- return false ;
131
-
132
- // Requirements for script injection:
133
- // * text/html response
134
- // * UTF-8 formatted (either explicitly or no charset defined)
135
-
136
- _isHtmlResponse =
137
- _context . Response . ContentType . Contains ( "text/html" , StringComparison . OrdinalIgnoreCase ) &&
138
- ( _context . Response . ContentType . Contains ( "utf-8" , StringComparison . OrdinalIgnoreCase ) ||
139
- ! _context . Response . ContentType . Contains ( "charset=" , StringComparison . OrdinalIgnoreCase ) ) ;
140
-
141
- if ( ! _isHtmlResponse . Value )
142
+ try
142
143
{
143
- _agent . CurrentTransaction ? . LogFinest ( $ "Skipping RUM injection: Not an HTML response. ContentType is { _context . Response . ContentType } ") ;
144
- return false ;
144
+ if ( ! forceReCheck && _isHtmlResponse != null )
145
+ return _isHtmlResponse . Value ;
146
+
147
+ // we need to check if the active request is still valid
148
+ // this can fail if we're in the middle of an error response
149
+ // or url rewrite in which case we can't intercept
150
+ if ( _context ? . Response == null )
151
+ return false ;
152
+
153
+ // Requirements for script injection:
154
+ // * text/html response
155
+ // * UTF-8 formatted (either explicitly or no charset defined)
156
+ _isHtmlResponse =
157
+ _context . Response . ContentType != null &&
158
+ _context . Response . ContentType . Contains ( "text/html" , StringComparison . OrdinalIgnoreCase ) &&
159
+ ( _context . Response . ContentType . Contains ( "utf-8" , StringComparison . OrdinalIgnoreCase ) ||
160
+ ! _context . Response . ContentType . Contains ( "charset=" , StringComparison . OrdinalIgnoreCase ) ) ;
161
+
162
+ if ( ! _isHtmlResponse . Value )
163
+ {
164
+ _agent . CurrentTransaction ? . LogFinest ( $ "Skipping RUM injection: Not an HTML response. ContentType is { _context . Response . ContentType } ") ;
165
+ return false ;
166
+ }
167
+
168
+ // Make sure we force dynamic content type since we're
169
+ // rewriting the content - static content will set the header explicitly
170
+ // and fail when it doesn't match if (_isHtmlResponse.Value)
171
+ if ( ! _isContentLengthSet && _context . Response . ContentLength != null )
172
+ {
173
+ _context . Response . ContentLength = null ;
174
+ _isContentLengthSet = true ;
175
+ }
145
176
}
146
-
147
- // Make sure we force dynamic content type since we're
148
- // rewriting the content - static content will set the header explicitly
149
- // and fail when it doesn't match if (_isHtmlResponse.Value)
150
- if ( ! _isContentLengthSet && _context . Response . ContentLength != null )
177
+ catch ( Exception e )
151
178
{
152
- _context . Response . Headers . ContentLength = null ;
153
- _isContentLengthSet = true ;
179
+ LogExceptionAndDisable ( e ) ;
154
180
}
155
181
156
- return _isHtmlResponse . Value ;
182
+ return _isHtmlResponse ?? false ;
157
183
}
158
184
185
+ private void LogExceptionAndDisable ( Exception e )
186
+ {
187
+ _agent . Logger . Log ( Level . Error ,
188
+ $ "Unexpected exception. Browser injection will be disabled. Exception: { e . Message } : { e . StackTrace } ") ;
189
+
190
+ Disabled = true ;
191
+ }
159
192
}
160
193
}
0 commit comments