HTTP Redirects Using HttpClient
[C#, Under The Hood, HttpClient]
In a previous post I had mentioned that the HttpClient
does not automatically process HTTP redirects and you would have to write the logic yourself.
As with most things in life, this is partly true and also partly false.
We can demonstrate this with a real example.
My company site is innova.co.ke. I have also bought innova.africa.
I have setup innova.africa
to redirect to innova.co.ke
, so that anyone visiting the former will be seamlessly redirected to the latter.
So let us make a normal request to innova.africa
// Create a plain client
var plainClient = new HttpClient();
var response = await plainClient.GetStringAsync("http://innova.africa");
Console.WriteLine(response);
We should see the html of the website (redirected) printed to the console.
Given nothing special has been done, this implies that the HttpClient
has in fact followed the redirect.
We can prove this by modifying the code slightly.
When constructing the HttpClient
we can pass it a HttpClientHandler that controls various properties.
One of these properties is AllowAutoRedirect, which we can see defaults to true.
We can turn this off like so:
// Create new client and turn off autoredirect
var clientAutoOff = new HttpClient(new HttpClientHandler() { AllowAutoRedirect = false });
response = await clientAutoOff.GetStringAsync("http://innova.africa");
Console.WriteLine(response);
If we run this we in fact get an error:
To understand what exactly is happening we need to dig a little deeper.
What we want to examine is the headers that the server responds with to the initial request.
For this we need to do two things:
-
Change how we are making the request - rather than make a request for content, we make a raw request using GetAsync rather than GetStringAsync
The difference is
GetAsync
returns aHttpResponse
object, which we want to examine, rather thanGetStringAsync
which returns the response content as astring
. -
Instruct the
HttpClient
that we are interested in just the headers, and not the body. This is done using an overload of theGetAsync
method.
The code is as follows:
// Reuse the previous client, and read just the headers in this request
var headerResponse = await clientAutoOff.GetAsync("http://innova.africa", HttpCompletionOption.ResponseHeadersRead);
// Iterate through the headers of the response and print them
foreach (var item in headerResponse.Headers)
{
Console.WriteLine($"Header: {item.Key} - {string.Join(",", item.Value)}");
}
If we run this we should see the following:
The key header here is Location
.
If the AllowAutoRedirect
is true
, the HttpClient
will retrieve the value of this header and automatically make a request to the URL specified there.
Now I opened by saying it is partly true and partly false that the HttpClient
automatically follows redirects, and I seem to have proved otherwise.
Here’s the thing: even with AllowAutoRedirect
being true, a request to a http resource that has been redirected to a https resource will NOT be auto redirected.
In other words a http
or https
resource redirected to another http
or https
resource will redirect just fine.
Where it will break is if the origin is http
and the corresponding redirect isn’t; or if the origin in https
and the corresponding redirect isn’t.
And to make things more complicated, this change was introduced in .NET Core, as it is arguably more secure.
However in the full .NET Framework 4.8 and earlier, redirects between http
and https
are honored. That means there is inconsistency in behaviour between .NET Core and .NET Framework.
The code is in my Github.
Happy hacking!