question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Add support to use externally provided HttpClient/HttpClientHandler

See original GitHub issue

API’s involved

There are 2 existing API mechanisms which could be used to achieve this.

BindingParameterCollection

A developer could create a class which implements IEndpointBehavior. In the AddBindingParameters method, they would add the relevant instance to the BindingParameterCollection. This would look like this:

    public class UseMyHttpClientEndpointBehavior : IEndpointBehavior
    {
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
            HttpClientHandler httpClientHandler = new HttpClientHandler();
            // Set any properties on httpClientHandler
            bindingParameters.Add(new HttpClient(httpClientHandler));
        }
        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }
        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }
        public void Validate(ServiceEndpoint endpoint) { }
    }

This is a naïve implementation, e.g. HttpClient needs to have timeouts disabled to prevent contention on the global TimerManager, but it shows the general pattern.

MessageProperties

A developer could add an outgoing message property for the relevant instance to the OutgoingMessageProperties of an OperationContext. This could be done either with a class which implements IClientMessageInspector or with the use of OperationContextScope. The latter would look like this:

    HttpClientHandler httpClientHandler = new HttpClientHandler();
    // Set any properties on httpClientHandler
    HttpClient httpClient = new HttpClient();
    using(new OperationContextScope((IContextChannel)myClient))
    {
        OperationContext.Current.OutgoingMessageProperties.Add("System.Net.Http.HttpClient", httpClient);
        myClient.DoWork();
    }

Which class(es) to support

We have multiple options about which class(es) we could potentially support being added to a BindingParameterCollection or OutgoingMessageProperties, each having their own limitations. We would be unable to modify any parameters on any passed in objects for 2 reasons. 1) Any property we might set could potentially be something which is intended to be overridden. 2) There are use cases where the passed in object might have already been used so is now immutable.

System.Net.Http.HttpClient

WCF sets HttpClient.Timeout to infinite to prevent a timer being registered for each request. We have logic to coalesce CancellationToken timers to prevent high contention on the global timer queue. Without setting this value, HttpClient will cause a lot of contention.

System.Net.Http.HttpMessageInvoker

This is the base class to HttpClient. It lacks a lot of the helper api’s that HttpClient provides such as the verb specific request api’s, e.g. GetAsync and PostAsync. WCF only needs to use SendAsync so this shouldn’t be an issue. Although we don’t currently do this, WCF could make use of HttpClient.DefaultRequestHeaders for the headers which will always be the same for all requests from the same HttpChannelFactory. If we decide to support the use of a single instance of HttpClient with multiple ChannelFactory instances, then we can’t use DefaultRequestHeaders.

System.Net.Http.HttpClientHandler

There are many properties on this class which WCF sets. One of the more important ones is Credentials. A developer would need to set the credentials on HttpClientHandler themselves. We would still be able to set any properties on the HttpClient instance we would create to wrap the HttpClientHandler. Supporting allowing this class to be provided would limit developers to using client implementations which ship with the framework.

System.Net.Http.HttpMessageHandler

This is the base class for HttpClientHandler and is the type that the constructor for HttpClient accepts. This has none of the properties that HttpClientHandler exposes, but as we can’t modify any properties as explained earlier, this does not present any additional problems. This would enable developers to provide any implementation of Http which works with HttpClient.

System.Net.Http.DelegatingHandler

This is a special class which allows to provide an existing HttpMessageHandler to be wrapped and potentially provide custom behavior. There is a property InnerHandler which would allow WCF to set the HttpClientHandler that we create on the DelegatingHandler. Presuming the constraint of only being able to give WCF a delegating handler which has not been used yet, we would be able to provide an unused HttpClientHandler to the delegating handler which would be able to modify any properties before first usage. This would allow WCF to set credentials etc on an HttpClientHandler while also allowing a developer to modify everything as much as they wish.

Decisions to be made

  • Which of the extensibility methods do we want to support?
  • Which class types do we want to support being provided?
  • What constraints are acceptable? E.g. can instances be shared with multiple ChannelFactory’s?

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:13 (7 by maintainers)

github_iconTop GitHub Comments

2reactions
mconnewcommented, Dec 15, 2017

Thinking about it a bit more, it could be a bit simpler than that. We could use a Func<HttpClientHandler, HttpMessageHandler> so we pass our HttpClientHandler with everything configured how we want to the Func and that returns an HttpMessageHandler which we then use to construct an HttpClient. The factory Func can ignore the HttpClientHandler, modify it, or wrap it in a DelegatingHandler and use it as is and just modify the requests.

0reactions
mconnewcommented, Feb 22, 2018

This change has now been merged in PR #2534. An example behavior using this feature is available here.

Read more comments on GitHub >

github_iconTop Results From Across the Web

No results found

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found