Skip to content

Commit

Permalink
Adding constructor that takes HttpClient (#2918)
Browse files Browse the repository at this point in the history
* Adding constructor that takes HttpClient. Adding tests. Fixing OperationalInisghts failing tests

* Merging UserAgent info if provided by the passed in HttpClient. Restructured tests and added more tests

* Incorporating feedback

* making sure we are not initializing handlers unnecessarily when httpClient is passed in
  • Loading branch information
shahabhijeet authored Mar 16, 2017
1 parent 867fe9d commit 7a042f6
Show file tree
Hide file tree
Showing 8 changed files with 447 additions and 48 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

namespace Microsoft.Rest.ClientRuntime.Tests
{
using Microsoft.Rest.ClientRuntime.Tests.CustomClients;
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

public class CustomClientWithHttpClientTests
{
const string MY_PRODUCT = "MyProduct";
const string MY_VERSION = "MyVersion";
const string DEFAULT_URI = "http://www.microsoft.com";

/// <summary>
/// Basic test to pass in simple HttpClient to the ServiceClient (Contoso)
/// </summary>
[Fact]
public void InitializeServiceClientWithHttpClient()
{
HttpClient hc = new HttpClient();
ContosoServiceClient contosoClient = new ContosoServiceClient(hc);
HttpResponseMessage response = contosoClient.DoSyncWork();
Assert.NotNull(response);
}

/// <summary>
/// Pass in null HttpClient and get default HttpClient
/// Verify if baseClient httpClient has the default information
/// </summary>
[Fact]
public void GetBaseClassHttpClient()
{
string defaultProductName = "FxVersion";
Version defaultProductVer;

ContosoServiceClient contosoClient = new ContosoServiceClient(null);
HttpResponseMessage response = contosoClient.DoSyncWork();
string cont = response.Content.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
Assert.NotNull(response);

HttpHeaderValueCollection<ProductInfoHeaderValue> userAgentValueCollection = contosoClient.HttpClient.DefaultRequestHeaders.UserAgent;
var dP = userAgentValueCollection.Where<ProductInfoHeaderValue>((p) => p.Product.Name.Equals(defaultProductName)).FirstOrDefault<ProductInfoHeaderValue>();
Version.TryParse(dP.Product.Version, out defaultProductVer);
Assert.Equal(defaultProductName, dP.Product.Name);
Assert.NotNull(defaultProductVer);
}

/// <summary>
/// Creates Constoso Client with it's own handler
/// </summary>
[Fact]
public void InitializeMessageHandlerPostContructor()
{
HttpClient hc = new HttpClient(new ContosoMessageHandler());
ContosoServiceClient contosoClient = new ContosoServiceClient(hc);
HttpResponseMessage response = contosoClient.DoSyncWork();
string cont = response.Content.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
Assert.Equal("Contoso Rocks", cont);
}

/// <summary>
/// This test will create contoso client with delayedHandler
/// </summary>
[Fact]
public void ProvideHttpClientAfterInitialization()
{
ContosoServiceClient contosoClient = new ContosoServiceClient(overRideDefaultHandler: true);
HttpResponseMessage response = contosoClient.DoSyncWork();
string cont = response.Content.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
Assert.Equal("Delayed User Provided HttpClient after initialization", cont);
}

/// <summary>
/// The purpose of this test is to verify if we use httpClient prior to
/// creating service client, everything works as expected
/// Also verifies if any properties set prior to constructing ServiceClient
/// The provided httpClient will not change it's properties
/// </summary>
[Fact]
public void UseHttpClientBeforeConstructingServiceClient()
{
HttpClient hc = new HttpClient(new ContosoMessageHandler());
hc.BaseAddress = new Uri(DEFAULT_URI);
HttpResponseMessage resMsg = SendAndReceiveResponse(hc);
Assert.Equal(HttpStatusCode.OK, resMsg.StatusCode);

ContosoServiceClient contosoClient = new ContosoServiceClient(hc);
HttpResponseMessage response = contosoClient.DoSyncWork();
string cont = response.Content.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
Assert.Equal("Contoso Rocks", cont);
Assert.Equal(new Uri(DEFAULT_URI), contosoClient.HttpClient.BaseAddress);
}

/// <summary>
/// THe HttpClient that is provided to ServiceClient will have it's own set of UserAgent information
/// inside default headers. This is to verify if we merge DefaultHeader information
/// </summary>
[Fact]
public void AddHeaderInformationToHttpClient()
{
Version defaultVersion = null;
HttpClient hc = new HttpClient(new ContosoMessageHandler());
hc.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(MY_PRODUCT, MY_VERSION));
ContosoServiceClient contosoClient = new ContosoServiceClient(hc);
HttpResponseMessage response = contosoClient.DoSyncWork();

HttpHeaderValueCollection<ProductInfoHeaderValue> userAgentValueCollection = contosoClient.HttpClient.DefaultRequestHeaders.UserAgent;
ProductInfoHeaderValue myProduct = userAgentValueCollection.Where<ProductInfoHeaderValue>((p) => p.Product.Name.Equals(MY_PRODUCT, StringComparison.OrdinalIgnoreCase)).First<ProductInfoHeaderValue>();
Assert.Equal(MY_VERSION, myProduct.Product.Version);

ProductInfoHeaderValue fxVer = userAgentValueCollection.Where<ProductInfoHeaderValue>((p) => p.Product.Name.Equals("FxVersion", StringComparison.OrdinalIgnoreCase)).First<ProductInfoHeaderValue>();
Version.TryParse(fxVer.Product.Version, out defaultVersion);
Assert.NotNull(defaultVersion);
}

/// <summary>
/// This is to verify if a HttpClient is passed, we still add default userAgent information
/// inside defaultheaders of the passed in HttpClient
/// </summary>
[Fact]
public void AddFxVersionHeaderInformation()
{
Version defaultVersion = null;
HttpClient hc = new HttpClient(new ContosoMessageHandler());
hc.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("FxVersion", "1.0.0.0"));
ContosoServiceClient contosoClient = new ContosoServiceClient(hc);
HttpResponseMessage response = contosoClient.DoSyncWork();

HttpHeaderValueCollection<ProductInfoHeaderValue> userAgentValueCollection = contosoClient.HttpClient.DefaultRequestHeaders.UserAgent;
ProductInfoHeaderValue fxVer = userAgentValueCollection.Where<ProductInfoHeaderValue>((p) => p.Product.Name.Equals("FxVersion", StringComparison.OrdinalIgnoreCase)).First<ProductInfoHeaderValue>();
Version.TryParse(fxVer.Product.Version, out defaultVersion);
Assert.Equal(defaultVersion.ToString(), "1.0.0.0");
}


private HttpResponseMessage SendAndReceiveResponse(HttpClient httpClient)
{
// Construct URL
//string url = "http://www.microsoft.com";

// Create HTTP transport objects
HttpRequestMessage _httpRequest = null;

_httpRequest = new HttpRequestMessage();
_httpRequest.Method = HttpMethod.Get;
_httpRequest.RequestUri = new Uri(DEFAULT_URI);

// Set Headers
_httpRequest.Headers.Add("x-ms-version", "2013-11-01");

return Task.Run<HttpResponseMessage>(async () => await httpClient.SendAsync(_httpRequest, new CancellationToken()).ConfigureAwait(false)).ConfigureAwait(false).GetAwaiter().GetResult();

}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

namespace Microsoft.Rest.ClientRuntime.Tests.CustomClients
{
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

/// <summary>
/// Customized client that emulates how partners will use Customized code to extend
/// generated client.
/// </summary>
public class ContosoServiceClient : ServiceClient<ContosoServiceClient>
{
/// <summary>
/// Initializes with default contosomessage handler
/// </summary>
public ContosoServiceClient():base()
{
HttpClient = new HttpClient(new ContosoMessageHandler());
}

/// <summary>
/// Overrides default handler (ContosoMessageHandler) with DelayedHandler if specified
/// </summary>
/// <param name="overRideDefaultHandler"></param>
public ContosoServiceClient(bool overRideDefaultHandler)
{
if(overRideDefaultHandler)
{
HttpClient = new HttpClient(new DelayedHandler("Delayed User Provided HttpClient after initialization"));
}
else
{
HttpClient = new HttpClient(new ContosoMessageHandler());
}
}

/// <summary>
/// Constructor that accepts HttpClient
/// </summary>
/// <param name="httpClient"></param>
public ContosoServiceClient(HttpClient httpClient) : base (httpClient)
{

}

public ContosoServiceClient(HttpClientHandler rootHandler, DelegatingHandler[] handlers)
: base(rootHandler, handlers)
{ }

/// <summary>
/// Some task emulating getting response back
/// </summary>
/// <param name="content"></param>
/// <returns></returns>
public HttpResponseMessage DoSyncWork(string content = null)
{
return Task.Factory.StartNew(() =>
{
return DoStuff(content);
}).Unwrap().GetAwaiter().GetResult();
}

/// <summary>
/// Creates request and sends
/// </summary>
/// <param name="content">string value</param>
/// <returns></returns>
private async Task<HttpResponseMessage> DoStuff(string content = null)
{
// Construct URL
string url = "http://www.microsoft.com";

// Create HTTP transport objects
HttpRequestMessage _httpRequest = null;

_httpRequest = new HttpRequestMessage();
_httpRequest.Method = HttpMethod.Get;
_httpRequest.RequestUri = new Uri(url);

// Set content
if (content != null)
{
_httpRequest.Content = new StringContent(content);
}

// Set Headers
_httpRequest.Headers.Add("x-ms-version", "2013-11-01");

return await this.HttpClient.SendAsync(_httpRequest, new CancellationToken()).ConfigureAwait(false);
}
}

/// <summary>
/// Custom message handler
/// </summary>
public class ContosoMessageHandler : DelegatingHandler
{
public ContosoMessageHandler() : base()
{
InnerHandler = new HttpClientHandler();
}

/// <summary>
/// Returns Contoso Rocks response
/// </summary>
/// <param name="request">HttpRequestMessage object</param>
/// <param name="cancellationToken">CancellationToken</param>
/// <returns></returns>
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
StringContent contosoContent = new StringContent("Contoso Rocks");
HttpResponseMessage response = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
response.Content = contosoContent;
return await Task.Run(() => response);
}
}

/// <summary>
/// Yet another delegating handler for tests
/// </summary>
public class DelayedHandler : DelegatingHandler
{
string _handlerData;
private DelayedHandler() : base()
{
InnerHandler = new HttpClientHandler();
}

public DelayedHandler(string handlerData)
: this()
{
_handlerData = handlerData;
}

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
StringContent contosoContent = new StringContent(_handlerData);
HttpResponseMessage response = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
response.Content = contosoContent;
return await Task.Run(() => response);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ public FakeServiceClient()
// Prevent base constructor from executing
}

public FakeServiceClient(HttpClient httpClient)
: base(httpClient)
{ }

public FakeServiceClient(HttpClientHandler httpMessageHandler, params DelegatingHandler[] handlers)
: base(httpMessageHandler, handlers)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,8 @@
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<ItemGroup>
<DnxInvisibleCompile Include="CustomClients\FabrikamServiceClient.cs" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using Microsoft.Rest.ClientRuntime.Tests.CustomClients;
using Microsoft.Rest.ClientRuntime.Tests.Fakes;
using Microsoft.Rest.TransientFaultHandling;
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using Microsoft.Rest.ClientRuntime.Tests.Fakes;
using Microsoft.Rest.TransientFaultHandling;
using Xunit;
using System.Net.Http.Headers;
using System.Diagnostics;
using Xunit;

namespace Microsoft.Rest.ClientRuntime.Tests
{
Expand Down Expand Up @@ -109,6 +109,16 @@ public void RetryHandlerRetriesWith500Errors()
Assert.Equal(2, attemptsFailed);
}

[Fact]
public void FakeSvcClientWithHttpClient()
{
HttpClient hc = new HttpClient(new ContosoMessageHandler());
var fakeClient = new FakeServiceClient(hc);
HttpResponseMessage response = fakeClient.DoStuffSync();
string responseContent = response.Content.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
Assert.Equal("Contoso Rocks", responseContent);
}

[Fact]
public void RetryHandlerRetriesWith500ErrorsAndSucceeds()
{
Expand Down Expand Up @@ -214,7 +224,7 @@ public void AddUserAgentInfoWithVersion()
Assert.True(testProduct.Product.Name.Equals(testProductName));
Assert.True(testProduct.Product.Version.Equals(testProductVersion));
}

#if NET451
[Fact]
public void VerifyOsInfoInUserAgent()
Expand Down
Loading

0 comments on commit 7a042f6

Please sign in to comment.