Skip to content

Commit c1193d9

Browse files
committed
fix(request-info): avoid reading handled post data
1 parent 468ba0f commit c1193d9

File tree

12 files changed

+98
-36
lines changed

12 files changed

+98
-36
lines changed

samples/Exceptionless.SampleAspNetCore/Controllers/ValuesController.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,16 @@ public Dictionary<string, string> Get() {
2626

2727
try {
2828
throw new Exception($"Handled Exception: {Guid.NewGuid()}");
29-
} catch (Exception handledException) {
29+
}
30+
catch (Exception handledException) {
3031
// Use the ToExceptionless extension method to submit this handled exception to Exceptionless using the client instance from DI.
3132
handledException.ToExceptionless(_exceptionlessClient).Submit();
3233
}
3334

3435
try {
3536
throw new Exception($"Handled Exception (Default Client): {Guid.NewGuid()}");
36-
} catch (Exception handledException) {
37+
}
38+
catch (Exception handledException) {
3739
// Use the ToExceptionless extension method to submit this handled exception to Exceptionless using the default client instance (ExceptionlessClient.Default).
3840
// This works and is convenient, but its generally not recommended to use static singleton instances because it makes testing and
3941
// other things harder.
@@ -44,4 +46,4 @@ public Dictionary<string, string> Get() {
4446
throw new Exception($"Unhandled Exception: {Guid.NewGuid()}");
4547
}
4648
}
47-
}
49+
}

src/Platforms/Exceptionless.AspNetCore/ExceptionlessAspNetCorePlugin.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public void Run(EventPluginContext context) {
3232
return;
3333

3434
try {
35-
ri = httpContext.GetRequestInfo(context.Client.Configuration);
35+
ri = httpContext.GetRequestInfo(context.Client.Configuration, context.ContextData.IsUnhandledError);
3636
} catch (Exception ex) {
3737
context.Log.Error(typeof(ExceptionlessAspNetCorePlugin), ex, "Error adding request info.");
3838
}

src/Platforms/Exceptionless.AspNetCore/ExceptionlessExtensions.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,9 @@ public static IApplicationBuilder UseExceptionless(this IApplicationBuilder app,
6868
/// </summary>
6969
/// <param name="context">The http context to gather information from.</param>
7070
/// <param name="config">The config.</param>
71-
public static RequestInfo GetRequestInfo(this HttpContext context, ExceptionlessConfiguration config) {
72-
return RequestInfoCollector.Collect(context, config);
71+
/// <param name="isUnhandledError">Whether this is an unhandled error. POST data is only collected for unhandled errors to avoid consuming the request stream.</param>
72+
public static RequestInfo GetRequestInfo(this HttpContext context, ExceptionlessConfiguration config, bool isUnhandledError = false) {
73+
return RequestInfoCollector.Collect(context, config, isUnhandledError);
7374
}
7475

7576
/// <summary>

src/Platforms/Exceptionless.AspNetCore/RequestInfoCollector.cs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public static class RequestInfoCollector {
1616
private const int MAX_BODY_SIZE = 50 * 1024;
1717
private const int MAX_DATA_ITEM_LENGTH = 1000;
1818

19-
public static RequestInfo Collect(HttpContext context, ExceptionlessConfiguration config) {
19+
public static RequestInfo Collect(HttpContext context, ExceptionlessConfiguration config, bool isUnhandledError = false) {
2020
if (context == null)
2121
return null;
2222

@@ -50,7 +50,9 @@ public static RequestInfo Collect(HttpContext context, ExceptionlessConfiguratio
5050
if (config.IncludeQueryString)
5151
info.QueryString = context.Request.Query.ToDictionary(exclusionList);
5252

53-
if (config.IncludePostData && !String.Equals(context.Request.Method, "GET", StringComparison.OrdinalIgnoreCase))
53+
// Only collect POST data for unhandled errors to avoid consuming the request stream
54+
// and breaking model binding for handled errors where the app continues processing.
55+
if (config.IncludePostData && isUnhandledError && !String.Equals(context.Request.Method, "GET", StringComparison.OrdinalIgnoreCase))
5456
info.PostData = GetPostData(context, config, exclusionList);
5557

5658
return info;
@@ -59,12 +61,7 @@ public static RequestInfo Collect(HttpContext context, ExceptionlessConfiguratio
5961
private static object GetPostData(HttpContext context, ExceptionlessConfiguration config, string[] exclusionList) {
6062
var log = config.Resolver.GetLog();
6163

62-
if (context.Request.HasFormContentType && context.Request.Form.Count > 0) {
63-
log.Debug("Reading POST data from Request.Form");
64-
return context.Request.Form.ToDictionary(exclusionList);
65-
}
66-
67-
var contentLength = context.Request.ContentLength.GetValueOrDefault();
64+
long contentLength = context.Request.ContentLength.GetValueOrDefault();
6865
if(contentLength == 0) {
6966
string message = "Content-length was zero, empty post.";
7067
log.Debug(message);
@@ -98,6 +95,11 @@ private static object GetPostData(HttpContext context, ExceptionlessConfiguratio
9895
return message;
9996
}
10097

98+
if (context.Request.HasFormContentType && context.Request.Form.Count > 0) {
99+
log.Debug("Reading POST data from Request.Form");
100+
return context.Request.Form.ToDictionary(exclusionList);
101+
}
102+
101103
// pass default values, except for leaveOpen: true. This prevents us from disposing the underlying stream
102104
using (var inputStream = new StreamReader(context.Request.Body, Encoding.UTF8, true, 1024, true)) {
103105
var sb = new StringBuilder();

src/Platforms/Exceptionless.Web/ExceptionlessWebExtensions.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,25 @@ public static class ExceptionlessWebExtensions {
1111
/// </summary>
1212
/// <param name="context">The http context to gather information from.</param>
1313
/// <param name="config">The config.</param>
14-
public static RequestInfo GetRequestInfo(this HttpContext context, ExceptionlessConfiguration config) {
14+
/// <param name="isUnhandledError">Whether this is an unhandled error. POST data is only collected for unhandled errors to avoid consuming the request stream.</param>
15+
public static RequestInfo GetRequestInfo(this HttpContext context, ExceptionlessConfiguration config, bool isUnhandledError = false) {
1516
if (context == null)
1617
return null;
1718

18-
return GetRequestInfo(context.ToWrapped(), config);
19+
return GetRequestInfo(context.ToWrapped(), config, isUnhandledError);
1920
}
2021

2122
/// <summary>
2223
/// Adds the current request info.
2324
/// </summary>
2425
/// <param name="context">The http context to gather information from.</param>
2526
/// <param name="config">The config.</param>
26-
public static RequestInfo GetRequestInfo(this HttpContextBase context, ExceptionlessConfiguration config) {
27+
/// <param name="isUnhandledError">Whether this is an unhandled error. POST data is only collected for unhandled errors to avoid consuming the request stream.</param>
28+
public static RequestInfo GetRequestInfo(this HttpContextBase context, ExceptionlessConfiguration config, bool isUnhandledError = false) {
2729
if (context == null && HttpContext.Current != null)
2830
context = HttpContext.Current.ToWrapped();
2931

30-
return RequestInfoCollector.Collect(context, config);
32+
return RequestInfoCollector.Collect(context, config, isUnhandledError);
3133
}
3234

3335
/// <summary>

src/Platforms/Exceptionless.Web/ExceptionlessWebPlugin.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public void Run(EventPluginContext context) {
3535
return;
3636

3737
try {
38-
ri = httpContext.GetRequestInfo(context.Client.Configuration);
38+
ri = httpContext.GetRequestInfo(context.Client.Configuration, context.ContextData.IsUnhandledError);
3939
} catch (Exception ex) {
4040
context.Log.Error(typeof(ExceptionlessWebPlugin), ex, "Error adding request info.");
4141
}

src/Platforms/Exceptionless.Web/RequestInfoCollector.cs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ internal static class RequestInfoCollector {
1515
private const int MAX_BODY_SIZE = 50 * 1024;
1616
private const int MAX_DATA_ITEM_LENGTH = 1000;
1717

18-
public static RequestInfo Collect(HttpContextBase context, ExceptionlessConfiguration config) {
18+
public static RequestInfo Collect(HttpContextBase context, ExceptionlessConfiguration config, bool isUnhandledError = false) {
1919
if (context == null)
2020
return null;
2121

@@ -63,7 +63,9 @@ public static RequestInfo Collect(HttpContextBase context, ExceptionlessConfigur
6363
}
6464
}
6565

66-
if (config.IncludePostData && !String.Equals(context.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
66+
// Only collect POST data for unhandled errors to avoid consuming the request stream
67+
// and breaking model binding for handled errors where the app continues processing.
68+
if (config.IncludePostData && isUnhandledError && !String.Equals(context.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
6769
info.PostData = GetPostData(context, config, exclusionList);
6870

6971
return info;
@@ -72,13 +74,7 @@ public static RequestInfo Collect(HttpContextBase context, ExceptionlessConfigur
7274
private static object GetPostData(HttpContextBase context, ExceptionlessConfiguration config, string[] exclusionList) {
7375
var log = config.Resolver.GetLog();
7476

75-
if (context.Request.Form.Count > 0) {
76-
log.Debug("Reading POST data from Request.Form");
77-
78-
return context.Request.Form.ToDictionary(exclusionList);
79-
}
80-
81-
var contentLength = context.Request.ContentLength;
77+
int contentLength = context.Request.ContentLength;
8278
if (contentLength == 0) {
8379
string message = "Content-length was zero, empty post.";
8480
log.Debug(message);
@@ -112,7 +108,13 @@ private static object GetPostData(HttpContextBase context, ExceptionlessConfigur
112108
return message;
113109
}
114110

115-
var maxDataToRead = contentLength == 0 ? MAX_BODY_SIZE : contentLength;
111+
if (context.Request.Form.Count > 0) {
112+
log.Debug("Reading POST data from Request.Form");
113+
114+
return context.Request.Form.ToDictionary(exclusionList);
115+
}
116+
117+
int maxDataToRead = contentLength == 0 ? MAX_BODY_SIZE : contentLength;
116118

117119
// pass default values, except for leaveOpen: true. This prevents us from disposing the underlying stream
118120
using (var inputStream = new StreamReader(context.Request.InputStream, Encoding.UTF8, true, 1024, true)) {
@@ -233,4 +235,4 @@ private static string GetUserIpAddress(HttpContextBase context) {
233235
return clientIp;
234236
}
235237
}
236-
}
238+
}

src/Platforms/Exceptionless.WebApi/ExceptionlessWebApiExtensions.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,9 @@ private static void ReplaceHttpErrorHandler(HttpConfiguration config, Exceptionl
6565
/// </summary>
6666
/// <param name="context">The http action context to gather information from.</param>
6767
/// <param name="config">The config.</param>
68-
public static RequestInfo GetRequestInfo(this HttpActionContext context, ExceptionlessConfiguration config) {
69-
return RequestInfoCollector.Collect(context, config);
68+
/// <param name="isUnhandledError">Whether this is an unhandled error. POST data collection is not implemented for WebApi.</param>
69+
public static RequestInfo GetRequestInfo(this HttpActionContext context, ExceptionlessConfiguration config, bool isUnhandledError = false) {
70+
return RequestInfoCollector.Collect(context, config, isUnhandledError);
7071
}
7172

7273
/// <summary>

src/Platforms/Exceptionless.WebApi/ExceptionlessWebApiPlugin.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public void Run(EventPluginContext context) {
2929
return;
3030

3131
try {
32-
ri = actionContext.GetRequestInfo(context.Client.Configuration);
32+
ri = actionContext.GetRequestInfo(context.Client.Configuration, context.ContextData.IsUnhandledError);
3333
} catch (Exception ex) {
3434
context.Log.Error(typeof(ExceptionlessWebApiPlugin), ex, "Error adding request info.");
3535
}

src/Platforms/Exceptionless.WebApi/RequestInfoCollector.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace Exceptionless.ExtendedData {
1414
internal static class RequestInfoCollector {
1515
private const int MAX_DATA_ITEM_LENGTH = 1000;
1616

17-
public static RequestInfo Collect(HttpActionContext context, ExceptionlessConfiguration config) {
17+
public static RequestInfo Collect(HttpActionContext context, ExceptionlessConfiguration config, bool isUnhandledError = false) {
1818
if (context == null)
1919
return null;
2020

@@ -48,8 +48,11 @@ public static RequestInfo Collect(HttpActionContext context, ExceptionlessConfig
4848
if (config.IncludeQueryString)
4949
info.QueryString = context.Request.RequestUri.ParseQueryString().ToDictionary(exclusionList);
5050

51+
// POST data collection is not implemented for WebApi due to async complexities and
52+
// the difficulty of reading the request body without interfering with model binding.
53+
// Other platforms (AspNetCore, Web) now only collect POST data for unhandled errors.
5154
// TODO: support getting post data asyncly.
52-
//if (config.IncludePostData && context.Request.Method != HttpMethod.Get)
55+
//if (config.IncludePostData && isUnhandledError && context.Request.Method != HttpMethod.Get)
5356
// info.PostData = GetPostData(context, config, exclusionList);
5457

5558
return info;

0 commit comments

Comments
 (0)