+ # Ping tick. We don't re-queue a timer there, it is instead queued when :pong is received.
+ # As we hibernate there, reset the count to 0.
+ # If the client misses :pong, Cowboy will automatically timeout the connection after
+ # `@idle_timeout`.
+ def websocket_info(:tick, state) do
+ {:reply, :ping, %{state | timer: nil, count: 0}, :hibernate}
+ end
+
+ def websocket_info(:close, state) do
+ {:stop, state}
+ end
+
+ # State can be `[]` only in case we terminate before switching to websocket,
+ # we already log errors for these cases in `init/1`, so just do nothing here
+ def terminate(_reason, _req, []), do: :ok
+
+ def terminate(reason, _req, state) do
+ Logger.debug(
+ "#{__MODULE__} terminating websocket connection for user #{(state.user || %{id: "anonymous"}).id}, topic #{state.topic || "?"}: #{inspect(reason)}"
+ )
+
+ Streamer.remove_socket(state.topic)
+ :ok
+ end
+
+ # Public streams without authentication.
+ defp authenticate_request(nil, nil) do
+ {:ok, nil, nil}
+ end
+
+ # Authenticated streams.
+ defp authenticate_request(access_token, sec_websocket) do
+ token = access_token || sec_websocket
+
+ with true <- is_bitstring(token),
+ oauth_token = %Token{user_id: user_id} <- Repo.get_by(Token, token: token),
+ user = %User{} <- User.get_cached_by_id(user_id) do
+ {:ok, user, oauth_token}
+ else
+ _ -> {:error, :unauthorized}
+ end
+ end