req icon indicating copy to clipboard operation
req copied to clipboard

Function clause on HTTP2 connection close when halting in `into:` while using `hostname`

Open kzemek opened this issue 10 months ago • 1 comments

We're running into an issue with into: together with a hostname option, where when a HTTP2 connection is halted with {:halt, {req, resp}} response, something tries to close the HTTP2 connection using Mint.HTTP1.close/1:

url = "https://142.250.185.238/images?q=tbn:ANd9GcRF1IwK6-SxM83UpFVY6WtUZxXx-phss_gAUfdKbkTfau6VWVkt"
hostname = "encrypted-tbn0.gstatic.com"
into_fun = fn _, {req, resp} -> {:halt, {req, resp}} end
Req.get(url, into: into_fun, connect_options: [hostname: hostname])
FunctionClauseError
** (FunctionClauseError) no function clause matching in Mint.HTTP1.close/1

    The following arguments were given to Mint.HTTP1.close/1:

        # 1
        %Mint.HTTP2{
          transport: Mint.Core.Transport.SSL,
          socket: {:sslsocket, {:gen_tcp, #Port<0.123>, :tls_connection, :undefined},
           [#PID<0.5015.0>, #PID<0.5014.0>]},
          mode: :passive,
          hostname: "encrypted-tbn0.gstatic.com",
          port: 443,
          scheme: "https",
          state: :open,
          buffer: <<0, 46, 218, 0, 1, 0, 0, 0, 3, 82, 118, 19, 30, 42, 178, 213, 74,
            148, 190, 209, 4, 68, 29, 160, 88, 253, 236, 180, 51, 15, 26, 196, 95, 167,
            119, 201, 73, 171, 69, 193, 217, 72, ...>>,
          window_size: 1048576,
          encode_table: %HPAX.Table{
            max_table_size: 4096,
            entries: [],
            size: 0,
            length: 0
          },
          decode_table: %HPAX.Table{
            max_table_size: 4096,
            entries: [
              {"alt-svc", "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"},
              {"content-type", "image/jpeg"},
              {"last-modified", "Fri, 02 Sep 2022 07:58:31 GMT"},
              {"age", "12585"},
              {"cache-control", "public, max-age=31536000"},
              {"expires", "Sat, 19 Apr 2025 08:23:25 GMT"},
              {"x-xss-protection", "0"},
              {"server", "sffe"},
              {"x-content-type-options", "nosniff"},
              {"content-length", "28378"},
              {"report-to",
               "{\"group\":\"images-tbn\",\"max_age\":2592000,\"endpoints\":[{\"url\":\"https://csp.withgoogle.com/csp/report-to/images-tbn\"}]}"},
              {"cross-origin-opener-policy-report-only",
               "same-origin; report-to=\"images-tbn\""},
              {"cross-origin-resource-policy", "cross-origin"},
              {"content-security-policy-report-only",
               "require-trusted-types-for 'script'; report-uri https://csp.withgoogle.com/csp/images-tbn"},
              {"access-control-allow-origin", "*"},
              {"accept-ranges", "bytes"}
            ],
            size: 1192,
            length: 16
          },
          ping_queue: {[], []},
          client_settings_queue: {[], []},
          next_stream_id: 5,
          streams: %{
            3 => %{
              id: 3,
              state: :half_closed_local,
              window_size: 65535,
              ref: #Reference<0.265786527.1684799489.93838>,
              received_first_headers?: true
            }
          },
          open_client_stream_count: 1,
          open_server_stream_count: 0,
          ref_to_stream_id: %{#Reference<0.265786527.1684799489.93838> => 3},
          server_settings: %{
            enable_push: true,
            max_concurrent_streams: 100,
            initial_window_size: 1048576,
            max_frame_size: 16384,
            max_header_list_size: 65536,
            enable_connect_protocol: false
          },
          client_settings: %{
            enable_push: true,
            max_concurrent_streams: 100,
            initial_window_size: 65535,
            max_frame_size: 16384,
            max_header_list_size: :infinity
          },
          headers_being_processed: nil,
          proxy_headers: [],
          private: %{},
          log: false
        }

    Attempted function clauses (showing 2 out of 2):

        def close(%Mint.HTTP1{state: :open} = conn)
        def close(%Mint.HTTP1{state: :closed} = conn)

    (mint 1.5.2) lib/mint/http1.ex:213: Mint.HTTP1.close/1
    (finch 0.18.0) lib/finch/http1/conn.ex:360: Finch.HTTP1.Conn.receive_response/8
    (finch 0.18.0) lib/finch/http1/conn.ex:131: Finch.HTTP1.Conn.request/8
    (finch 0.18.0) lib/finch/http1/pool.ex:60: anonymous fn/10 in Finch.HTTP1.Pool.request/6
    (nimble_pool 1.1.0) lib/nimble_pool.ex:462: NimblePool.checkout!/4
    (finch 0.18.0) lib/finch/http1/pool.ex:52: 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) /Users/konrad/v7/agidb-backend/deps/telemetry/src/telemetry.erl:321: :telemetry.span/3
    (req 0.4.14) lib/req/steps.ex:875: Req.Steps.finch_stream_into_fun/5
    (req 0.4.14) lib/req/request.ex:993: Req.Request.run_request/1
    (req 0.4.14) lib/req/request.ex:938: Req.Request.run/1
    iex:52: (file)

This doesn't happen when using Finch directly:

Finch.start_link(name: Finch, pools: %{default: [conn_opts: [hostname: "encrypted-tbn0.gstatic.com"]]})
request = Finch.build(:get, "https://142.250.185.238/images?q=tbn:ANd9GcRF1IwK6-SxM83UpFVY6WtUZxXx-phss_gAUfdKbkTfau6VWVkt")
Finch.stream_while(request, Finch, nil, fn
  {:status, status}, acc ->
    {:cont, acc}
  {:headers, headers}, acc ->
    {:cont, acc}
  {:data, data}, acc ->
    {:halt, acc}
end)

{:ok, nil}

Req version: 0.4.14 Finch version: 0.18.0

kzemek avatar Apr 19 '24 11:04 kzemek

Thank you for the report. In the meantime you may try forcing particular protocol version.

connect_options: [protocols: [:http1]] # or :http2

wojtekmach avatar Apr 19 '24 14:04 wojtekmach