diff --git a/WEB/Src/WindowsServer/WindowsServer.Tests/AzureInstanceMetadataEndToEndTests.cs b/WEB/Src/WindowsServer/WindowsServer.Tests/AzureInstanceMetadataEndToEndTests.cs
index 9711b95569..9ba06a6e6d 100644
--- a/WEB/Src/WindowsServer/WindowsServer.Tests/AzureInstanceMetadataEndToEndTests.cs
+++ b/WEB/Src/WindowsServer/WindowsServer.Tests/AzureInstanceMetadataEndToEndTests.cs
@@ -3,16 +3,20 @@ namespace Microsoft.ApplicationInsights.WindowsServer
using System;
using System.IO;
using System.Net;
+ using System.Net.Http;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading;
+ using System.Threading.Tasks;
+
using Microsoft.ApplicationInsights.WindowsServer.Implementation;
using Microsoft.ApplicationInsights.WindowsServer.Implementation.DataContracts;
using Microsoft.ApplicationInsights.WindowsServer.Mock;
-#if NETCOREAPP
- using Microsoft.AspNetCore.Http;
-#endif
using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ using Moq;
+ using Moq.Protected;
+
using Assert = Xunit.Assert;
///
@@ -20,237 +24,156 @@ namespace Microsoft.ApplicationInsights.WindowsServer
/// end to end functionality as closely as possible.
///
[TestClass]
-#if NETCOREAPP
- [Ignore("Problems with in-proc test server. These tests are temporarially disabled while working on a fix. See #2357 and #2355.")]
-#endif
public class AzureInstanceMetadataEndToEndTests
{
- internal const string MockTestUri = "http://localhost:9922/";
-
[TestMethod]
- public void SpoofedResponseFromAzureIMSDoesntCrash()
+ public async Task SpoofedResponseFromAzureIMSDoesntCrash()
{
- var testMetadata = this.GetTestMetadata();
- string testPath = "spoofedResponse";
+ // SETUP
+ var testMetadata = GetTestMetadata();
+ Mock mockHttpMessageHandler = GetMockHttpMessageHandler(testMetadata);
+ var azureIms = new AzureMetadataRequestor(new HttpClient(mockHttpMessageHandler.Object));
- using (new AzureInstanceMetadataServiceMock(
- AzureInstanceMetadataEndToEndTests.MockTestUri,
- testPath,
- (response) =>
- {
- response.StatusCode = (int)HttpStatusCode.OK;
+ // ACT
+ var azureImsProps = new AzureComputeMetadataHeartbeatPropertyProvider();
+ var azureIMSData = await azureIms.GetAzureComputeMetadataAsync();
- var jsonStream = this.GetTestMetadataStream(testMetadata);
- response.SetContentLength(jsonStream.Length);
- response.ContentType = "application/json";
- response.SetContentEncoding(Encoding.UTF8);
- response.WriteStreamToBody(jsonStream);
- }))
+ // VERIFY
+ foreach (string fieldName in azureImsProps.ExpectedAzureImsFields)
{
- var azureIms = new AzureMetadataRequestor
- {
- BaseAimsUri = string.Concat(AzureInstanceMetadataEndToEndTests.MockTestUri, testPath, "/")
- };
-
- var azureImsProps = new AzureComputeMetadataHeartbeatPropertyProvider();
- var azureIMSData = azureIms.GetAzureComputeMetadataAsync();
- azureIMSData.Wait();
-
- foreach (string fieldName in azureImsProps.ExpectedAzureImsFields)
- {
- string fieldValue = azureIMSData.Result.GetValueForField(fieldName);
- Assert.NotNull(fieldValue);
- Assert.Equal(fieldValue, testMetadata.GetValueForField(fieldName));
- }
+ string fieldValue = azureIMSData.GetValueForField(fieldName);
+ Assert.NotNull(fieldValue);
+ Assert.Equal(fieldValue, testMetadata.GetValueForField(fieldName));
}
}
[TestMethod]
- public void AzureImsResponseTooLargeStopsCollection()
+ public async Task AzureImsResponseExcludesMalformedValues()
{
- string testPath = "tooLarge";
-
- using (new AzureInstanceMetadataServiceMock(
- AzureInstanceMetadataEndToEndTests.MockTestUri,
- testPath,
- (response) =>
- {
- response.StatusCode = (int)HttpStatusCode.OK;
-
- var tester = this.GetTestMetadata();
-
- // ensure we will be outside the max allowed content size by setting a single text field to max length + 1
- var testStuff = new char[AzureMetadataRequestor.AzureImsMaxResponseBufferSize + 1];
- for (int i = 0; i < (AzureMetadataRequestor.AzureImsMaxResponseBufferSize + 1); ++i)
- {
- testStuff[i] = (char)((int)'a' + (i % 26));
- }
+ // SETUP
+ var testMetadata = GetTestMetadata();
+ // make it a malicious-ish response...
+ testMetadata.Name = "Not allowed for VM names";
+ testMetadata.ResourceGroupName = "Not allowed for resource group name";
+ testMetadata.SubscriptionId = "Definitely-not-a GUID up here";
+
+ Mock mockHttpMessageHandler = GetMockHttpMessageHandler(testMetadata);
+ var azureIms = new AzureMetadataRequestor(new HttpClient(mockHttpMessageHandler.Object));
+
+ // ACT
+ var azureImsProps = new AzureComputeMetadataHeartbeatPropertyProvider(azureIms);
+ var hbeatProvider = new HeartbeatProviderMock();
+ var result = await azureImsProps.SetDefaultPayloadAsync(hbeatProvider);
+
+ // VERIFY
+ Assert.True(result);
+ Assert.Empty(hbeatProvider.HbeatProps["azInst_name"]);
+ Assert.Empty(hbeatProvider.HbeatProps["azInst_resourceGroupName"]);
+ Assert.Empty(hbeatProvider.HbeatProps["azInst_subscriptionId"]);
+ }
- tester.Publisher = new string(testStuff);
- var jsonStream = this.GetTestMetadataStream(tester);
- response.SetContentLength(3 * jsonStream.Length);
- response.ContentType = "application/json";
- response.SetContentEncoding(Encoding.UTF8);
- response.WriteStreamToBody(jsonStream);
- }))
- {
- var azureIms = new AzureMetadataRequestor
- {
- BaseAimsUri = string.Concat(AzureInstanceMetadataEndToEndTests.MockTestUri, testPath, "/")
- };
+ [TestMethod]
+ public async Task AzureImsResponseHandlesException()
+ {
+ // SETUP
+ var testMetadata = GetTestMetadata();
+ var mockHttpMessageHandler = GetMockHttpMessageHandler(testMetadata, throwException: true);
+ var azureIms = new AzureMetadataRequestor(new HttpClient(mockHttpMessageHandler.Object));
- var azureIMSData = azureIms.GetAzureComputeMetadataAsync();
- azureIMSData.Wait();
+ // ACT
+ var result = await azureIms.GetAzureComputeMetadataAsync();
- Assert.Null(azureIMSData.Result);
- }
+ // VERIFY
+ Assert.Null(result);
}
[TestMethod]
- public void AzureImsResponseExcludesMalformedValues()
+ public async Task AzureImsResponseUnsuccessful()
{
- string testPath = "malformedValues";
- using (new AzureInstanceMetadataServiceMock(
- AzureInstanceMetadataEndToEndTests.MockTestUri,
- testPath,
- (response) =>
- {
- response.StatusCode = (int)HttpStatusCode.OK;
-
- // make it a malicious-ish response...
- var malformedData = this.GetTestMetadata();
- malformedData.Name = "Not allowed for VM names";
- malformedData.ResourceGroupName = "Not allowed for resource group name";
- malformedData.SubscriptionId = "Definitely-not-a GUID up here";
- var malformedJsonStream = this.GetTestMetadataStream(malformedData);
-
- response.SetContentLength(malformedJsonStream.Length);
- response.ContentType = "application/json";
- response.SetContentEncoding(Encoding.UTF8);
- response.WriteStreamToBody(malformedJsonStream);
- }))
- {
- var azureIms = new AzureMetadataRequestor
- {
- BaseAimsUri = string.Concat(AzureInstanceMetadataEndToEndTests.MockTestUri, testPath, "/")
- };
+ // SETUP
+ var testMetadata = GetTestMetadata();
+ var mockHttpMessageHandler = GetMockHttpMessageHandler(testMetadata, HttpStatusCode.Forbidden);
+ var azureIms = new AzureMetadataRequestor(new HttpClient(mockHttpMessageHandler.Object));
- var azureImsProps = new AzureComputeMetadataHeartbeatPropertyProvider(azureIms);
- var hbeatProvider = new HeartbeatProviderMock();
- var azureIMSData = azureImsProps.SetDefaultPayloadAsync(hbeatProvider);
- azureIMSData.Wait();
+ // ACT
+ var azureIMSData = await azureIms.GetAzureComputeMetadataAsync();
- Assert.Empty(hbeatProvider.HbeatProps["azInst_name"]);
- Assert.Empty(hbeatProvider.HbeatProps["azInst_resourceGroupName"]);
- Assert.Empty(hbeatProvider.HbeatProps["azInst_subscriptionId"]);
- }
+ // VERIFY
+ Assert.Null(azureIMSData);
}
- [TestMethod]
- public void AzureImsResponseTimesOut()
+ ///
+ /// Creates test data for heartbeat e2e.
+ ///
+ /// An Azure Instance Metadata Compute object suitable for use in testing.
+ private static AzureInstanceComputeMetadata GetTestMetadata() => new AzureInstanceComputeMetadata()
+ {
+ Location = "US-West",
+ Name = "test-vm01",
+ Offer = "D9_USWest",
+ OsType = "Linux",
+ PlacementGroupId = "placement-grp",
+ PlatformFaultDomain = "0",
+ PlatformUpdateDomain = "0",
+ Publisher = "Microsoft",
+ ResourceGroupName = "test.resource-group_01",
+ Sku = "Windows_10",
+ SubscriptionId = Guid.NewGuid().ToString(),
+ Tags = "thisTag;thatTag",
+ Version = "10.8a",
+ VmId = Guid.NewGuid().ToString(),
+ VmSize = "A8",
+ VmScaleSetName = "ScaleName"
+ };
+
+ private static string SerializeAsJsonString(AzureInstanceComputeMetadata azureInstanceComputeMetadata)
{
- string testPath = "timeOut";
- using (new AzureInstanceMetadataServiceMock(
- AzureInstanceMetadataEndToEndTests.MockTestUri,
- testPath,
- (response) =>
- {
- // wait for longer than the request timeout
- Thread.Sleep(TimeSpan.FromSeconds(5));
+ DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(AzureInstanceComputeMetadata));
- response.StatusCode = (int)HttpStatusCode.OK;
+ string returnData;
- var jsonStream = this.GetTestMetadataStream();
- response.SetContentLength(jsonStream.Length);
- response.ContentType = "application/json";
- response.SetContentEncoding(Encoding.UTF8);
- response.WriteStreamToBody(jsonStream);
- }))
+ using (MemoryStream memoryStream = new MemoryStream())
{
- var azureIms = new AzureMetadataRequestor
- {
- BaseAimsUri = string.Concat(AzureInstanceMetadataEndToEndTests.MockTestUri, testPath, "/"),
- AzureImsRequestTimeout = TimeSpan.FromSeconds(1)
- };
+ serializer.WriteObject(memoryStream, azureInstanceComputeMetadata);
+ memoryStream.Position = 0;
+ StreamReader sr = new StreamReader(memoryStream);
- var azureIMSData = azureIms.GetAzureComputeMetadataAsync();
- azureIMSData.Wait();
+ returnData = sr.ReadToEnd();
- Assert.Null(azureIMSData.Result);
+ sr.Close();
+ memoryStream.Close();
}
+
+ return returnData;
}
- [TestMethod]
- public void AzureImsResponseUnsuccessful()
+ private static Mock GetMockHttpMessageHandler(AzureInstanceComputeMetadata metadata, HttpStatusCode httpStatusCode = HttpStatusCode.OK, bool throwException = false)
{
- string testPath = "errorForbidden";
+ var json = SerializeAsJsonString(metadata);
- using (new AzureInstanceMetadataServiceMock(
- AzureInstanceMetadataEndToEndTests.MockTestUri,
- testPath,
- (response) =>
- {
- // don't send anything in content at all, or the context defaults to 200 OK
- response.StatusCode = (int)HttpStatusCode.Forbidden;
- }))
+ var mockHttpMessageHandler = new Mock();
+ var response = new HttpResponseMessage
{
- var azureIms = new AzureMetadataRequestor
- {
- BaseAimsUri = string.Concat(AzureInstanceMetadataEndToEndTests.MockTestUri, testPath, "/")
- };
-
- var azureIMSData = azureIms.GetAzureComputeMetadataAsync();
- azureIMSData.Wait();
+ StatusCode = httpStatusCode,
+ Content = new StringContent(json, Encoding.UTF8, "application/json"),
+ };
- Assert.Null(azureIMSData.Result);
+ if (throwException)
+ {
+ mockHttpMessageHandler
+ .Protected()
+ .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny())
+ .Callback(() => throw new Exception("unit test forced exception"));
}
- }
-
- ///
- /// Return a memory stream adequate for testing.
- ///
- /// An Azure instance metadata compute object.
- /// Azure Instance Compute Metadata as a JSON-encoded MemoryStream.
- private MemoryStream GetTestMetadataStream(AzureInstanceComputeMetadata inst = null)
- {
- if (inst == null)
+ else
{
- inst = this.GetTestMetadata();
+ mockHttpMessageHandler
+ .Protected()
+ .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny())
+ .ReturnsAsync(value: response);
}
- DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(AzureInstanceComputeMetadata));
-
- MemoryStream jsonStream = new MemoryStream();
- serializer.WriteObject(jsonStream, inst);
-
- return jsonStream;
- }
-
- ///
- /// Creates test data for heartbeat e2e.
- ///
- /// An Azure Instance Metadata Compute object suitable for use in testing.
- private AzureInstanceComputeMetadata GetTestMetadata()
- {
- return new AzureInstanceComputeMetadata()
- {
- Location = "US-West",
- Name = "test-vm01",
- Offer = "D9_USWest",
- OsType = "Linux",
- PlacementGroupId = "placement-grp",
- PlatformFaultDomain = "0",
- PlatformUpdateDomain = "0",
- Publisher = "Microsoft",
- ResourceGroupName = "test.resource-group_01",
- Sku = "Windows_10",
- SubscriptionId = Guid.NewGuid().ToString(),
- Tags = "thisTag;thatTag",
- Version = "10.8a",
- VmId = Guid.NewGuid().ToString(),
- VmSize = "A8",
- VmScaleSetName = "ScaleName"
- };
+ return mockHttpMessageHandler;
}
}
}
diff --git a/WEB/Src/WindowsServer/WindowsServer.Tests/AzureInstanceMetadataTests.cs b/WEB/Src/WindowsServer/WindowsServer.Tests/AzureInstanceMetadataTests.cs
index 2f17c683c9..97533b9cde 100644
--- a/WEB/Src/WindowsServer/WindowsServer.Tests/AzureInstanceMetadataTests.cs
+++ b/WEB/Src/WindowsServer/WindowsServer.Tests/AzureInstanceMetadataTests.cs
@@ -6,11 +6,16 @@ namespace Microsoft.ApplicationInsights.WindowsServer
using System.Net.Http;
using System.Runtime.Serialization.Json;
using System.Text;
+ using System.Threading;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights.WindowsServer.Implementation;
using Microsoft.ApplicationInsights.WindowsServer.Implementation.DataContracts;
using Microsoft.ApplicationInsights.WindowsServer.Mock;
using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ using Moq;
+ using Moq.Protected;
+
using Assert = Xunit.Assert;
[TestClass]
@@ -291,17 +296,20 @@ public void AzureIMSReturnsExpectedValuesForEachFieldAfterSerialization()
}
[TestMethod]
- public void AzureIMSGetFailsWithException()
+ public async Task AzureIMSGetFailsWithException()
{
- var requestor = new AzureMetadataRequestor(makeAzureIMSRequestor: (string uri) =>
- {
- throw new HttpRequestException("MaxResponseContentLength exceeded");
- });
+ var mockHttpMessageHandler = new Mock();
+ mockHttpMessageHandler
+ .Protected()
+ .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny())
+ .Callback(() => throw new HttpRequestException("unit test forced exception"));
+
+ var requestor = new AzureMetadataRequestor(new HttpClient(mockHttpMessageHandler.Object));
try
{
- var result = requestor.GetAzureComputeMetadataAsync();
- Assert.Null(result.GetAwaiter().GetResult());
+ var result = await requestor.GetAzureComputeMetadataAsync();
+ Assert.Null(result);
}
catch
{
diff --git a/WEB/Src/WindowsServer/WindowsServer.Tests/Mock/AzureInstanceMetadataServiceMock.cs b/WEB/Src/WindowsServer/WindowsServer.Tests/Mock/AzureInstanceMetadataServiceMock.cs
deleted file mode 100644
index 45aed9c9fd..0000000000
--- a/WEB/Src/WindowsServer/WindowsServer.Tests/Mock/AzureInstanceMetadataServiceMock.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-namespace Microsoft.ApplicationInsights.WindowsServer.Mock
-{
- using System;
- using System.Net;
- using System.Threading;
- using System.Threading.Tasks;
-#if NETCOREAPP
- using System.Collections.Generic;
- using Microsoft.AspNetCore.Builder;
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.AspNetCore.Http;
-#endif
-
- internal class AzureInstanceMetadataServiceMock : IDisposable
- {
-#if NETCOREAPP
- private readonly IWebHost host;
-#else
- private readonly HttpListener listener;
-#endif
- private readonly CancellationTokenSource cts;
-
-#if NETCOREAPP
-
- internal AzureInstanceMetadataServiceMock(string baseUrl, string testName, Action onRequest = null)
- {
- ResponseHandlerMock.OnRequestDictionary.Add(testName, onRequest);
-
- this.cts = new CancellationTokenSource();
- this.host = new WebHostBuilder()
- .UseKestrel()
- .UseStartup()
- .UseUrls(baseUrl + "?testName=" + testName)
- .Build();
-
- // Breaking change. This method is not available in NetCoreApp3 and greater
- // Other methods do not have the same behavior and break existing tests.
- // Task.Run(() => this.host.Run(this.cts.Token));
- }
-
-#else
-
- internal AzureInstanceMetadataServiceMock(string url, string testName, Action onRequest = null)
- {
- var alistener = new System.Net.Http.HttpClient();
- this.listener = new HttpListener();
- this.listener.Prefixes.Add(url);
- this.listener.Start();
- this.cts = new CancellationTokenSource();
-
- Task.Run(
- () =>
- {
- if (!this.cts.IsCancellationRequested)
- {
- HttpListenerContext context = this.listener.GetContext();
- if (onRequest != null)
- {
- onRequest(context.Response);
- }
- else
- {
- context.Response.StatusCode = 200;
- }
-
- context.Response.OutputStream.Close();
- context.Response.Close();
- }
- },
- this.cts.Token);
- }
-#endif
-
- public void Dispose()
- {
- this.cts.Cancel(false);
-#if NETCOREAPP
- this.host.Dispose();
-#else
- this.listener.Abort();
- ((IDisposable)this.listener).Dispose();
-#endif
- this.cts.Dispose();
- }
-
-#if NETCOREAPP
-
- public class ResponseHandlerMock
- {
- public static Dictionary> OnRequestDictionary { get; set; } = new Dictionary>();
-
- public void Configure(IApplicationBuilder app)
- {
- app.Run(async (context) =>
- {
- string testPath = context.Request.Path.Value.Trim('/');
-
- if (ResponseHandlerMock.OnRequestDictionary.ContainsKey(testPath))
- {
- Action onRequest = ResponseHandlerMock.OnRequestDictionary[testPath];
- onRequest(context.Response);
- }
- else
- {
- context.Response.StatusCode = 200;
- await context.Response.WriteAsync("Hello World!");
- }
- });
- }
- }
-
-#endif
-
- }
-}
diff --git a/WEB/Src/WindowsServer/WindowsServer.Tests/WindowsServer.Tests.csproj b/WEB/Src/WindowsServer/WindowsServer.Tests/WindowsServer.Tests.csproj
index b082240095..a1d68031b7 100644
--- a/WEB/Src/WindowsServer/WindowsServer.Tests/WindowsServer.Tests.csproj
+++ b/WEB/Src/WindowsServer/WindowsServer.Tests/WindowsServer.Tests.csproj
@@ -13,6 +13,7 @@
+
diff --git a/WEB/Src/WindowsServer/WindowsServer/AzureInstanceMetadataTelemetryModule.cs b/WEB/Src/WindowsServer/WindowsServer/AzureInstanceMetadataTelemetryModule.cs
index 4faf6e0f5b..ae1e6642d1 100644
--- a/WEB/Src/WindowsServer/WindowsServer/AzureInstanceMetadataTelemetryModule.cs
+++ b/WEB/Src/WindowsServer/WindowsServer/AzureInstanceMetadataTelemetryModule.cs
@@ -74,10 +74,7 @@ public void Initialize(TelemetryConfiguration unused)
// to the core event log.
try
{
- var heartbeatProperties = new AzureComputeMetadataHeartbeatPropertyProvider();
- Task.Factory.StartNew(
- async () => await heartbeatProperties.SetDefaultPayloadAsync(hbeatManager)
- .ConfigureAwait(false));
+ Task.Factory.StartNew(async () => await SetDefaultPayloadAsync(hbeatManager).ConfigureAwait(false));
}
catch (Exception heartbeatAquisitionException)
{
@@ -90,5 +87,13 @@ public void Initialize(TelemetryConfiguration unused)
}
}
}
+
+ private static async Task SetDefaultPayloadAsync(IHeartbeatPropertyManager heartbeatPropertyManager)
+ {
+ using (var heartbeatPropertyProvider = new AzureComputeMetadataHeartbeatPropertyProvider())
+ {
+ await heartbeatPropertyProvider.SetDefaultPayloadAsync(heartbeatPropertyManager).ConfigureAwait(false);
+ }
+ }
}
}
diff --git a/WEB/Src/WindowsServer/WindowsServer/Implementation/AzureComputeMetadataHeartbeatPropertyProvider.cs b/WEB/Src/WindowsServer/WindowsServer/Implementation/AzureComputeMetadataHeartbeatPropertyProvider.cs
index 320f5130dd..9806995693 100644
--- a/WEB/Src/WindowsServer/WindowsServer/Implementation/AzureComputeMetadataHeartbeatPropertyProvider.cs
+++ b/WEB/Src/WindowsServer/WindowsServer/Implementation/AzureComputeMetadataHeartbeatPropertyProvider.cs
@@ -6,7 +6,7 @@
using System.Threading.Tasks;
using Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing;
- internal class AzureComputeMetadataHeartbeatPropertyProvider
+ internal class AzureComputeMetadataHeartbeatPropertyProvider : IDisposable
{
internal const string HeartbeatPropertyPrefix = "azInst_"; // to ensure no collisions with base heartbeat properties
@@ -34,13 +34,15 @@ internal class AzureComputeMetadataHeartbeatPropertyProvider
"vmScaleSetName",
};
+ private readonly IAzureMetadataRequestor azureInstanceMetadataRequestor;
+
///
/// Flags that will tell us whether or not Azure VM metadata has been attempted to be gathered or not, and
/// if we should even attempt to look for it in the first place.
///
private bool isAzureMetadataCheckCompleted = false;
- private IAzureMetadataRequestor azureInstanceMetadataRequestor = null;
+ private bool isDisposed;
///
/// Initializes a new instance of the class.
@@ -101,5 +103,28 @@ public async Task SetDefaultPayloadAsync(IHeartbeatPropertyManager provide
return hasSetFields;
}
+
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+ this.Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!this.isDisposed)
+ {
+ if (disposing)
+ {
+ if (this.azureInstanceMetadataRequestor is IDisposable disposableRequestor)
+ {
+ disposableRequestor.Dispose();
+ }
+ }
+
+ this.isDisposed = true;
+ }
+ }
}
}
diff --git a/WEB/Src/WindowsServer/WindowsServer/Implementation/AzureMetadataRequestor.cs b/WEB/Src/WindowsServer/WindowsServer/Implementation/AzureMetadataRequestor.cs
index 07585a23e2..723abe85c9 100644
--- a/WEB/Src/WindowsServer/WindowsServer/Implementation/AzureMetadataRequestor.cs
+++ b/WEB/Src/WindowsServer/WindowsServer/Implementation/AzureMetadataRequestor.cs
@@ -9,7 +9,7 @@
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.WindowsServer.Implementation.DataContracts;
- internal class AzureMetadataRequestor : IAzureMetadataRequestor
+ internal class AzureMetadataRequestor : IAzureMetadataRequestor, IDisposable
{
///
/// Azure Instance Metadata Service exists on a single non-routable IP on machines configured
@@ -19,21 +19,21 @@ internal class AzureMetadataRequestor : IAzureMetadataRequestor
internal const string AzureImsJsonFormat = "format=json";
internal const int AzureImsMaxResponseBufferSize = 512;
- ///
- /// Default timeout for the web requests made to obtain Azure IMS data. Internal to expose to tests.
- ///
- internal TimeSpan AzureImsRequestTimeout = TimeSpan.FromSeconds(10);
+ private readonly HttpClient httpClient;
///
- /// Private function for mocking out the actual call to IMS in unit tests. Available to internal only.
+ /// Default timeout for the web requests made to obtain Azure IMS data.
///
- /// parameter sent to the func is a string representing the Uri to request Azure IMS data from.
- /// An instance of AzureInstanceComputeMetadata or null.
- private Func> azureIMSRequestor = null;
+ private TimeSpan azureImsRequestTimeout = TimeSpan.FromSeconds(10);
+ private bool isDisposed;
- internal AzureMetadataRequestor(Func> makeAzureIMSRequestor = null)
+ public AzureMetadataRequestor(HttpClient httpClient = null)
{
- this.azureIMSRequestor = makeAzureIMSRequestor;
+ this.httpClient = httpClient ?? new HttpClient();
+
+ this.httpClient.MaxResponseContentBufferSize = AzureMetadataRequestor.AzureImsMaxResponseBufferSize;
+ this.httpClient.DefaultRequestHeaders.Add("Metadata", "True");
+ this.httpClient.Timeout = this.azureImsRequestTimeout;
}
///
@@ -56,33 +56,44 @@ public Task GetAzureComputeMetadataAsync()
return this.MakeAzureMetadataRequestAsync(metadataRequestUrl);
}
- private async Task MakeAzureMetadataRequestAsync(string metadataRequestUrl)
+ public void Dispose()
{
- AzureInstanceComputeMetadata requestResult = null;
+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+ this.Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
- SdkInternalOperationsMonitor.Enter();
- try
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!this.isDisposed)
{
- if (this.azureIMSRequestor != null)
- {
- requestResult = await this.azureIMSRequestor(metadataRequestUrl).ConfigureAwait(false);
- }
- else
+ if (disposing)
{
- requestResult = await this.MakeWebRequestAsync(metadataRequestUrl).ConfigureAwait(false);
+ this.httpClient.Dispose();
}
+
+ this.isDisposed = true;
+ }
+ }
+
+ private async Task MakeAzureMetadataRequestAsync(string metadataRequestUrl)
+ {
+ SdkInternalOperationsMonitor.Enter();
+ try
+ {
+ return await this.MakeWebRequestAsync(metadataRequestUrl).ConfigureAwait(false);
}
catch (Exception ex)
{
WindowsServerEventSource.Log.AzureInstanceMetadataRequestFailure(
metadataRequestUrl, ex.Message, ex.InnerException != null ? ex.InnerException.Message : string.Empty);
+
+ return null;
}
finally
{
SdkInternalOperationsMonitor.Exit();
}
-
- return requestResult;
}
private async Task MakeWebRequestAsync(string requestUrl)
@@ -90,20 +101,13 @@ private async Task MakeWebRequestAsync(string requ
AzureInstanceComputeMetadata azureIms = null;
DataContractJsonSerializer deserializer = new DataContractJsonSerializer(typeof(AzureInstanceComputeMetadata));
- using (var azureImsClient = new HttpClient())
- {
- azureImsClient.MaxResponseContentBufferSize = AzureMetadataRequestor.AzureImsMaxResponseBufferSize;
- azureImsClient.DefaultRequestHeaders.Add("Metadata", "True");
- azureImsClient.Timeout = this.AzureImsRequestTimeout;
-
- Stream content = await azureImsClient.GetStreamAsync(new Uri(requestUrl)).ConfigureAwait(false);
- azureIms = (AzureInstanceComputeMetadata)deserializer.ReadObject(content);
- content.Dispose();
+ Stream content = await this.httpClient.GetStreamAsync(new Uri(requestUrl)).ConfigureAwait(false);
+ azureIms = (AzureInstanceComputeMetadata)deserializer.ReadObject(content);
+ content.Dispose();
- if (azureIms == null)
- {
- WindowsServerEventSource.Log.CannotObtainAzureInstanceMetadata();
- }
+ if (azureIms == null)
+ {
+ WindowsServerEventSource.Log.CannotObtainAzureInstanceMetadata();
}
return azureIms;