dcrck

dcrck

Handling WebSockets 308 Redirect (using Gun 1.3)

Hi,

I’m trying to use the Gun library to connect to Polygon’s socket server, wss://socket.polygon.io/stocks.

This is the response I expect:

> websocat wss://socket.polygon.io/stocks
[{"ev":"status","status":"connected","message":"Connected Successfully"}]

I’ve written a basic wrapper on the Gun library’s methods, as follows:

  def connect(ws), do: ws_upgrade(ws)

  defp ws_upgrade(%{ path: path, port: port, host: host } = state) do
    {:ok, _} = :application.ensure_all_started(:gun)

    connect_opts = %{
      connect_timeout: :timer.minutes(1),
      retry: 10,
      retry_timeout: 300
    }

    case open_connection(host, port, connect_opts) do
      {:ok, gun_pid} ->
        stream_ref = :gun.ws_upgrade(gun_pid, path)
        %{state | stream_ref: stream_ref, gun_pid: gun_pid}
      {:error, _ } ->
        state
    end
  end

  defp open_connection(host, port, connect_opts) do
    with {:ok, conn_pid} <- :gun.open(host, port, connect_opts),
         {:ok, _protocol} <- :gun.await_up(conn_pid, :timer.minutes(1)) do
      {:ok, conn_pid}
    else
      {:error, reason} ->
        {:error, reason}
    end
  end

I’ve also added Gun response handlers based on this example.

I’ve tested this with basic examples, like 'echo.websockets.org' on port 80, and it works just fine. However, when I try with 'socket.polygon.io' with the path "/stocks", I get a gun_response message with a 308 permanent redirect code. The new location is given by the headers as https://socket.polygon.io/stocks.

This is a bit confusing to me because WebSockets are only available for HTTP/1.1, or at least that’s what the Gun documentation told me, and this seems to want to redirect me to HTTP/2. Has anyone else had to deal with WebSocket redirects like this before? If so, how did you do it? Thanks!

Marked As Solved

dcrck

dcrck

Thanks Luca,

Turns out that yes, it was forcing me to use TLS, and Gun’s default behavior when given port 443 is to upgrade to HTTP/2 automatically. Gun’s documentation even mentions that you have to “force HTTP/1.1” in those cases, although the documentation doesn’t make it clear how.

For future readers, here’s what I did to get it working:

  def ws_upgrade(%{ path: path, port: port, host: host } = state) do
    {:ok, _} = :application.ensure_all_started(:gun)

    connect_opts =
      ssl_options(state)
      |> Enum.into(%{
        connect_timeout: :timer.minutes(1),
        retry: 10,
        retry_timeout: 300
      })

    case open_connection(host, port, connect_opts) do
      {:ok, gun_pid} ->
        stream_ref = :gun.ws_upgrade(gun_pid, path)
        %{state | stream_ref: stream_ref, gun_pid: gun_pid}
      {:error, _ } ->
        state
    end
  end

  # When trying to connect to WSS, restrict the protocol to
  # HTTP/1.1, per https://ninenines.eu/docs/en/gun/1.3/guide/websocket/
  defp ssl_options(%{ port: 443 }) do
    %{ protocols: [:http] }
  end
  defp ssl_options(_), do: %{}

# ... other code here

Also Liked

lucaong

lucaong

If you make an HTTP request to socket.polygon.io/stocks (port 80) you get a permanent redirect to an HTTPS endpoint, which incidentally seems to be served by a HTTP/2 capable webserver, but HTTP/2 is not playing a role in the redirect.

This host is forcing use of TLS, that’s why you see the redirect. If you connect to websocket over TLS (wss, port 443) it seems to work all fine (trying with the websocat command line client and I can connect to wss://socket.polygon.io/stocks).

Where Next?

Popular in Questions Top

srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
logicmason
Hi there, I'm working through my first release with elixir/phoenix. I've built a release with distillery and found that it crashes when I...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
Mooodi
Given a string, how can I get access to its character by index? Enum.at("my_string", 2) doesn't work. Or rather, not char, but a substr...
New

Other popular topics Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
beno
I will often find my self writing things similar to: case some_value do nil -&gt; something() "" -&gt; something() _ -&gt; someth...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
273 38985 115
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

We're in Beta

About us Mission Statement