//-----------------------------------------------------------------------------
//
// 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.
//
//-----------------------------------------------------------------------------
using Amazon.XRay.Recorder.AutoInstrumentation.UnitTests.Tools;
using Amazon.XRay.Recorder.Core;
using Amazon.XRay.Recorder.Core.Internal.Entities;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
#if !NET45
using System.Linq;
#endif
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace Amazon.XRay.Recorder.AutoInstrumentation.Unittests
{
[TestClass]
public class HttpOutDiagnosticListenerTest : TestBase
{
private const string URL = "https://httpbin.org/";
private const string URL404 = "https://httpbin.org/404";
private readonly HttpClient _httpClient;
private static AWSXRayRecorder _recorder;
private static IDisposable _subscription;
public HttpOutDiagnosticListenerTest()
{
_httpClient = new HttpClient();
var subscription = new List()
{
#if NET45
new HttpOutDiagnosticListenerNetframework()
#else
new HttpOutDiagnosticListenerNetstandard()
#endif
};
_subscription = DiagnosticListener.AllListeners.Subscribe(new DiagnosticListenerObserver(subscription));
}
[TestInitialize]
public void TestInitialize()
{
_recorder = new AWSXRayRecorder();
#if NET45
AWSXRayRecorder.InitializeInstance(_recorder);
#else
AWSXRayRecorder.InitializeInstance(recorder : _recorder);
#endif
}
[TestCleanup]
public new void TestCleanup()
{
base.TestCleanup();
_recorder.Dispose();
_recorder = null;
_subscription.Dispose();
}
[TestMethod]
public async Task TestHttpClientSendAsync()
{
AWSXRayRecorder.Instance.BeginSegment("HttpClientSegment", TraceId);
var request = new HttpRequestMessage(HttpMethod.Get, URL);
using (await _httpClient.SendAsync(request)) { }
// HttpOutDiagnosticListenerNetframwork injects trace header in HttpWebRequest layer, upon which
// HttpClient layer is built, and trace header will be passed to downstream service through HttpWebRequest,
// but will not be present in HttpClient header.
#if !NET45
var traceHeader = request.Headers.GetValues(TraceHeader.HeaderKey).SingleOrDefault();
Assert.IsNotNull(traceHeader);
#endif
var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity();
AWSXRayRecorder.Instance.EndSegment();
var requestInfo = segment.Subsegments[0].Http["request"] as Dictionary;
Assert.AreEqual(URL, requestInfo["url"]);
Assert.AreEqual("GET", requestInfo["method"]);
var responseInfo = segment.Subsegments[0].Http["response"] as Dictionary;
Assert.AreEqual(200, responseInfo["status"]);
Assert.IsNotNull(responseInfo["content_length"]);
}
[TestMethod]
public async Task TestHttpClientSendAsync404()
{
AWSXRayRecorder.Instance.BeginSegment("HttpClientSegment", TraceId);
var request = new HttpRequestMessage(HttpMethod.Get, URL404);
using (await _httpClient.SendAsync(request)) { }
// HttpOutDiagnosticListenerNetframwork injects trace header in HttpWebRequest layer, upon which
// HttpClient layer is built, and trace header will be passed to downstream service through HttpWebRequest,
// but will not be present in HttpClient header.
#if !NET45
var traceHeader = request.Headers.GetValues(TraceHeader.HeaderKey).SingleOrDefault();
Assert.IsNotNull(traceHeader);
#endif
var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity();
AWSXRayRecorder.Instance.EndSegment();
var requestInfo = segment.Subsegments[0].Http["request"] as Dictionary;
Assert.AreEqual(URL404, requestInfo["url"]);
Assert.AreEqual("GET", requestInfo["method"]);
Assert.IsTrue(segment.Subsegments[0].HasError);
Assert.IsFalse(segment.Subsegments[0].HasFault);
}
[TestMethod]
public void TestHttpWebRequestSend()
{
AWSXRayRecorder.Instance.BeginSegment("HttpWebRequestSegment", TraceId);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
using (var response = request.GetResponse()) { }
var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity();
AWSXRayRecorder.Instance.EndSegment();
// HttpOutDiagnosticListenerNetstandard injects trace header in HttpRequestMessage layer, upon which
// HttpWebRequest layer is built, and trace header will be passed to downstream service through HttpRequestMessage,
// but will not be present in HttpWebRequest header.
#if NET45
Assert.IsNotNull(request.Headers[TraceHeader.HeaderKey]);
#endif
var requestInfo = segment.Subsegments[0].Http["request"] as Dictionary;
Assert.AreEqual(URL, requestInfo["url"]);
Assert.AreEqual("GET", requestInfo["method"]);
var responseInfo = segment.Subsegments[0].Http["response"] as Dictionary;
Assert.AreEqual(200, responseInfo["status"]);
Assert.IsNotNull(responseInfo["content_length"]);
}
[TestMethod]
public void TestHttpWebRequestSend404()
{
AWSXRayRecorder.Instance.BeginSegment("HttpWebRequestSegment", TraceId);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL404);
try
{
using (var response = request.GetResponse())
{
}
Assert.Fail();
}
catch (WebException) // Expected
{
var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity();
AWSXRayRecorder.Instance.EndSegment();
// HttpOutDiagnosticListenerNetstandard injects trace header in HttpRequestMessage layer, upon which
// HttpWebRequest layer is built, and trace header will be passed to downstream service through HttpRequestMessage,
// but will not be present in HttpWebRequest header.
#if NET45
Assert.IsNotNull(request.Headers[TraceHeader.HeaderKey]);
#endif
var requestInfo = segment.Subsegments[0].Http["request"] as Dictionary;
Assert.AreEqual(URL404, requestInfo["url"]);
Assert.AreEqual("GET", requestInfo["method"]);
var subsegment = segment.Subsegments[0];
Assert.IsTrue(subsegment.HasError);
Assert.IsFalse(subsegment.HasFault);
}
}
[TestMethod]
public async Task TestHttpWebRequestSendAsync()
{
AWSXRayRecorder.Instance.BeginSegment("HttpWebRequestSegment", TraceId);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
using (await request.GetResponseAsync())
{
}
var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity();
AWSXRayRecorder.Instance.EndSegment();
// HttpOutDiagnosticListenerNetstandard injects trace header in HttpRequestMessage layer, upon which
// HttpWebRequest layer is built, and trace header will be passed to downstream service through HttpRequestMessage,
// but will not be present in HttpWebRequest header.
#if NET45
Assert.IsNotNull(request.Headers[TraceHeader.HeaderKey]);
#endif
var requestInfo = segment.Subsegments[0].Http["request"] as Dictionary;
Assert.AreEqual(URL, requestInfo["url"]);
Assert.AreEqual("GET", requestInfo["method"]);
var responseInfo = segment.Subsegments[0].Http["response"] as Dictionary;
Assert.AreEqual(200, responseInfo["status"]);
Assert.IsNotNull(responseInfo["content_length"]);
}
[TestMethod]
public async Task TestHttpWebRequestSendAsync404()
{
AWSXRayRecorder.Instance.BeginSegment("HttpWebRequestSegment", TraceId);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL404);
try
{
using (await request.GetResponseAsync())
{
}
Assert.Fail();
}
catch (WebException) // Expected
{
var segment = AWSXRayRecorder.Instance.TraceContext.GetEntity();
AWSXRayRecorder.Instance.EndSegment();
// HttpOutDiagnosticListenerNetstandard injects trace header in HttpRequestMessage layer, upon which
// HttpWebRequest layer is built, and trace header will be passed to downstream service through HttpRequestMessage,
// but will not be present in HttpWebRequest header.
#if NET45
Assert.IsNotNull(request.Headers[TraceHeader.HeaderKey]);
#endif
var requestInfo = segment.Subsegments[0].Http["request"] as Dictionary;
Assert.AreEqual(URL404, requestInfo["url"]);
Assert.AreEqual("GET", requestInfo["method"]);
var subsegment = segment.Subsegments[0];
Assert.IsTrue(subsegment.HasError);
Assert.IsFalse(subsegment.HasFault);
}
}
[TestMethod]
public async Task TestXrayDisabledSendAsync()
{
_recorder = new MockAWSXRayRecorder() { IsTracingDisabledValue = true };
#if NET45
AWSXRayRecorder.InitializeInstance(_recorder);
#else
AWSXRayRecorder.InitializeInstance(recorder: _recorder);
#endif
Assert.IsTrue(AWSXRayRecorder.Instance.IsTracingDisabled());
var request = new HttpRequestMessage(HttpMethod.Get, URL);
using (var response = await _httpClient.SendAsync(request))
{
Assert.IsNotNull(response);
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
}
[TestMethod]
public async Task TestXrayContextMissingStrategySendAsync() // Test that respects ContextMissingStrategy
{
_recorder = new MockAWSXRayRecorder();
#if NET45
AWSXRayRecorder.InitializeInstance(_recorder);
#else
AWSXRayRecorder.InitializeInstance(recorder: _recorder);
#endif
AWSXRayRecorder.Instance.ContextMissingStrategy = Core.Strategies.ContextMissingStrategy.LOG_ERROR;
Assert.IsFalse(AWSXRayRecorder.Instance.IsTracingDisabled());
_recorder.EndSegment();
// The test should not break. No segment is available in the context, however, since the context missing strategy is log error,
// no exception should be thrown by below code.
var request = new HttpRequestMessage(HttpMethod.Get, URL);
using (var response = await _httpClient.SendAsync(request))
{
Assert.IsNotNull(response);
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
}
}
}