req icon indicating copy to clipboard operation
req copied to clipboard

Redirect behavior is inconvenient with transport_opts overrides

Open liamwhite opened this issue 8 months ago • 9 comments

Currently when I make a request through Req, I have a setup where I parse the URL scheme in order to determine whether I need to apply a sha1withrsa override (see relevant otp report here for why I want to do that):

%{scheme: "https"} ->  
  [
    transport_opts: [
      customize_hostname_check: [
        match_fun: :public_key.pkix_verify_hostname_match_fun(:https)
      ],
      signature_algs_cert: :ssl.signature_algs(:default, :"tlsv1.3") ++ [sha: :rsa]
    ]
  ]

_ ->
  []

This is because the transport_opts passed to Mint are directly passed to gen_tcp or ssl, depending on the scheme. When attempting to fetch a https URL that redirects to http (I created one on a link shortener that redirects to example.com for testing) with these transport_opts, the following error occurs:

iex(4)> get("https://t.ly/E8MCX")
[debug] redirecting to http://example.com
** (exit) :badarg
    (finch 0.18.0) lib/finch/http1/pool.ex:96: Finch.HTTP1.Pool.request/6
    (finch 0.18.0) lib/finch.ex:423: anonymous fn/6 in Finch.stream_while/5
    (telemetry 1.2.1) telemetry/src/telemetry.erl:321: :telemetry.span/3
    (req 0.5.0) lib/req/steps.ex:823: Req.Steps.finch_stream_into_fun/5
    (req 0.5.0) lib/req/request.ex:1007: Req.Request.run_request/1
    (req 0.5.0) lib/req/steps.ex:2047: Req.Steps.redirect/1
    (req 0.5.0) lib/req/request.ex:1024: anonymous fn/2 in Req.Request.run_response/2
    (elixir 1.17.0) lib/enum.ex:4858: Enumerable.List.reduce/3
    (elixir 1.17.0) lib/enum.ex:2585: Enum.reduce_while/3
    (req 0.5.0) lib/req/request.ex:952: Req.Request.run/1
    iex:4: (file)

A similar but more limited error occurs when a link to an insecure website redirects to a link to a secure website with a sha1 root or intermediate certificate in the chain. The absence of the override in transport_opts causes the https request to then fail due to the ssl module rejecting the connection.

What should req do about this

It seems unreasonable to modify the redirect step to try to handle this. Instead, it would be nice to have more configurability about the transport_opts passed on a per-scheme basis, rather than globally. This would also eliminate my need to parse the scheme to determine which transport_opts to apply.

Alternatively, if OTP 28 fixes ssl being inoperable by default on a large number of hosts, this issue may no longer be relevant.

liamwhite avatar Jun 26 '24 15:06 liamwhite