hackney icon indicating copy to clipboard operation
hackney copied to clipboard

Fix ssl_closed message leak when server hangs up ("Connection: close" header)

Open rozap opened this issue 5 years ago • 8 comments

This should fix #464

  • mutually recursive maybe_continue and async_recv functions are really tricky to reason about, but it doesn't seem like it makes sense for maybe_continue to do error handling at all, since async_recv was already doing it

    • maybe_continue and stream_loop shouldn't select socket messages
    • we're getting into a state where the socket sends us a message while we're doing other things and we do that receive and then crash
  • this change makes async_recv the only one to do a select for transport messages

  • When the server closes the connection and :ssl sends us :ssl_closed, we weren't handling the message, which caused it to leak into the mailbox even after the request was ended

    • need to flush to make sure it's gone in this case where the server hangs up
    • caused when Connection: close header is sent

fix debugging statement

rozap avatar Jun 12 '20 17:06 rozap

@benoitc Hi, please why is it still not merged as a whole? I can see only second commit changes in https://github.com/benoitc/hackney/compare/1.16.0...v1.17.0 probably cherry-picked? Thanks

erik-mocny avatar Dec 28 '20 15:12 erik-mocny

i didn't cherry-picked :) I merged your other PR #641 . I didn't merge this one yet as i wanted to test it first. There will be a new release next week that should fix the issue.

benoitc avatar Dec 29 '20 06:12 benoitc

Thanks for the reply. It wasn't my PR I just asked about it because we were facing same issue in our projects.

erik-mocny avatar Dec 29 '20 08:12 erik-mocny

Hello @benoitc, what is the current status on this PR?

There will be a new release next week that should fix the issue.

Also is this referring to a 1.17.1 version coming soon? Because with 1.17.0 which mentions this PR, the issue still exists. Thank you

edit* I cloned the 1.17.0 master branch and cherry picked this PR and I can confirm that it solved the issue.

Dvogiatz avatar Feb 01 '21 12:02 Dvogiatz

Update: after 2 days of working properly without any issues, it started to throw the same error again.

Dvogiatz avatar Feb 04 '21 09:02 Dvogiatz

Sorry to have been silent. There is a new release coming next week that will fix this issue. I will create a ticket summarizing it.

On Mon 28 Dec 2020 at 16:56 Erik Mocny [email protected] wrote:

@benoitc https://github.com/benoitc Hi, please why is it still not merged as a whole? I can see only second commit changes in 1.16.0...v1.17.0 https://github.com/benoitc/hackney/compare/1.16.0...v1.17.0 probably cherry-picked? Thanks

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/benoitc/hackney/pull/640#issuecomment-751761715, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAADRIQMU6533TEVVYHWHXTSXCTCLANCNFSM4N4QYHCA .

benoitc avatar Feb 05 '21 01:02 benoitc

@benoitc I'm trying the new version of hackney: 1.17.4 but it keeps the same behavior:

{:ssl_closed,
 {:sslsocket, {:gen_tcp, #Port<0.330>, :tls_connection, :undefined}
  • Erlang/OTP 22 [erts-10.7.2.7]
  • Hackney 1.17.4

luizmiranda7 avatar Mar 23 '21 13:03 luizmiranda7

 [error] Unexpected message: {:ssl_closed, {:sslsocket, {:gen_tcp, #Port<0.213>, :tls_connection, :undefined}, [#PID<0.2999.0>, #PID<0.2998.0>]}}

same issue here as well

Erlang/OTP 22 [erts-10.7.2.7] Hackney 1.17.4

here is the function implementation


  def get(url) do
    Stream.resource(
      # start_fun
      fn ->
        HTTPoison.get!(
          url,
          %{},
          stream_to: self(),
          async: :once,
          hackney: [
            ssl_options: [
              versions: [:"tlsv1.2"]
            ]
          ]
        )
      end,
      fn %HTTPoison.AsyncResponse{id: id} = resp ->
        receive do
          %HTTPoison.AsyncStatus{id: ^id, code: _code} ->
            HTTPoison.stream_next(resp)
            {[], resp}

          %HTTPoison.AsyncHeaders{id: ^id, headers: _headers} ->
            HTTPoison.stream_next(resp)
            {[], resp}

          %HTTPoison.AsyncChunk{id: ^id, chunk: chunk} ->
            HTTPoison.stream_next(resp)
            {[chunk], resp}

          %HTTPoison.AsyncEnd{id: ^id} ->
            {:halt, resp}
        end
      end,
      fn _end_func ->
        nil
      end
    )
  end

ringofhealth avatar May 18 '21 19:05 ringofhealth