Configure HttpClientFactory to Use Fiddler in .NET Core Microservices-based Docker Containers
Joni 【ジョニー】
Posted on June 15, 2019
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?
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
June 15, 2019