forked from ravendb/ravendb
/
RavenProfilingHandler.cs
159 lines (138 loc) · 4.82 KB
/
RavenProfilingHandler.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Routing;
using Newtonsoft.Json;
using Raven.Abstractions;
using Raven.Client.Document;
namespace Raven.Client.MvcIntegration
{
public class RavenProfilingHandler : IRouteHandler, IHttpHandler
{
private readonly JsonFormatterAndFieldsFilterer jsonFormatterAndFieldsFilterer;
private readonly ConcurrentDictionary<DocumentStore, object> stores = new ConcurrentDictionary<DocumentStore, object>();
public static string SourcePath;
private ConcurrentDictionary<string, string> cache = new ConcurrentDictionary<string, string>();
public RavenProfilingHandler(HashSet<string> fieldsToFilter)
{
jsonFormatterAndFieldsFilterer = new JsonFormatterAndFieldsFilterer(fieldsToFilter);
}
/// <summary>
/// Provides the object that processes the request.
/// </summary>
/// <returns>
/// An object that processes the request.
/// </returns>
/// <param name="requestContext">An object that encapsulates information about the request.</param>
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return this;
}
/// <summary>
/// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"/> interface.
/// </summary>
/// <param name="context">An <see cref="T:System.Web.HttpContext"/> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests. </param>
public void ProcessRequest(HttpContext context)
{
var path = context.Request.QueryString["path"];
if(string.IsNullOrEmpty(path) == false)
{
HandlePathRequest(context, path);
}
else
{
HandleDataReuqest(context);
}
}
private void HandleDataReuqest(HttpContext context)
{
context.Response.ContentType = "application/json";
var rawIds = context.Request.QueryString.GetValues("id") ??
context.Request.QueryString.GetValues("id[]") ??
Enumerable.Empty<string>();
var ids = rawIds.Select(Guid.Parse);
var items = from documentStore in stores.Keys
from id in ids
let profilingInformation = documentStore.GetProfilingInformationFor(id)
where profilingInformation != null
select jsonFormatterAndFieldsFilterer.Filter(profilingInformation);
var results = items.ToList();
CreateJsonSerializer().Serialize(context.Response.Output, results);
context.Response.Output.Flush();
}
private void HandlePathRequest(HttpContext context, string path)
{
if(path.EndsWith(".js"))
context.Response.ContentType = "application/javascript";
else if (path.EndsWith(".tmpl.html"))
context.Response.ContentType = "text/html";
else if (path.EndsWith(".css"))
context.Response.ContentType = "text/css";
if (string.IsNullOrEmpty(SourcePath))
{
var value = cache.GetOrAdd(path, s =>
{
using(var stream = typeof(RavenProfilingHandler).Assembly.GetManifestResourceStream("Raven.Client.MvcIntegration." + path.Replace("/",".")))
{
return new StreamReader(stream).ReadToEnd();
}
});
context.Response.Output.Write(value);
}
else // debug mode, probably
{
var file = Path.Combine(SourcePath, path);
context.Response.Output.Write(File.ReadAllText(file));
}
context.Response.Output.Flush();
}
private static JsonSerializer CreateJsonSerializer()
{
var jsonSerializer = new JsonSerializer();
foreach (var jsonConverter in Default.Converters)
{
jsonSerializer.Converters.Add(jsonConverter);
}
return jsonSerializer;
}
/// <summary>
/// Gets a value indicating whether another request can use the <see cref="T:System.Web.IHttpHandler"/> instance.
/// </summary>
/// <returns>
/// true if the <see cref="T:System.Web.IHttpHandler"/> instance is reusable; otherwise, false.
/// </returns>
public bool IsReusable
{
get { return true; }
}
public void AddStore(IDocumentStore store)
{
var documentStore = store as DocumentStore;
if (documentStore == null)
return;
if (documentStore.WasDisposed)
return;
object _;
documentStore.AfterDispose += (sender, args) => stores.TryRemove(documentStore, out _);
documentStore.SessionCreatedInternal += OnSessionCreated;
stores.TryAdd(documentStore, null);
}
private void OnSessionCreated(InMemoryDocumentSessionOperations operations)
{
RavenProfiler.ContextualSessionList.Add(operations.Id);
if (HttpContext.Current == null)
return;
try
{
HttpContext.Current.Response.AddHeader("X-RavenDb-Profiling-Id", operations.Id.ToString());
}
catch (HttpException)
{
// headers were already written, nothing much that we can do here, ignoring
}
}
}
}