Permalink
Browse files

Refactoring websocket to have a nicer interface

  • Loading branch information...
1 parent e95a31a commit a9bcee7cb9e70d8cba5ecbfc36f55e51ea1b91ce @jeregrine committed Feb 24, 2014
Showing with 48 additions and 45 deletions.
  1. +39 −34 lib/phoenix/controller/websocket.ex
  2. +2 −2 lib/phoenix/examples/controller.ex
  3. +7 −9 test/phoenix/websocket_test.exs
@@ -42,7 +42,7 @@ defmodule Phoenix.Controller.Websocket do
quote location: :keep do
@behaviour :cowboy_websocket_handler
- defrecord Socket, conn: nil
+ defrecord Socket, conn: nil, pid: nil
import Phoenix.Controller.Websocket
@@ -53,47 +53,36 @@ defmodule Phoenix.Controller.Websocket do
@doc false
def websocket_init(transport, req, opts) do
- start(transport, Socket.new(conn: req), opts)
+ case start(transport, Socket.new(conn: req, pid: self()), opts) do
+ {:ok, state} -> {:ok, req, state}
+ {:ok, state, timeout} -> {:ok, req, state, timeout}
+ _ -> {:ok, req, :undefined_state}
+ end
end
@doc """
- Handles initalization of the websocket, default implementation returns
- {:ok, req, :undefined_state}
+ Handles initalization of the websocket
+
Possible returns:
- {:ok, req, state}
- {:ok, req, state, :hibernate} # To tell the websocket to hibernate
- {:ok, req, state, timeout} # Timeout defines how long it waits for activity from the client. Default: infinity.
- {:ok, req, state, timeout, :hibernate}
- {:shutdown, req} # Shut the socket down before upgrading.
+ :ok
+ {:ok, state}
+ {:ok, state, timeout} # Timeout defines how long it waits for activity from the client. Default: infinity.
"""
def start(_transport, req, _opts) do
{:ok, req, :undefined_state}
end
@doc """
Handles handles recieving data from the client, default implementation does nothing.
- {:ok, req, state}
- Possible returns:
- {:ok, req, state}
- {:ok, req, state, :hibernate} # Do nothing and hibernate
- {:reply, out_frame, req, state} # Reply to the client.
- {:reply, out_frame, req, state, :hibernate} # Reply and hibernate connection
- {:shutdown, req, state} # Terminate connection
-
- out_frame is defined as:
- :close | :ping | :pong
- {:text | :binary | :close | :ping | :pong, iodata()}
- {:close, close_code(), iodata()}
-
- close_code: 1000..4999
"""
def stream(data, req, state) do
{:ok, req, state}
end
@doc false
def websocket_handle(data, req, state) do
- stream(data, Socket.new(conn: req), state)
+ stream(data, Socket.new(conn: req, pid: self()), state)
+ {:ok, req, state}
end
@doc """
@@ -106,8 +95,22 @@ defmodule Phoenix.Controller.Websocket do
end
@doc false
+ def websocket_info({:send, frame, state}, req, _state) do
+ {:reply, frame, req, state}
+ end
+ @doc false
+ def websocket_info(:shutdown, req, state) do
+ {:shutdown, req, state}
+ end
+ @doc false
+ def websocket_info(:hibernate, req, state) do
+ {:ok, req, state, :hibernate}
+ end
+
+ @doc false
def websocket_info(data, req, state) do
- info(data, Socket.new(conn: req), state)
+ info(data, Socket.new(conn: req, pid: self()), state)
+ {:ok, req, state}
end
@doc """
@@ -133,29 +136,31 @@ defmodule Phoenix.Controller.Websocket do
@doc """
Sends a reply to the socket. Follow the cowboy websocket frame syntax
+ Frame is defined as
+ :close | :ping | :pong
+ {:text | :binary | :close | :ping | :pong, iodata()}
+ {:close, close_code(), iodata()}
Options:
:state
:hibernate # (true | false) if you want to hibernate the connection
+ close_code: 1000..4999
"""
- def reply(socket, frame, options \\ []) do
- case Dict.has_key?(options, :hibernate) do
- true -> {:reply, frame, socket.conn, options[:state], :hibernate}
- false -> {:reply, frame, socket.conn, options[:state]}
- end
+ def reply(socket, frame, state \\ []) do
+ send(socket.pid, {:send, frame, state})
end
@doc """
Terminates a connection.
"""
- def terminate(socket, state) do
- {:shutdown, socket.conn, state}
+ def terminate(socket) do
+ send(socket.pid, :shutdown)
end
@doc """
Hibernates the socket.
"""
- def hibernate(socket, state) do
- {:ok, socket.conn, state, :hibernate}
+ def hibernate(socket) do
+ send(socket.pid, :hibernate)
end
end
@@ -59,7 +59,7 @@ end
defmodule Phoenix.Examples.Controllers.Echo do
use Phoenix.Controller.Websocket
- def stream(data, req, state) do
- {:reply, {:text, data}, req, state}
+ def stream(data, socket, _state) do
+ reply socket, data
end
end
@@ -2,22 +2,20 @@ defmodule Phoenix.Controller.WebsocketTest do
use ExUnit.Case, async: true
alias Phoenix.Controller.Websocket
- defrecord Socket, conn: nil
+ defrecord Socket, conn: nil, pid: nil
test "verify correct return from terminate" do
- assert Websocket.terminate(Socket.new(conn: :conn), nil) == {:shutdown, :conn, nil}
+ Websocket.terminate(Socket.new(pid: self()))
+ assert_received :shutdown
end
test "verify correct return from hibernate" do
- assert Websocket.hibernate(Socket.new(conn: :conn), nil) == {:ok, :conn, nil, :hibernate}
+ Websocket.hibernate(Socket.new(pid: self))
+ assert_received :hibernate
end
test "verify basic reply" do
- assert Websocket.reply(Socket.new(conn: :conn), :frame) == {:reply, :frame, :conn, nil}
+ Websocket.reply(Socket.new(pid: self), {:text, "hello"})
+ assert_received {:send, {:text, "hello"}, []}
end
-
- test "verify hibernate reply" do
- assert Websocket.reply(Socket.new(conn: :conn), :frame, hibernate: true) == {:reply, :frame, :conn, nil, :hibernate}
- end
-
end

0 comments on commit a9bcee7

Please sign in to comment.