//-----------------------------------------------------------------------------
//
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// A copy of the License is located at
//
// http://aws.amazon.com/apache2.0
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.
//
//-----------------------------------------------------------------------------
#if NET45
using Amazon.Runtime.Internal.Util;
using Amazon.XRay.Recorder.Core;
using Amazon.XRay.Recorder.Core.Exceptions;
using Amazon.XRay.Recorder.Core.Internal.Entities;
using Amazon.XRay.Recorder.AutoInstrumentation.Utils;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
namespace Amazon.XRay.Recorder.AutoInstrumentation
{
///
/// Diagnostic listener for processing Http outgoing request
///
public class HttpOutDiagnosticListenerNetframework : DiagnosticListenerBase
{
private static readonly Logger _logger = Logger.GetLogger(typeof(HttpOutDiagnosticListenerNetframework));
private static readonly ConcurrentDictionary CurrentHttpWebRequests = new ConcurrentDictionary();
internal override string Name => "System.Net.Http.Desktop";
protected override void OnEvent(KeyValuePair value)
{
try
{
switch (value.Key)
{
case "System.Net.Http.Desktop.HttpRequestOut.Start":
{
OnEventStart(value.Value);
}
break;
case "System.Net.Http.Desktop.HttpRequestOut.Stop":
{
OnEventStop(value.Value);
}
break;
case "System.Net.Http.Desktop.HttpRequestOut.Ex.Stop":
{
OnEventException(value.Value);
}
break;
}
}
catch (Exception e)
{
_logger.Error(e, "Invalid diagnostic source key ({0})", value.Key);
}
}
///
/// Process http outgoing request
///
private void OnEventStart(object value)
{
var request = AgentUtil.FetchPropertyUsingReflection(value, "Request");
if (request is HttpWebRequest webRequest)
{
// Skip AWS SDK Request since it is instrumented using the SDK
if (HttpRequestUtil.IsTraceable(webRequest))
{
HttpRequestUtil.ProcessRequest(webRequest);
try
{
var currentSubsegment = AWSXRayRecorder.Instance.GetEntity() as Subsegment;
if (currentSubsegment != null)
{
CurrentHttpWebRequests.TryAdd(webRequest, currentSubsegment);
}
}
catch (EntityNotAvailableException e)
{
AWSXRayRecorder.Instance.TraceContext.HandleEntityMissing(AWSXRayRecorder.Instance, e, "Subsegment is not available in trace context.");
}
}
}
}
///
/// Process http response
///
private void OnEventStop(object value)
{
var request = AgentUtil.FetchPropertyUsingReflection(value, "Request");
var response = AgentUtil.FetchPropertyUsingReflection(value, "Response");
if (request is HttpWebRequest webRequest && response is HttpWebResponse webResponse)
{
if (CurrentHttpWebRequests.TryRemove(webRequest, out var currentSubsegment))
{
if (webResponse != null)
{
HttpRequestUtil.ProcessResponse(webResponse.StatusCode, webResponse.ContentLength, currentSubsegment);
}
HttpRequestUtil.EndSubsegment(currentSubsegment);
}
}
}
///
/// Process exception
///
private void OnEventException(object value)
{
var request = AgentUtil.FetchPropertyUsingReflection(value, "Request");
var status = AgentUtil.FetchPropertyUsingReflection(value, "StatusCode");
if (request is HttpWebRequest webRequest && status is HttpStatusCode httpStatusCode)
{
if (CurrentHttpWebRequests.TryRemove(webRequest, out var currentSubsegment))
{
HttpRequestUtil.HandleStatus(httpStatusCode, currentSubsegment);
HttpRequestUtil.EndSubsegment(currentSubsegment);
}
}
}
}
}
#endif