Configure HttpClientFactory to Use Fiddler in .NET Core Microservices-based Docker Containers

joni2nja

Joni 【ジョニー】

Posted on June 15, 2019

Configure HttpClientFactory to Use Fiddler in .NET Core Microservices-based Docker Containers

This post originally appeared on Medium

Ever wonder how to configure your Dockerized applications to use Fiddler?

If all of the following conditions are true, then this post might help you. Hopefully.

  • You are developing microservices-based applications using .NET Core 2.1 or above
  • You develop using Docker Desktop for Windows (minimum version: 18.03)
  • You are using HttpClientFactory, an opinionated factory for creating HttpClient instances
  • You love Fiddler and trying to configure your HttpClient to use Fiddler as the debugging proxy

Let’s say our Docker container application has the following HttpClientFactory setup in Startup.cs:

services.AddHttpClient("mySuperSplendidMarvelousAmazingService")
  .SetHandlerLifetime(TimeSpan.FromMinutes(5))
  .AddPolicyHandler(GetRetryPolicy())
  .AddPolicyHandler(GetCircuitBreakerPolicy());

Note that AddPolicyHandler(...) might be a typical Microservices pattern you can possibly use. See “.NET Microservices: Architecture for Containerized .NET Applications” references.

Now, to configure the HttpClient to “talk” to the Fiddler on the host port 8888 (Fiddler listens on port 8888 by default), we will need to know the host IP address. Thanks to Docker v18.03, we can use the special DNS name host.docker.internal.

Refer to the documentation for more details:

Networking features in Docker Desktop for Windows

So let’s use that special DNS:

services.AddHttpClient("mySuperSplendidMarvelousAmazingService")
  .SetHandlerLifetime(TimeSpan.FromMinutes(5))
// ADD THIS 👇👇👇
  .ConfigurePrimaryHttpMessageHandler(() =>  
  {  
      return new HttpClientHandler  
      {  
          Proxy = new WebProxy("http://host.docker.internal:8888"),  
          UseProxy = true  
      };  
  }) 
// --------- 👆👆👆
  .AddPolicyHandler(GetRetryPolicy())
  .AddPolicyHandler(GetCircuitBreakerPolicy());

That’s it! Now we should be able to inspect the requests/responses in Fiddler.

But wait 🛑⛔🚫!! We don’t want to ship that debugging code to the production, right? Of course not. But sometimes we just want to be able to use it during debugging with a simple flip. Let’s explore some possible solutions:

  • Use Preprocessor Directive
  • Use ConditionalAttribute

Use Preprocessor Directive

We accomplish this by using a C# preprocessor directive:

services.AddHttpClient("mySuperSplendidMarvelousAmazingService")
  .SetHandlerLifetime(TimeSpan.FromMinutes(5))
#if DEBUG 😎
  .ConfigurePrimaryHttpMessageHandler(() =>
  {
      return new HttpClientHandler
      {
          Proxy = new WebProxy("http://host.docker.internal:8888"),
          UseProxy = true
      };
  })
#endif
  .AddPolicyHandler(GetRetryPolicy())
  .AddPolicyHandler(GetCircuitBreakerPolicy());

This way, on the production build, it won’t be compiled into Microsoft Intermediate Language (MSIL, or IL for short).

Looks good. Until it’s not. It will fail if we forget to launch Fiddler every time we start our application in the debug mode. So, a better alternative, define and use our own custom symbol, for example: ENABLE_FIDDLER_DEBUG_PROXY:

services.AddHttpClient("mySuperSplendidMarvelousAmazingService")
  .SetHandlerLifetime(TimeSpan.FromMinutes(5))
#if ENABLE_FIDDLER_DEBUG_PROXY 😍
  .ConfigurePrimaryHttpMessageHandler(() =>
  {
      return new HttpClientHandler
      {
          Proxy = new WebProxy("http://host.docker.internal:8888"),
          UseProxy = true
      };
  })
#endif
  .AddPolicyHandler(GetRetryPolicy())
  .AddPolicyHandler(GetCircuitBreakerPolicy());

And define it only when we really need it:

Awesome! 👍

Use ConditionalAttribute

Another possible solution, make use of ConditionalAttribute. Let’s add an extension method:

public static class FiddlerExtensions
{
[Conditional("ENABLE_FIDDLER_DEBUG_PROXY")]
  public static void AddFiddler(this IHttpClientBuilder builder)
  {
    builder.ConfigurePrimaryHttpMessageHandler(() =>
    {
      return new HttpClientHandler
      {
        Proxy = new WebProxy("http://host.docker.internal:8888"),
        UseProxy = true
      };
    });
  }
}

and use it like:

var builder = services.AddHttpClient("mySuperSplendidMarvelousAmazingService")
  .SetHandlerLifetime(TimeSpan.FromMinutes(5))
  .AddPolicyHandler(GetRetryPolicy())
  .AddPolicyHandler(GetCircuitBreakerPolicy());
builder.AddFiddler();

The beauty of this is, the builder.AddFidler() won’t be included in the IL if we don’t have ENABLE_FIDDLER_DEBUG_PROXY defined.

Now, the ugly part, we can’t continue chaining the method calls because it will end up being removed from the IL altogether, thus we are introducing a variable builder here. You can use ILSpy or dnSpy to disassemble and verify this behavior.

Bonus tips!

There is another cool trick for debugging: Debugger.IsAttached

Gets a value that indicates whether a debugger is attached to the process.

if (Debugger.IsAttached) { ... }

This might be useful for some certain scenarios, but I don’t think I will use it for this scenario.

What’s your solution?

💖 💪 🙅 🚩
joni2nja
Joni 【ジョニー】

Posted on June 15, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related