/// <summary> /// Processes the incoming HTTP call and capture details about /// the request, the response, the identity of the caller and the /// call duration to persistent storage. /// </summary> /// <param name="context">A reference to the Owin context.</param> /// <returns /> public override async Task Invoke(IOwinContext context) { var request = context.Request; var response = context.Response; // if it's an options call or tracing is not enabled, don't do anything if (IsOptionsRequest(context) || !_options.Trace || !_options.UrlPrefixes.Any(context.Request.Uri.AbsolutePath.StartsWith)) { await Next.Invoke(context); return; } // start stopwatch to record call duration var sw = new Stopwatch(); sw.Start(); // record call time _options.HttpTrackingEntry.CallDateTime = DateTime.UtcNow; // replace the request stream in order to intercept downstream reads // Buffering mvc reponse HttpResponse httpResponse = HttpContext.Current.Response; StreamHelper outputCapture = new StreamHelper(httpResponse.Filter); httpResponse.Filter = outputCapture; // Buffering Owin response if any IOwinResponse owinResponse = context.Response; Stream owinResponseStream = owinResponse.Body; owinResponse.Body = new MemoryStream(); string input = string.Empty; var httpInputStream = HttpContext.Current?.Request?.InputStream; if (httpInputStream.Length > 0) // can access http call { SetEventRequestHeaders(HttpContext.Current?.Request, _options.HttpTrackingEntry); input = HttpUtility.UrlDecode(await new StreamReader(httpInputStream).ReadToEndAsync()); HttpContext.Current.Request.InputStream.Position = 0; } else { // owin var requestBuffer = new MemoryStream(); var requestStream = new ContentStream(requestBuffer, request.Body); request.Body = requestStream; SetEventRequestHeaders(request, _options.HttpTrackingEntry); input = HttpUtility.UrlDecode(await WriteContentAsync(requestStream, _options.HttpTrackingEntry.RequestHeaders, _maxRecordedRequestLength)); } var anonymizedInput = _options.Anonymizer.Anonymize(input, request.Uri.ToString(), Direction.In); // record request/input _options.HttpTrackingEntry.Request = anonymizedInput; // invoke next middleware await Next.Invoke(context); // in case owin contains response if (outputCapture.CapturedData.Length == 0) { owinResponse.Body.Position = 0; await owinResponse.Body.CopyToAsync(owinResponseStream); } else { // in case we have captured data from mvc response copy it into owinResponse outputCapture.CapturedData.Position = 0; outputCapture.CapturedData.CopyTo(owinResponse.Body); } // set response headers. SetEventResponseHeaders(owinResponse, _options.HttpTrackingEntry); // record call duration sw.Stop(); var elapsed = HumanizeTimespan(sw.Elapsed); _options.HttpTrackingEntry.CallDuration = elapsed; // finally read final reponse body owinResponse.Body.Seek(0, SeekOrigin.Begin); var apiResponse = _options.Anonymizer.Anonymize(new StreamReader(owinResponse.Body).ReadToEnd(), request.Uri.ToString(), Direction.Out); _options.HttpTrackingEntry.Response = apiResponse; await SaveTraceAsync(_options.Context, _options.HttpTrackingEntry); }
/// <summary> /// Write content / request /// </summary> /// <param name="stream"></param> /// <param name="headers"></param> /// <param name="maxLength"></param> /// <returns></returns> private static async Task <string> WriteContentAsync(ContentStream stream, IDictionary <string, string[]> headers, long maxLength) { var contentType = headers.ContainsKey(ContentTypeHeader) ? headers[ContentTypeHeader][0] : null; return(await stream.ReadContentAsync(contentType, maxLength)); }