Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #42: Added support for a custom HttpMessageHandler #74

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Microsoft.Azure.Cosmos/src/CosmosClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ internal CosmosClient(CosmosClientConfiguration cosmosClientConfiguration)
transportClientHandlerFactory: cosmosClientConfiguration.TransportClientHandlerFactory,
connectionPolicy: cosmosClientConfiguration.GetConnectionPolicy(),
enableCpuMonitor: cosmosClientConfiguration.EnableCpuMonitor,
storeClientFactory: cosmosClientConfiguration.StoreClientFactory);
storeClientFactory: cosmosClientConfiguration.StoreClientFactory,
handler: cosmosClientConfiguration.HttpMessageHandler);

Init(
cosmosClientConfiguration,
Expand Down
17 changes: 17 additions & 0 deletions Microsoft.Azure.Cosmos/src/CosmosClientBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

using System.Net.Http;

namespace Microsoft.Azure.Cosmos
{
using System;
Expand Down Expand Up @@ -212,6 +214,21 @@ public virtual CosmosClientBuilder UseThrottlingRetryOptions(TimeSpan maxRetryWa
return this;
}

/// <summary>
/// Sets the HTTP handler stack to use for sending requests.
/// </summary>
/// <param name="handler">The HTTP handler stack to use for sending requests</param>
/// <remarks>
/// A custom HttpMessageHandler (e.g., HttpClientHandler) may be used to override SSL self-signed certificate verification when connecting to the Cosmos DB emulator
/// in a development environment. It may also be used to specify an HTTP proxy.
/// </remarks>
/// <seealso cref="System.Net.Http.HttpMessageHandler"/>
public virtual CosmosClientBuilder UseHttpMessageHandler(HttpMessageHandler handler)
{
this.cosmosClientConfiguration.HttpMessageHandler = handler;
return this;
}

/// <summary>
/// Set a custom JSON serializer.
/// </summary>
Expand Down
7 changes: 7 additions & 0 deletions Microsoft.Azure.Cosmos/src/CosmosClientConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

using System.Net.Http;

namespace Microsoft.Azure.Cosmos
{
using System;
Expand Down Expand Up @@ -280,6 +282,11 @@ public virtual CosmosJsonSerializer CosmosJsonSerializer
/// </summary>
internal IStoreClientFactory StoreClientFactory { get; set; }

/// <summary>
/// The HTTP handler stack to use for sending requests (e.g., HttpClientHandler)
/// </summary>
internal HttpMessageHandler HttpMessageHandler { get; set; }

/// <summary>
/// Flag that controls whether CPU monitoring thread is created to enrich timeout exceptions with additional diagnostic. Default value is true.
/// </summary>
Expand Down
56 changes: 52 additions & 4 deletions Microsoft.Azure.Cosmos/src/DocumentClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,10 @@ internal partial class DocumentClient : IDisposable, IAuthorizationTokenProvider

private JsonSerializerSettings serializerSettings;
private event EventHandler<SendingRequestEventArgs> sendingRequest;
private event EventHandler<ReceivedResponseEventArgs> receivedResponse;
private event EventHandler<ReceivedResponseEventArgs> receivedResponse;

/// This will form the inner handler for HttpClient requests
private HttpMessageHandler innerHandler;
private Func<TransportClient, TransportClient> transportClientHandlerFactory;

//Callback for on execution of scalar LINQ queries event.
Expand Down Expand Up @@ -334,6 +337,47 @@ public DocumentClient(Uri serviceEndpoint,
{
}

/// <summary>
/// Initializes a new instance of the <see cref="DocumentClient"/> class using the
/// specified service endpoint, an authorization key (or resource token) a connection policy
/// and a custom HttpMessageHandler
/// for the Azure Cosmos DB service.
/// </summary>
/// <param name="serviceEndpoint">The service endpoint to use to create the client.</param>
/// <param name="authKeyOrResourceToken">The authorization key or resource token to use to create the client.</param>
/// <param name="handler">The HTTP handler stack to use for sending requests (e.g., HttpClientHandler)</param>
/// <param name="connectionPolicy">(Optional) The connection policy for the client.</param>
/// <param name="desiredConsistencyLevel">(Optional) The default consistency policy for client operations.</param>
/// <remarks>
/// The service endpoint can be obtained from the Azure Management Portal.
/// If you are connecting using one of the Master Keys, these can be obtained along with the endpoint from the Azure Management Portal
/// If however you are connecting as a specific Azure Cosmos DB User, the value passed to <paramref name="authKeyOrResourceToken"/> is the ResourceToken obtained from the permission feed for the user.
/// <para>
/// Using Direct connectivity, wherever possible, is recommended.
/// </para>
/// <para>
/// A custom HttpMessageHandler may be used to override SSL self-signed certificate verification when connecting to the Cosmos DB emulator
/// in a development environment, or for specifying an HTTP proxy.
/// </para>
/// </remarks>
/// <seealso cref="Uri"/>
/// <seealso cref="ConnectionPolicy"/>
/// <seealso cref="ConsistencyLevel"/>
public DocumentClient(Uri serviceEndpoint,
string authKeyOrResourceToken,
HttpMessageHandler handler,
ConnectionPolicy connectionPolicy = null,
ConsistencyLevel? desiredConsistencyLevel = null)
: this(serviceEndpoint, authKeyOrResourceToken, sendingRequestEventArgs: null, connectionPolicy: connectionPolicy, desiredConsistencyLevel: desiredConsistencyLevel)
{
if (handler == null)
{
throw new ArgumentNullException("handler");
}

this.innerHandler = handler;
}

/// <summary>
/// Initializes a new instance of the <see cref="DocumentClient"/> class using the
/// specified service endpoint, an authorization key (or resource token) and a connection policy
Expand All @@ -349,6 +393,7 @@ public DocumentClient(Uri serviceEndpoint,
/// <param name="serializerSettings">The custom JsonSerializer settings to be used for serialization/derialization.</param>
/// <param name="apitype">Api type for the account</param>
/// <param name="sessionContainer">The default session container with which DocumentClient is created</param>
/// <param name="handler">The HTTP handler stack to use for sending requests (e.g., HttpClientHandler)</param>
/// <param name="enableCpuMonitor">Flag that indicates whether client-side CPU monitoring is enabled for improved troubleshooting.</param>
/// <param name="storeClientFactory">Factory that creates store clients sharing the same transport client to optimize network resource reuse across multiple document clients in the same process.</param>
/// <remarks>
Expand All @@ -372,6 +417,7 @@ internal DocumentClient(Uri serviceEndpoint,
EventHandler<ReceivedResponseEventArgs> receivedResponseEventArgs = null,
ISessionContainer sessionContainer = null,
Func<TransportClient, TransportClient> transportClientHandlerFactory = null,
HttpMessageHandler handler = null,
bool? enableCpuMonitor = null,
IStoreClientFactory storeClientFactory = null)
{
Expand Down Expand Up @@ -407,6 +453,7 @@ internal DocumentClient(Uri serviceEndpoint,
this.authKeyHashFunction = new StringHMACSHA256Hash(authKeyOrResourceToken);
}

this.innerHandler = handler;
this.transportClientHandlerFactory = transportClientHandlerFactory;
this.Initialize(serviceEndpoint: serviceEndpoint,
connectionPolicy: connectionPolicy,
Expand Down Expand Up @@ -917,7 +964,7 @@ internal virtual void Initialize(Uri serviceEndpoint,

this.globalEndpointManager = new GlobalEndpointManager(this, this.connectionPolicy);

this.httpMessageHandler = new HttpRequestMessageHandler(this.sendingRequest, this.receivedResponse);
this.httpMessageHandler = new HttpRequestMessageHandler(this.sendingRequest, this.receivedResponse, innerHandler);

this.mediaClient = new HttpClient(this.httpMessageHandler);

Expand Down Expand Up @@ -6894,11 +6941,12 @@ private class HttpRequestMessageHandler : DelegatingHandler
private readonly EventHandler<SendingRequestEventArgs> sendingRequest;
private readonly EventHandler<ReceivedResponseEventArgs> receivedResponse;

public HttpRequestMessageHandler(EventHandler<SendingRequestEventArgs> sendingRequest, EventHandler<ReceivedResponseEventArgs> receivedResponse)
public HttpRequestMessageHandler(EventHandler<SendingRequestEventArgs> sendingRequest, EventHandler<ReceivedResponseEventArgs> receivedResponse, HttpMessageHandler innerHandler = null)
{
this.sendingRequest = sendingRequest;
this.receivedResponse = receivedResponse;
InnerHandler = new HttpClientHandler();

InnerHandler = innerHandler ?? new HttpClientHandler();
}

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
Expand Down