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.