/// <summary> /// Gets a ClientTimings object from a <see cref="ResultRequest"/>. /// </summary> /// <param name="request">The request to convert.</param> /// <returns>A <see cref="ClientTimings"/> object.</returns> public static ClientTimings FromRequest(ResultRequest request) { var result = new ClientTimings() { RedirectCount = request.RedirectCount ?? 0, Timings = new List <ClientTiming>(request.Performance?.Count + request.Probes?.Count ?? 0) }; if (request.Performance?.Count > 0) { foreach (var t in request.Performance) { if (t.Name?.EndsWith("End") == true) { continue; } result.Timings.Add(t); } } if (request.Probes?.Count > 0) { result.Timings.AddRange(request.Probes); } // Noise result.Timings.RemoveAll(t => t.Start < 0 || t.Duration < 0); // Sort for storage later result.Timings.Sort((a, b) => a.Start.CompareTo(b.Start)); // TODO: Collapse client start/end timings? Probably... return(result); }
/// <summary> /// Returns null if there is not client timing stuff /// </summary> /// <param name="request">The <see cref="HttpRequest"/> to get client timings from.</param> public static ClientTimings GetClientTimings(this HttpRequest request) { var dict = new Dictionary <string, string>(); var form = request.Form; foreach (var k in form.AllKeys) { dict.Add(k, form[k]); } return(ClientTimings.FromForm(dict)); }
/// <summary> /// Returns either json or full page html of a previous <see cref="MiniProfiler"/> session, /// identified by its <c>"?id=GUID"</c> on the query. /// </summary> /// <param name="context">The context to get a profiler response for.</param> private string GetSingleProfilerResult(HttpContext context) { Guid id; ResultRequest clientRequest = null; // When we're rendering as a button/popup in the corner, it's an AJAX/JSON request. // If that's absent, we're rendering results as a full page for sharing. var jsonRequest = context.Request.Headers["Accept"]?.Contains("application/json") == true; // Try to parse from the JSON payload first if (jsonRequest && context.Request.ContentLength > 0 && ResultRequest.TryParse(context.Request.InputStream, out clientRequest) && clientRequest.Id.HasValue) { id = clientRequest.Id.Value; } else if (Guid.TryParse(context.Request["id"], out id)) { // We got the guid from the querystring } else if (Options.StopwatchProvider != null) { // Fall back to the last result id = Options.Storage.List(1).FirstOrDefault(); } if (id == default(Guid)) { return(jsonRequest ? NotFound(context) : NotFound(context, "text/plain", "No Guid id specified on the query string")); } var profiler = Options.Storage.Load(id); string user = Options.UserIdProvider?.Invoke(context.Request); Options.Storage.SetViewed(user, id); if (profiler == null) { return(jsonRequest ? NotFound(context) : NotFound(context, "text/plain", "No MiniProfiler results found with Id=" + id.ToString())); } bool needsSave = false; if (profiler.ClientTimings == null && clientRequest?.TimingCount > 0) { profiler.ClientTimings = ClientTimings.FromRequest(clientRequest); needsSave = true; } if (!profiler.HasUserViewed) { profiler.HasUserViewed = true; needsSave = true; } if (needsSave) { Options.Storage.Save(profiler); } if (!AuthorizeRequest(context, isList: false, message: out string authorizeMessage)) { context.Response.ContentType = "application/json"; return(@"""hidden"""); // JSON } return(jsonRequest ? ResultsJson(context, profiler) : ResultsFullPage(context, profiler)); }
/// <summary> /// Returns null if there is not client timing stuff /// </summary> public static ClientTimings FromRequest(HttpRequest request) { ClientTimings timing = null; long navigationStart; long.TryParse(request[ClientTimingPrefix + "navigationStart]"], out navigationStart); if (navigationStart > 0) { var timings = new List<ClientTiming>(); timing = new ClientTimings(); int redirectCount; int.TryParse(request["clientPerformance[navigation][redirectCount]"], out redirectCount); timing.RedirectCount = redirectCount; var clientPerf = new Dictionary<string, ClientTiming>(); var clientProbes = new Dictionary<int, ClientTiming>(); foreach ( string key in request.Form.Keys.Cast<string>() .OrderBy(i => i.IndexOf("Start]", StringComparison.Ordinal) > 0 ? "_" + i : i)) { if (key.StartsWith(ClientTimingPrefix)) { long val; long.TryParse(request[key], out val); val -= navigationStart; string parsedName = key.Substring( ClientTimingPrefix.Length, (key.Length - 1) - ClientTimingPrefix.Length); // just ignore stuff that is negative ... not relevant if (val > 0) { if (parsedName.EndsWith("Start")) { var shortName = parsedName.Substring(0, parsedName.Length - 5); clientPerf[shortName] = new ClientTiming { Duration = -1, Name = parsedName, Start = val }; } else if (parsedName.EndsWith("End")) { var shortName = parsedName.Substring(0, parsedName.Length - 3); ClientTiming t; if (clientPerf.TryGetValue(shortName, out t)) { t.Duration = val - t.Start; t.Name = shortName; } } else { clientPerf[parsedName] = new ClientTiming { Name = parsedName, Start = val, Duration = -1 }; } } } if (key.StartsWith(ClientProbesPrefix)) { int probeId; if (key.IndexOf("]", StringComparison.Ordinal) > 0 && int.TryParse(key.Substring(ClientProbesPrefix.Length, key.IndexOf("]", StringComparison.Ordinal) - ClientProbesPrefix.Length), out probeId)) { ClientTiming t; if (!clientProbes.TryGetValue(probeId, out t)) { t = new ClientTiming(); clientProbes.Add(probeId, t); } if (key.EndsWith("[n]")) { t.Name = request[key]; } if (key.EndsWith("[d]")) { long val; long.TryParse(request[key], out val); if (val > 0) { t.Start = val - navigationStart; } } } } } foreach (var group in clientProbes .Values.OrderBy(p => p.Name) .GroupBy(p => p.Name)) { ClientTiming current = null; foreach (var item in group) { if (current == null) { current = item; } else { current.Duration = item.Start - current.Start; timings.Add(current); current = null; } } } foreach (var item in clientPerf.Values) { item.Name = SentenceCase(item.Name); } timings.AddRange(clientPerf.Values); timing.Timings = timings.OrderBy(t => t.Start).ToList(); } return timing; }
/// <summary> /// Returns null if there is not client timing stuff /// </summary> public static ClientTimings FromRequest(HttpRequest request) { ClientTimings timing = null; long navigationStart; long.TryParse(request[ClientTimingPrefix + "navigationStart]"], out navigationStart); if (navigationStart > 0) { var timings = new List <ClientTiming>(); timing = new ClientTimings(); int redirectCount; int.TryParse(request["clientPerformance[navigation][redirectCount]"], out redirectCount); timing.RedirectCount = redirectCount; var clientPerf = new Dictionary <string, ClientTiming>(); var clientProbes = new Dictionary <int, ClientTiming>(); foreach ( string key in request.Form.Keys.Cast <string>() .OrderBy(i => i.IndexOf("Start]", StringComparison.Ordinal) > 0 ? "_" + i : i)) { if (key.StartsWith(ClientTimingPrefix)) { long val; long.TryParse(request[key], out val); val -= navigationStart; string parsedName = key.Substring( ClientTimingPrefix.Length, (key.Length - 1) - ClientTimingPrefix.Length); // just ignore stuff that is negative ... not relevant if (val > 0) { if (parsedName.EndsWith("Start")) { var shortName = parsedName.Substring(0, parsedName.Length - 5); clientPerf[shortName] = new ClientTiming { Duration = -1, Name = parsedName, Start = val }; } else if (parsedName.EndsWith("End")) { var shortName = parsedName.Substring(0, parsedName.Length - 3); ClientTiming t; if (clientPerf.TryGetValue(shortName, out t)) { t.Duration = val - t.Start; t.Name = shortName; } } else { clientPerf[parsedName] = new ClientTiming { Name = parsedName, Start = val, Duration = -1 }; } } } if (key.StartsWith(ClientProbesPrefix)) { int probeId; if (key.IndexOf("]", StringComparison.Ordinal) > 0 && int.TryParse(key.Substring(ClientProbesPrefix.Length, key.IndexOf("]", StringComparison.Ordinal) - ClientProbesPrefix.Length), out probeId)) { ClientTiming t; if (!clientProbes.TryGetValue(probeId, out t)) { t = new ClientTiming(); clientProbes.Add(probeId, t); } if (key.EndsWith("[n]")) { t.Name = request[key]; } if (key.EndsWith("[d]")) { long val; long.TryParse(request[key], out val); if (val > 0) { t.Start = val - navigationStart; } } } } } foreach (var group in clientProbes .Values.OrderBy(p => p.Name) .GroupBy(p => p.Name)) { ClientTiming current = null; foreach (var item in group) { if (current == null) { current = item; } else { current.Duration = item.Start - current.Start; timings.Add(current); current = null; } } } foreach (var item in clientPerf.Values) { item.Name = SentenceCase(item.Name); } timings.AddRange(clientPerf.Values); timing.Timings = timings.OrderBy(t => t.Start).ToList(); } return(timing); }
/// <summary> /// Returns either JSON or full page HTML of a previous <c>MiniProfiler</c> session, /// identified by its <c>"?id=GUID"</c> on the query. /// </summary> /// <param name="context">The context to get a profiler response for.</param> private async Task <string> GetSingleProfilerResultAsync(HttpContext context) { Guid id; ResultRequest clientRequest = null; // When we're rendering as a button/popup in the corner, it's an AJAX/JSON request. // If that's absent, we're rendering results as a full page for sharing. bool jsonRequest = context.Request.Headers["Accept"].FirstOrDefault()?.Contains("application/json") == true; // Try to parse from the JSON payload first if (jsonRequest && context.Request.ContentLength > 0 && ResultRequest.TryParse(context.Request.Body, out clientRequest) && clientRequest.Id.HasValue) { id = clientRequest.Id.Value; } else if (Guid.TryParse(context.Request.Query["id"], out id)) { // We got the guid from the querystring } else if (Options.StopwatchProvider != null) { // Fall back to the last result id = (await Options.Storage.ListAsync(1).ConfigureAwait(false)).FirstOrDefault(); } if (id == default(Guid)) { return(NotFound(context, jsonRequest ? null : "No GUID id specified on the query string")); } var profiler = await Options.Storage.LoadAsync(id).ConfigureAwait(false); string user = Options.UserIdProvider?.Invoke(context.Request); await Options.Storage.SetViewedAsync(user, id).ConfigureAwait(false); if (profiler == null) { return(NotFound(context, jsonRequest ? null : "No MiniProfiler results found with Id=" + id.ToString())); } bool needsSave = false; if (profiler.ClientTimings == null && clientRequest?.TimingCount > 0) { profiler.ClientTimings = ClientTimings.FromRequest(clientRequest); needsSave = true; } if (!profiler.HasUserViewed) { profiler.HasUserViewed = true; needsSave = true; } if (needsSave) { await Options.Storage.SaveAsync(profiler).ConfigureAwait(false); } if (!AuthorizeRequest(context, isList: false, message: out string authorizeMessage)) { context.Response.ContentType = "application/json"; return(@"""hidden"""); // JSON } if (jsonRequest) { context.Response.ContentType = "application/json"; return(profiler.ToJson()); } else { context.Response.ContentType = "text/html; charset=utf-8"; return(Render.SingleResultHtml(profiler, context.Request.PathBase + Options.RouteBasePath.Value.EnsureTrailingSlash())); } }
/// <summary> /// Returns either json or full page html of a previous <c>MiniProfiler</c> session, /// identified by its <c>"?id=GUID"</c> on the query. /// </summary> private static string GetSingleProfilerResult(HttpContext context) { // when we're rendering as a button/popup in the corner, we'll pass ?popup=1 // if it's absent, we're rendering results as a full page for sharing var isPopup = context.Request["popup"].HasValue(); // this guid is the MiniProfiler.Id property // if this guid is not supplied, the last set of results needs to be // displayed. The home page doesn't have profiling otherwise. Guid id; if (!Guid.TryParse(context.Request["id"], out id)) { id = MiniProfiler.Settings.Storage.List(1).FirstOrDefault(); } if (id == default(Guid)) { return(isPopup ? NotFound(context) : NotFound(context, "text/plain", "No Guid id specified on the query string")); } MiniProfiler.Settings.EnsureStorageStrategy(); var profiler = MiniProfiler.Settings.Storage.Load(id); var provider = WebRequestProfilerProvider.Settings.UserProvider; string user = null; if (provider != null) { user = provider.GetUser(context.Request); } MiniProfiler.Settings.Storage.SetViewed(user, id); if (profiler == null) { return(isPopup ? NotFound(context) : NotFound(context, "text/plain", "No MiniProfiler results found with Id=" + id.ToString())); } bool needsSave = false; if (profiler.ClientTimings == null) { profiler.ClientTimings = ClientTimings.FromRequest(context.Request); if (profiler.ClientTimings != null) { needsSave = true; } } if (!profiler.HasUserViewed) { profiler.HasUserViewed = true; needsSave = true; } if (needsSave) { MiniProfiler.Settings.Storage.Save(profiler); } var authorize = MiniProfiler.Settings.Results_Authorize; if (authorize != null && !authorize(context.Request)) { context.Response.ContentType = "application/json"; return("hidden".ToJson()); } return(isPopup ? ResultsJson(context, profiler) : ResultsFullPage(context, profiler)); }
/// <summary> /// Returns either json or full page html of a previous <c>MiniProfiler</c> session, /// identified by its <c>"?id=GUID"</c> on the query. /// </summary> /// <param name="context">The context to get a profiler response for.</param> private async Task <string> GetSingleProfilerResultAsync(HttpContext context) { bool jsonRequest = false; IFormCollection form = null; // When we're rendering as a button/popup in the corner, we'll pass { popup: 1 } from jQuery // If it's absent, we're rendering results as a full page for sharing. if (context.Request.HasFormContentType) { form = await context.Request.ReadFormAsync().ConfigureAwait(false); // TODO: Get rid of popup and switch to application/json Accept header detection jsonRequest = form["popup"] == "1"; } // This guid is the MiniProfiler.Id property. If a guid is not supplied, // the last set of results needs to be displayed. string requestId = form?["id"] ?? context.Request.Query["id"]; if (!Guid.TryParse(requestId, out var id) && MiniProfiler.Settings.Storage != null) { id = (await MiniProfiler.Settings.Storage.ListAsync(1).ConfigureAwait(false)).FirstOrDefault(); } if (id == default(Guid)) { return(NotFound(context, jsonRequest ? null : "No GUID id specified on the query string")); } var profiler = await MiniProfiler.Settings.Storage.LoadAsync(id).ConfigureAwait(false); string user = Options.UserIdProvider?.Invoke(context.Request); await MiniProfiler.Settings.Storage.SetViewedAsync(user, id).ConfigureAwait(false); if (profiler == null) { return(NotFound(context, jsonRequest ? null : "No MiniProfiler results found with Id=" + id.ToString())); } bool needsSave = false; if (profiler.ClientTimings == null && form != null) { var dict = new Dictionary <string, string>(); foreach (var k in form.Keys) { dict.Add(k, form[k]); } profiler.ClientTimings = ClientTimings.FromForm(dict); if (profiler.ClientTimings != null) { needsSave = true; } } if (!profiler.HasUserViewed) { profiler.HasUserViewed = true; needsSave = true; } if (needsSave) { await MiniProfiler.Settings.Storage.SaveAsync(profiler).ConfigureAwait(false); } if (!AuthorizeRequest(context, isList: false, message: out string authorizeMessage)) { context.Response.ContentType = "application/json"; return(@"""hidden"""); // JSON } return(jsonRequest ? ResultsJson(context, profiler) : ResultsFullPage(context, profiler)); }
/// <summary> /// Returns null if there is not client timing stuff /// </summary> /// <param name="request"></param> /// <returns></returns> public static ClientTimings FromRequest(HttpRequest request) { ClientTimings timing = null; long navigationStart = 0; long.TryParse(request[clientTimingPrefix + "navigationStart]"], out navigationStart); if (navigationStart > 0) { timing = new ClientTimings(); int redirectCount = 0; int.TryParse(request["clientPerformance[navigation][redirectCount]"], out redirectCount); timing.RedirectCount = redirectCount; foreach (string key in request.Form.Keys) { if (key.StartsWith(clientTimingPrefix)) { long val = 0; long.TryParse(request[key], out val); val -= navigationStart; string parsedName = key.Substring(clientTimingPrefix.Length, (key.Length-1) - clientTimingPrefix.Length); // just ignore stuff that is negative ... not relevant if (val > 0) { switch (parsedName) { case "unloadEventStart" : timing.UnloadEventStart = val; break; case "unloadEventEnd": timing.UnloadEventEnd = val; break; case "redirectStart": timing.RedirectStart = val; break; case "redirectEnd": timing.RedirectEnd = val; break; case "fetchStart": timing.FetchStart = val; break; case "domainLookupStart": timing.DomainLookupStart = val; break; case "domainLookupEnd": timing.DomainLookupEnd = val; break; case "connectStart": timing.ConnectStart = val; break; case "connectEnd": timing.ConnectEnd = val; break; case "secureConnectionStart": timing.SecureConnectionStart = val; break; case "requestStart": timing.RequestStart = val; break; case "responseStart": timing.ResponseStart = val; break; case "responseEnd": timing.ResponseEnd = val; break; case "domLoading": timing.DomLoading = val; break; case "domInteractive": timing.DomInteractive = val; break; case "domContentLoadedEventStart": timing.DomContentLoadedEventStart = val; break; case "domContentLoadedEventEnd": timing.DomContentLoadedEventEnd = val; break; case "domComplete": timing.DomComplete = val; break; case "loadEventStart": timing.LoadEventStart = val; break; case "loadEventEnd": timing.LoadEventEnd = val; break; default: break; } } } } } return timing; }
/// <summary> /// Returns either json or full page html of a previous <c>MiniProfiler</c> session, /// identified by its <c>"?id=GUID"</c> on the query. /// </summary> /// <param name="context">The context to get a profiler response for.</param> private async Task <string> GetSingleProfilerResultAsync(HttpContext context) { var isPost = context.Request.HasFormContentType; // when we're rendering as a button/popup in the corner, we'll pass ?popup=1 // if it's absent, we're rendering results as a full page for sharing var isPopup = isPost && context.Request.Form["popup"].FirstOrDefault() == "1"; // this guid is the MiniProfiler.Id property // if this guid is not supplied, the last set of results needs to be // displayed. The home page doesn't have profiling otherwise. var requestId = isPost ? context.Request.Form["id"] : context.Request.Query["id"]; if (!Guid.TryParse(requestId, out var id) && MiniProfiler.Settings.Storage != null) { id = (await MiniProfiler.Settings.Storage.ListAsync(1).ConfigureAwait(false)).FirstOrDefault(); } if (id == default(Guid)) { return(isPopup ? NotFound(context) : NotFound(context, "No GUID id specified on the query string")); } var profiler = await MiniProfiler.Settings.Storage.LoadAsync(id).ConfigureAwait(false); string user = Options.UserIdProvider?.Invoke(context.Request); await MiniProfiler.Settings.Storage.SetViewedAsync(user, id).ConfigureAwait(false); if (profiler == null) { return(isPopup ? NotFound(context) : NotFound(context, "No MiniProfiler results found with Id=" + id.ToString())); } bool needsSave = false; if (profiler.ClientTimings == null && isPost) { var form = context.Request.Form; var dict = new Dictionary <string, string>(); foreach (var k in form.Keys) { dict.Add(k, form[k]); } profiler.ClientTimings = ClientTimings.FromForm(dict); if (profiler.ClientTimings != null) { needsSave = true; } } if (!profiler.HasUserViewed) { profiler.HasUserViewed = true; needsSave = true; } if (needsSave) { await MiniProfiler.Settings.Storage.SaveAsync(profiler).ConfigureAwait(false); } if (!AuthorizeRequest(context, isList: false, message: out string authorizeMessage)) { context.Response.ContentType = "application/json"; return("\"hidden\""); // JSON } return(isPopup ? ResultsJson(context, profiler) : ResultsFullPage(context, profiler)); }
public async Task AdddClientTiming() { var resultRequest = await jsRuntime.InvokeAsync <ResultRequest>("MiniProfiler.Blazor.getClientTimings"); MiniProfiler.Current.ClientTimings = ClientTimings.FromRequest(resultRequest); }