Skip to content

Commit 670ead2

Browse files
authored
Fixes #127: Added ability to record http headers (#300)
* Fixes #127: Added ability to record http headers * Normalized request info collectors * Added TODO for getting web api post data
1 parent 9e24a9c commit 670ead2

File tree

5 files changed

+189
-56
lines changed

5 files changed

+189
-56
lines changed

src/Exceptionless/Configuration/ExceptionlessConfiguration.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ public bool IncludePrivateInformation {
228228
IncludeUserName = value;
229229
IncludeMachineName = value;
230230
IncludeIpAddress = value;
231+
IncludeHeaders = value;
231232
IncludeCookies = value;
232233
IncludePostData = value;
233234
IncludeQueryString = value;
@@ -247,6 +248,11 @@ public bool IncludePrivateInformation {
247248
/// </summary>
248249
public bool IncludeIpAddress { get; set; }
249250
/// <summary>
251+
/// Gets or sets a value indicating whether to include Headers.
252+
/// NOTE: DataExclusions are applied to all Headers keys when enabled.
253+
/// </summary>
254+
public bool IncludeHeaders { get; set; }
255+
/// <summary>
250256
/// Gets or sets a value indicating whether to include Cookies.
251257
/// NOTE: DataExclusions are applied to all Cookie keys when enabled.
252258
/// </summary>

src/Exceptionless/Models/Client/Data/RequestInfo.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace Exceptionless.Models.Data {
55
public class RequestInfo : IData {
66
public RequestInfo() {
77
Data = new DataDictionary();
8+
Headers = new Dictionary<string, string[]>();
89
Cookies = new Dictionary<string, string>();
910
QueryString = new Dictionary<string, string>();
1011
}
@@ -49,6 +50,11 @@ public RequestInfo() {
4950
/// </summary>
5051
public string ClientIpAddress { get; set; }
5152

53+
/// <summary>
54+
/// The header values from the request.
55+
/// </summary>
56+
public Dictionary<string, string[]> Headers { get; set; }
57+
5258
/// <summary>
5359
/// The request cookies.
5460
/// </summary>
@@ -70,7 +76,7 @@ public RequestInfo() {
7076
public DataDictionary Data { get; set; }
7177

7278
protected bool Equals(RequestInfo other) {
73-
return string.Equals(UserAgent, other.UserAgent) && string.Equals(HttpMethod, other.HttpMethod) && IsSecure == other.IsSecure && string.Equals(Host, other.Host) && Port == other.Port && string.Equals(Path, other.Path) && string.Equals(Referrer, other.Referrer) && string.Equals(ClientIpAddress, other.ClientIpAddress) && Cookies.CollectionEquals(other.Cookies) && QueryString.CollectionEquals(other.QueryString) && Equals(Data, other.Data);
79+
return string.Equals(UserAgent, other.UserAgent) && string.Equals(HttpMethod, other.HttpMethod) && IsSecure == other.IsSecure && string.Equals(Host, other.Host) && Port == other.Port && string.Equals(Path, other.Path) && string.Equals(Referrer, other.Referrer) && string.Equals(ClientIpAddress, other.ClientIpAddress) && Headers.CollectionEquals(other.Headers) && Cookies.CollectionEquals(other.Cookies) && QueryString.CollectionEquals(other.QueryString) && Equals(Data, other.Data);
7480
}
7581

7682
public override bool Equals(object obj) {
@@ -95,6 +101,7 @@ public override int GetHashCode() {
95101
hashCode = (hashCode * 397) ^ (Path == null ? 0 : Path.GetHashCode());
96102
hashCode = (hashCode * 397) ^ (Referrer == null ? 0 : Referrer.GetHashCode());
97103
hashCode = (hashCode * 397) ^ (ClientIpAddress == null ? 0 : ClientIpAddress.GetHashCode());
104+
hashCode = (hashCode * 397) ^ (Headers == null ? 0 : Headers.GetCollectionHashCode());
98105
hashCode = (hashCode * 397) ^ (Cookies == null ? 0 : Cookies.GetCollectionHashCode(_cookieHashCodeExclusions));
99106
hashCode = (hashCode * 397) ^ (QueryString == null ? 0 : QueryString.GetCollectionHashCode());
100107
hashCode = (hashCode * 397) ^ (Data == null ? 0 : Data.GetCollectionHashCode());

src/Platforms/Exceptionless.AspNetCore/RequestInfoCollector.cs

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@
1414
namespace Exceptionless.AspNetCore {
1515
public static class RequestInfoCollector {
1616
private const int MAX_BODY_SIZE = 50 * 1024;
17+
private const int MAX_DATA_ITEM_LENGTH = 1000;
18+
1719
public static RequestInfo Collect(HttpContext context, ExceptionlessConfiguration config) {
1820
if (context == null)
1921
return null;
2022

2123
var info = new RequestInfo {
2224
HttpMethod = context.Request.Method,
2325
IsSecure = context.Request.IsHttps,
24-
Path = context.Request.Path.HasValue ? context.Request.Path.Value : "/",
26+
Path = context.Request.Path.HasValue ? context.Request.Path.Value : "/"
2527
};
2628

2729
if (config.IncludeIpAddress)
@@ -32,13 +34,16 @@ public static RequestInfo Collect(HttpContext context, ExceptionlessConfiguratio
3234

3335
info.Port = context.Request.Host.Port.GetValueOrDefault(info.IsSecure ? 443 : 80);
3436

35-
if (context.Request.Headers.ContainsKey(HeaderNames.UserAgent))
36-
info.UserAgent = context.Request.Headers[HeaderNames.UserAgent].ToString();
37+
if (context.Request.Headers.TryGetValue(HeaderNames.UserAgent, out var userAgentHeader))
38+
info.UserAgent = userAgentHeader.ToString();
3739

38-
if (context.Request.Headers.ContainsKey(HeaderNames.Referer))
39-
info.Referrer = context.Request.Headers[HeaderNames.Referer].ToString();
40+
if (context.Request.Headers.TryGetValue(HeaderNames.Referer, out var refererHeader))
41+
info.Referrer = refererHeader.ToString();
4042

4143
var exclusionList = config.DataExclusions as string[] ?? config.DataExclusions.ToArray();
44+
if (config.IncludeHeaders)
45+
info.Headers = context.Request.Headers.ToHeaderDictionary(exclusionList);
46+
4247
if (config.IncludeCookies)
4348
info.Cookies = context.Request.Cookies.ToDictionary(exclusionList);
4449

@@ -93,17 +98,15 @@ private static object GetPostData(HttpContext context, ExceptionlessConfiguratio
9398
return message;
9499
}
95100

96-
var maxDataToRead = contentLength == 0 ? MAX_BODY_SIZE : contentLength;
97-
98101
// pass default values, except for leaveOpen: true. This prevents us from disposing the underlying stream
99102
using (var inputStream = new StreamReader(context.Request.Body, Encoding.UTF8, true, 1024, true)) {
100103
var sb = new StringBuilder();
101104
int numRead;
102105

103-
int bufferSize = (int)Math.Min(1024, maxDataToRead);
106+
int bufferSize = (int)Math.Min(1024, contentLength);
104107

105108
char[] buffer = new char[bufferSize];
106-
while ((numRead = inputStream.ReadBlock(buffer, 0, bufferSize)) > 0 && (sb.Length + numRead) <= maxDataToRead) {
109+
while ((numRead = inputStream.ReadBlock(buffer, 0, bufferSize)) > 0 && (sb.Length + numRead) <= contentLength) {
107110
sb.Append(buffer, 0, numRead);
108111
}
109112
string postData = sb.ToString();
@@ -121,8 +124,15 @@ private static object GetPostData(HttpContext context, ExceptionlessConfiguratio
121124
}
122125
}
123126

124-
private static readonly List<string> _ignoredFormFields = new List<string> {
125-
"__*"
127+
private static readonly List<string> _ignoredHeaders = new List<string> {
128+
HeaderNames.Authorization,
129+
HeaderNames.Cookie,
130+
HeaderNames.Host,
131+
HeaderNames.Method,
132+
HeaderNames.Path,
133+
HeaderNames.ProxyAuthorization,
134+
HeaderNames.Referer,
135+
HeaderNames.UserAgent
126136
};
127137

128138
private static readonly List<string> _ignoredCookies = new List<string> {
@@ -131,20 +141,44 @@ private static object GetPostData(HttpContext context, ExceptionlessConfiguratio
131141
"*SessionId*"
132142
};
133143

134-
private static Dictionary<string, string> ToDictionary(this IRequestCookieCollection cookies, IList<string> exclusions) {
144+
private static readonly List<string> _ignoredFormFields = new List<string> {
145+
"__*"
146+
};
147+
148+
private static Dictionary<string, string[]> ToHeaderDictionary(this IEnumerable<KeyValuePair<string, StringValues>> headers, string[] exclusions) {
149+
var d = new Dictionary<string, string[]>();
150+
151+
foreach (var header in headers) {
152+
if (String.IsNullOrEmpty(header.Key) || _ignoredHeaders.Contains(header.Key) || header.Key.AnyWildcardMatches(exclusions))
153+
continue;
154+
155+
string[] values = header.Value.Where(hv => hv != null && hv.Length < MAX_DATA_ITEM_LENGTH).ToArray();
156+
if (values.Length == 0)
157+
continue;
158+
159+
d[header.Key] = values;
160+
}
161+
162+
return d;
163+
}
164+
165+
private static Dictionary<string, string> ToDictionary(this IRequestCookieCollection cookies, string[] exclusions) {
135166
var d = new Dictionary<string, string>();
136167

137168
foreach (var kvp in cookies) {
138169
if (String.IsNullOrEmpty(kvp.Key) || kvp.Key.AnyWildcardMatches(_ignoredCookies) || kvp.Key.AnyWildcardMatches(exclusions))
139170
continue;
140171

141-
d.Add(kvp.Key, kvp.Value);
172+
if (kvp.Value == null || kvp.Value.Length >= MAX_DATA_ITEM_LENGTH)
173+
continue;
174+
175+
d[kvp.Key] = kvp.Value;
142176
}
143177

144178
return d;
145179
}
146180

147-
private static Dictionary<string, string> ToDictionary(this IEnumerable<KeyValuePair<string, StringValues>> values, IEnumerable<string> exclusions) {
181+
private static Dictionary<string, string> ToDictionary(this IEnumerable<KeyValuePair<string, StringValues>> values, string[] exclusions) {
148182
var d = new Dictionary<string, string>();
149183

150184
foreach (var kvp in values) {
@@ -153,10 +187,12 @@ private static Dictionary<string, string> ToDictionary(this IEnumerable<KeyValue
153187

154188
try {
155189
string value = kvp.Value.ToString();
156-
d.Add(kvp.Key, value);
190+
if (value.Length >= MAX_DATA_ITEM_LENGTH)
191+
continue;
192+
193+
d[kvp.Key] = value;
157194
} catch (Exception ex) {
158-
if (!d.ContainsKey(kvp.Key))
159-
d.Add(kvp.Key, ex.Message);
195+
d[kvp.Key] = $"EXCEPTION: {ex.Message}";
160196
}
161197
}
162198

src/Platforms/Exceptionless.Web/RequestInfoCollector.cs

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Collections.Specialized;
4-
using System.Diagnostics;
54
using System.IO;
65
using System.Linq;
76
using System.Text;
@@ -13,8 +12,8 @@
1312

1413
namespace Exceptionless.ExtendedData {
1514
internal static class RequestInfoCollector {
15+
private const int MAX_BODY_SIZE = 50 * 1024;
1616
private const int MAX_DATA_ITEM_LENGTH = 1000;
17-
private const int MAX_BODY_SIZE = 50*1024;
1817

1918
public static RequestInfo Collect(HttpContextBase context, ExceptionlessConfiguration config) {
2019
if (context == null)
@@ -50,13 +49,12 @@ public static RequestInfo Collect(HttpContextBase context, ExceptionlessConfigur
5049
info.Port = context.Request.Url.Port;
5150

5251
var exclusionList = config.DataExclusions as string[] ?? config.DataExclusions.ToArray();
52+
if (config.IncludeHeaders)
53+
info.Headers = context.Request.Headers.ToHeaderDictionary(exclusionList);
5354

5455
if (config.IncludeCookies)
5556
info.Cookies = context.Request.Cookies.ToDictionary(exclusionList);
56-
57-
if (config.IncludePostData && !String.Equals(context.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
58-
info.PostData = GetPostData(context, config, exclusionList);
59-
57+
6058
if (config.IncludeQueryString) {
6159
try {
6260
info.QueryString = context.Request.QueryString.ToDictionary(exclusionList);
@@ -65,6 +63,9 @@ public static RequestInfo Collect(HttpContextBase context, ExceptionlessConfigur
6563
}
6664
}
6765

66+
if (config.IncludePostData && !String.Equals(context.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
67+
info.PostData = GetPostData(context, config, exclusionList);
68+
6869
return info;
6970
}
7071

@@ -139,8 +140,15 @@ private static object GetPostData(HttpContextBase context, ExceptionlessConfigur
139140
}
140141
}
141142

142-
private static readonly List<string> _ignoredFormFields = new List<string> {
143-
"__*"
143+
private static readonly List<string> _ignoredHeaders = new List<string> {
144+
"Authorization",
145+
"Cookie",
146+
"Host",
147+
"Method",
148+
"Path",
149+
"Proxy-Authorization",
150+
"Referer",
151+
"User-Agent"
144152
};
145153

146154
private static readonly List<string> _ignoredCookies = new List<string> {
@@ -149,38 +157,68 @@ private static object GetPostData(HttpContextBase context, ExceptionlessConfigur
149157
"*SessionId*"
150158
};
151159

152-
private static Dictionary<string, string> ToDictionary(this HttpCookieCollection cookies, IEnumerable<string> exclusions) {
160+
private static readonly List<string> _ignoredFormFields = new List<string> {
161+
"__*"
162+
};
163+
164+
private static Dictionary<string, string[]> ToHeaderDictionary(this NameValueCollection headers, string[] exclusions) {
165+
var result = new Dictionary<string, string[]>();
166+
167+
foreach (string key in headers.AllKeys) {
168+
if (String.IsNullOrEmpty(key) || _ignoredHeaders.Contains(key) || key.AnyWildcardMatches(exclusions))
169+
continue;
170+
171+
try {
172+
string value = headers.Get(key);
173+
if (value == null || value.Length >= MAX_DATA_ITEM_LENGTH)
174+
continue;
175+
176+
result[key] = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
177+
}
178+
catch (Exception ex) {
179+
result[key] = new[] { $"EXCEPTION: {ex.Message}" };
180+
}
181+
}
182+
183+
return result;
184+
}
185+
186+
private static Dictionary<string, string> ToDictionary(this HttpCookieCollection cookies, string[] exclusions) {
153187
var d = new Dictionary<string, string>();
154188

155-
foreach (string key in cookies.AllKeys.Distinct().Where(k => !String.IsNullOrEmpty(k) && !k.AnyWildcardMatches(_ignoredCookies) && !k.AnyWildcardMatches(exclusions))) {
189+
foreach (string key in cookies.AllKeys.Distinct()) {
190+
if (String.IsNullOrEmpty(key) || key.AnyWildcardMatches(_ignoredCookies) || key.AnyWildcardMatches(exclusions))
191+
continue;
192+
156193
try {
157-
HttpCookie cookie = cookies.Get(key);
158-
if (cookie != null && cookie.Value != null && cookie.Value.Length < MAX_DATA_ITEM_LENGTH && !d.ContainsKey(key))
159-
d.Add(key, cookie.Value);
194+
var cookie = cookies.Get(key);
195+
if (cookie == null || cookie.Value == null || cookie.Value.Length >= MAX_DATA_ITEM_LENGTH)
196+
continue;
197+
198+
d[key] = cookie.Value;
160199
} catch (Exception ex) {
161-
if (!d.ContainsKey(key))
162-
d.Add(key, ex.Message);
200+
d[key] = $"EXCEPTION: {ex.Message}";
163201
}
164202
}
165203

166204
return d;
167205
}
168206

169-
private static Dictionary<string, string> ToDictionary(this NameValueCollection values, IEnumerable<string> exclusions) {
207+
private static Dictionary<string, string> ToDictionary(this NameValueCollection values, string[] exclusions) {
170208
var d = new Dictionary<string, string>();
171-
172-
var exclusionsArray = exclusions as string[] ?? exclusions.ToArray();
209+
173210
foreach (string key in values.AllKeys) {
174-
if (String.IsNullOrEmpty(key) || key.AnyWildcardMatches(_ignoredFormFields) || key.AnyWildcardMatches(exclusionsArray))
211+
if (String.IsNullOrEmpty(key) || key.AnyWildcardMatches(_ignoredFormFields) || key.AnyWildcardMatches(exclusions))
175212
continue;
176213

177214
try {
178215
string value = values.Get(key);
179-
if (value != null && !d.ContainsKey(key) && value.Length < MAX_DATA_ITEM_LENGTH)
180-
d.Add(key, value);
216+
if (value == null || d.ContainsKey(key) || value.Length >= MAX_DATA_ITEM_LENGTH)
217+
continue;
218+
219+
d[key] = value;
181220
} catch (Exception ex) {
182-
if (!d.ContainsKey(key))
183-
d.Add(key, "EXCEPTION: " + ex.Message);
221+
d[key] = $"EXCEPTION: {ex.Message}";
184222
}
185223
}
186224

0 commit comments

Comments
 (0)