Just leaving some breadcrumbs for future me and future others like me.
Connect with TCP (not secured) - most servers will reject but useful for localhost, etc.
connect_opts = %{ connect_timeout: 60000, retry: 10, retry_timeout: 300, transport: :tcp, # Specify :http or :http2 to tell the server what you'd like. Support for http 2 is not great generally protocols: [:http], # Tell the server which version to use. Note: this is an atom. http_opts: %{version: :"HTTP/1.1"} }
Connect with TLS/SSH but you don’t care to verify the host
connect_opts, do: %{ connect_timeout: 60000, retry: 10, retry_timeout: 300, transport: :tls, tls_opts: [ verify: :verify_none, # Specify a bunch of certs cacerts: :certifi.cacerts(), # Or just one cert # cacertfile: CAStore.file_path(), depth: 99, reuse_sessions: false ], # See above explanation why you should specify http_opts: %{version: :"HTTP/1.1"}, protocols: [:http], }
Using TLS/SSH and you want to verify the host
connect_opts = %{ connect_timeout: 60000, retry: 10, retry_timeout: 300, transport: :tls, tls_opts: [ verify: :verify_peer, cacerts: :certifi.cacerts(), # cacertfile: CAStore.file_path(), depth: 99, server_name_indication: 'example.com', reuse_sessions: false, verify_fun: {&:ssl_verify_hostname.verify_fun/3, [check_hostname: 'example.com']} ], http_opts: %{version: :"HTTP/1.1"}, protocols: [:http] }
For :certifi
and CAStore
you may need to install these libs if they are not already in use in your project.
Use like
{:ok, gun_pid} = :gun.open('ws.example.com', 443, connect_opts) # Wait for gun to be up - equivalent to :gun_up message {:ok, http_version} = :gun.await_up(gun_pid) # Put gun_pid and stream_ref in state to match on incoming messages stream_ref = :gun.ws_upgrade(gun_pid, path(), headers()) |> IO.inspect()
Elsewhere monitor for the upgrade message. If you’re using a GenServer this will land in handle_info
.
def handle_info({:gun_upgrade, gun_pid, stream_ref, stuff, headers}, %{gun_pid: gun_pid, stream_ref: stream_ref} = state) do # Now the websocket is upgraded and can send and receive :gun.ws_send(gun_pid, stream_ref, {:text, "Hello Server"}) {:noreply, state} end
If you’re using a GenServer the rest of the messages from gun will land in other handle_info
s so make a clause for each one you want to handle.