ninenines / gun

HTTP/1.1, HTTP/2, Websocket client (and more) for Erlang/OTP.
ISC License
891 stars 232 forks source link

:gun_http.ws_send/6 is undefined or private #297

Open cultofmetatron opened 2 years ago

cultofmetatron commented 2 years ago

version: 2.0

I'm getting a crash in our gun cluster system. seems to happen only when we send a ping

** (UndefinedFunctionError) function :gun_http.ws_send/6 is undefined or private
    (gun 2.0.0-rc.2) :gun_http.ws_send({:ping, "ping"}, {:http_state, {:sslsocket, {:gen_tcp, #Port<0.843240>, :tls_connection, :undefined}, [#PID<0.18172.744>, #PID<0.2620.744>]}, :gun_tls, %{}, :"HTTP/1.1", :keepalive, "", :undefined, [{:stream, {:websocket, #Reference<0.3877686489.3231711234.17167>, #PID<0.30539.291>, "GkoTOlbK5BKnn6USQSTgwQ==", [], %{silence_pings: false}}, #PID<0.30539.291>, :infinity, "GET", 'Sp422452vloc2.connect.paymentsense.cloud', "/pat?token=0784af15-8445-45e8-82b8-1b6cc0c16c2e&api-version=v1&software-house-id=SP769R44&installer-id=SP769R44", true, :undefined}], :head, {0, 0}, :head}, #Reference<0.3877686489.3231711234.17167>, #PID<0.30539.291>, :gun_default_event_h, :undefined)
    (gun 2.0.0-rc.2) /app/deps/gun/src/gun.erl:1307: :gun.connected/3
    (stdlib 3.12.1.2) gen_statem.erl:1118: :gen_statem.loop_state_callback/11
    (stdlib 3.12.1.2) proc_lib.erl:249: :proc_lib.init_p_do_apply/3

at no point in my own code am I calling a 6 arrity version of ws_send so I'm at a loss. I can only conclude something is causing it to call some old code that calls a function that does't exist in this case?

essen commented 2 years ago

There's no ping in HTTP so that might be why this happens. How do you send pings?

cultofmetatron commented 2 years ago

@essen thsi is the code I have that does it. the traceback is non exsitant but this is the only place I send {:ping, "ping"} so I assume this is where it slips into the library

  def handle_cast({:send_message, "ping"}, %{name: name, stream_ref: stream_ref, gun_pid: gun_pid} = state) do

    case :gun.ws_send(gun_pid, stream_ref, {:ping, "ping"}) do
      :ok ->
        {:noreply, state}
      reply ->
        {:noreply, state}
    end
  end

and this is occurring over a websocket

essen commented 2 years ago

The 6-arity is an internal call that results from yours. What might be a bug is if you try to send a Websocket ping when the connection is using HTTP, it tries to call a function that does not exist instead of rejecting the call.

cultofmetatron commented 2 years ago

hmm ok so what it uses depends conditionally on if the websocket handshake is established?

cultofmetatron commented 2 years ago

ok so I added a guard clause that prevents the ws_send from sending the ping unless the status of the websocket is successfully upgraded. will let you know if it works.

Thankyou for the insight!

essen commented 2 years ago

Cheers. If that's it please leave the ticket open and I'll make sure Gun avoids this crash.

cultofmetatron commented 2 years ago

unfortunately I am still seeing it after the guard clause was added.

vshev4enko commented 1 year ago

I got the same crashes. It happens when gen_statem in e.g connected state receives {ws_send, frames()} but the protocol is gun_http which doesn't have a ws_send function. Depending conditionally on ws handshake doesn't solve the issue due to the async nature there is no guarantee gun will not reconnect having {ws_send, frames()} in the mailbox. It is easily reproducible just by calling gun:ws_send/3 and reconnecting.

essen commented 1 year ago

Good point. Should handle that better.