dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.44k stars 4.76k forks source link

HttpClient does not automatically follow https to http redirects #108588

Open jordinl opened 1 month ago

jordinl commented 1 month ago

Description

https to http are not automatically followed even though the documentation states they should: https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclienthandler.allowautoredirect?view=net-8.0#remarks

Reproduction Steps

Clone this repo: https://github.com/jordinl/http-client-redirect-bug and execute it.

Expected behavior

Fetching https://verizon.com should return 200 status code and response URL https://www.verizon.com/

Actual behavior

Fetching https://verizon.com returns 301 status code and location header: http://verizon.com/

Regression?

No response

Known Workarounds

No response

Configuration

Other information

No response

filipnavara commented 1 month ago

The documentation you linked has this big purple note box:

With AllowAutoRedirect set to true, the .NET Framework will follow redirections even when being redirected to an HTTP URI from an HTTPS URI. .NET Core versions 1.0, 1.1 and 2.0 will not follow a redirection from HTTPS to HTTP even if AllowAutoRedirect is set to true.

While it doesn't explicitly state anything about .NET Core > 2.0 I would assume the same restriction still applies and the documentation was just not updated.

antonfirsov commented 1 month ago

Triage: this intended behavior, and won't be changed. We should the AllowAutoRedirect docs.

jordinl commented 1 month ago

Coming from other programming languages, I find not allowing https to http redirects surprising. And if I wanted to not allow those I would choose to raise an exception when that happened.

Most languages handle those just fine:

Node.js

node -e '
const response = await fetch("https://verizon.com");
console.log(response.status, response.url);
'
#=> 200 https://www.verizon.com/

Python

python -c '
import requests
response = requests.get("https://verizon.com")
print(response.status_code, response.url)
'
#=> 200 https://www.verizon.com

Curl

curl -L https://verizon.com -o /dev/null -s -w '%{http_code} %{url_effective}\n'
#=> 200 https://www.verizon.com/

Java

jshell -s - <<EOF
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
var client = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.ALWAYS).build();
var request = HttpRequest.newBuilder().uri(new URI("https://verizon.com")).GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("\n" +response.statusCode() + " " + response.uri());
EOF

#=> 200 https://www.verizon.com

Ruby

By default it raises an exception, which is how I would handle it if I didn't want to allow the redirects

ruby -e '
require "open-uri"
response = URI.open("https://verizon.com")
puts response.status, response.base_uri
'

/home/jordi/.rubies/ruby-3.1.3/lib/ruby/3.1.0/open-uri.rb:225:in `open_loop': redirection forbidden: https://verizon.com -> http://verizon.com/ (RuntimeError)
    from /home/jordi/.rubies/ruby-3.1.3/lib/ruby/3.1.0/open-uri.rb:151:in `open_uri'
    from /home/jordi/.rubies/ruby-3.1.3/lib/ruby/3.1.0/open-uri.rb:721:in `open'
    from /home/jordi/.rubies/ruby-3.1.3/lib/ruby/3.1.0/open-uri.rb:29:in `open'
    from -e:3:in `<main>'

But using a third-party gem allows the redirect:

gem install httparty
ruby -e '
require "httparty"
response = HTTParty.get("https://verizon.com")
puts "#{response.code} #{response.request.last_uri}"
'
#=> 200 https://www.verizon.com/

I understand not wanting to change the default for backwards compatibility, but I think it would be nice to add a flag so https to http redirects could be followed. Apparently this came up a couple times already:

https://github.com/dotnet/runtime/issues/23801 https://github.com/dotnet/runtime/issues/28039

MihaZupan commented 1 month ago

Adding APIs to make opting-in to such redirects easier is tracked by #45364