Skip to content

Commit

Permalink
Make SecureConversation inner channel factory share the same HttpClie…
Browse files Browse the repository at this point in the history
…nt as outer factory (#4438)

* Make SecureConversation inner channel factory share the same HttpClient as outer factory

* Fix lock semantics ref counting in mrucache

Co-authored-by: Matt Connew <matt.connew@microsoft.com>
  • Loading branch information
mconnew and Matt Connew authored Jan 12, 2021
1 parent 491d7ff commit 4dc0f68
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.


using System.Collections.Generic;
using System.ServiceModel;

Expand All @@ -17,12 +16,29 @@ internal class MruCache<TKey, TValue> : IDisposable
private readonly int _lowWatermark;
private readonly int _highWatermark;
private CacheEntry _mruEntry;
private int _refCount = 1;
private object _mutex = new object();

public MruCache(int watermark)
: this(watermark * 4 / 5, watermark)
{
}

// Returns whether the cache can be used by the caller after calling AddRef
public bool AddRef()
{
lock(_mutex)
{
if (_refCount == 0)
{
return false;
}

_refCount++;
return true;
}
}

//
// The cache will grow until the high watermark. At which point, the least recently used items
// will be purge until the cache's size is reduced to low watermark
Expand Down Expand Up @@ -202,17 +218,29 @@ public bool TryGetValue(TKey key, out TValue value)

public void Dispose()
{
Dispose(true);
int refCount;
lock (_mutex)
{
refCount = --_refCount;
}
Fx.Assert(refCount >= 0, "Ref count shouldn't go below zero");
if (refCount == 0)
{
Dispose(true);
}
}

protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (!IsDisposed)
lock (_mutex)
{
IsDisposed = true;
Clear(true);
if (!IsDisposed)
{
IsDisposed = true;
Clear(true);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ internal HttpChannelFactory(HttpTransportBindingElement bindingElement, BindingC
WebSocketSettings = WebSocketHelper.GetRuntimeWebSocketSettings(bindingElement.WebSocketSettings);
_clientWebSocketFactory = ClientWebSocketFactory.GetFactory();
_webSocketSoapContentType = new Lazy<string>(() => MessageEncoderFactory.CreateSessionEncoder().ContentType, LazyThreadSafetyMode.ExecutionAndPublication);
_httpClientCache = bindingElement.GetProperty<MruCache<string, HttpClient>>(context);
}

public bool AllowCookies { get; }
Expand Down Expand Up @@ -255,17 +256,6 @@ internal async Task<HttpClient> GetHttpClientAsync(EndpointAddress to, Uri via,
NetworkCredential credential = await HttpChannelUtilities.GetCredentialAsync(AuthenticationScheme,
tokenProvider, impersonationLevelWrapper, authenticationLevelWrapper, timeout);

if (_httpClientCache == null)
{
lock (ThisLock)
{
if (_httpClientCache == null)
{
_httpClientCache = new MruCache<string, HttpClient>(10);
}
}
}

HttpClient httpClient;

string connectionGroupName = GetConnectionGroupName(credential, authenticationLevelWrapper.Value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Net;
using System.Net.Http;
using System.Net.Security;
using System.Runtime;
using System.Security.Authentication.ExtendedProtection;
Expand All @@ -29,6 +30,7 @@ public class HttpTransportBindingElement
private WebSocketTransportSettings _webSocketSettings;
private ExtendedProtectionPolicy _extendedProtectionPolicy;
private int _maxPendingAccepts;
private MruCache<string, HttpClient> _httpClientCache;

public HttpTransportBindingElement()
: base()
Expand Down Expand Up @@ -395,6 +397,11 @@ public override T GetProperty<T>(BindingContext context)
{
return (T)(object)new TransportCompressionSupportHelper();
}
else if (typeof(T) == typeof(MruCache<string, HttpClient>))
{
EnsureHttpClientCache();
return (T)(object)_httpClientCache;
}
else
{
Contract.Assert(context.BindingParameters != null);
Expand All @@ -406,6 +413,16 @@ public override T GetProperty<T>(BindingContext context)
}
}

private MruCache<string, HttpClient> EnsureHttpClientCache()
{
if (_httpClientCache == null || !_httpClientCache.AddRef())
{
_httpClientCache = new MruCache<string, HttpClient>(10);
}

return _httpClientCache;
}

public override bool CanBuildChannelFactory<TChannel>(BindingContext context)
{
if (typeof(TChannel) == typeof(IRequestChannel))
Expand Down

0 comments on commit 4dc0f68

Please sign in to comment.