Skip to content

Commit

Permalink
✨ Add async option to RPC
Browse files Browse the repository at this point in the history
Ex: `ChromeRemoteInterface.RPC.Page.navigate(page_pid, %{url: "google.com"}, async: true)`

Instead of a blocking call, this will `cast` the RPC request and send a message back
to the current process.

This message will be in the format of:

`{:chrome_remote_interface, "Page.navigate", response}`
  • Loading branch information
andrewvy committed Oct 1, 2017
1 parent e67bc44 commit a02ae8d
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 21 deletions.
31 changes: 26 additions & 5 deletions lib/chrome_remote_interface.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ defmodule ChromeRemoteInterface do
Documentation for ChromeRemoteInterface.
"""

alias ChromeRemoteInterface.PageSession

protocol =
File.read!("priv/protocol.json")
|> Poison.decode!()
Expand Down Expand Up @@ -32,11 +34,30 @@ defmodule ChromeRemoteInterface do
Parameters:
#{arg_doc}
"""
def unquote(:"#{name}")(page_pid, parameters \\ %{}) do
page_pid |> ChromeRemoteInterface.PageSession.execute_command(
unquote("#{domain["domain"]}.#{name}"),
parameters
)
def unquote(:"#{name}")(page_pid) do
page_pid |> PageSession.call(
unquote("#{domain["domain"]}.#{name}"),
%{}
)
end
def unquote(:"#{name}")(page_pid, parameters) do
page_pid |> PageSession.call(
unquote("#{domain["domain"]}.#{name}"),
parameters
)
end
def unquote(:"#{name}")(page_pid, parameters, opts) when is_list(opts) do
if opts[:async] do
page_pid |> PageSession.cast(
unquote("#{domain["domain"]}.#{name}"),
parameters
)
else
page_pid |> PageSession.call(
unquote("#{domain["domain"]}.#{name}"),
parameters
)
end
end
end
end
Expand Down
66 changes: 51 additions & 15 deletions lib/page_session.ex
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,16 @@ defmodule ChromeRemoteInterface.PageSession do
@doc """
Executes a raw JSON RPC command through Websockets.
"""
def execute_command(pid, method, params) do
GenServer.call(pid, {:execute_command, method, params})
def call(pid, method, params) do
GenServer.call(pid, {:call_command, method, params})
end

@doc """
Executes a raw JSON RPC command through Websockets, but sends the
response as a message to the requesting process.
"""
def cast(pid, method, params, from \\ self()) do
GenServer.cast(pid, {:cast_command, method, params, from})
end

# ---
Expand All @@ -99,22 +107,24 @@ defmodule ChromeRemoteInterface.PageSession do
{:ok, state}
end

def handle_call({:execute_command, method, params}, from, state) do
message = %{
"id" => state.ref_id,
"method" => method,
"params" => params
}
def handle_cast({:cast_command, method, params, from}, state) do
send_rpc_request(state, method, params)

json = Poison.encode!(message)
WebSockex.send_frame(state.socket, {:text, json})
new_state =
state
|> add_callback({:cast, method, from})
|> increment_ref_id()

{:noreply, new_state}
end

def handle_call({:call_command, method, params}, from, state) do
send_rpc_request(state, method, params)

new_state =
state
|> Map.update(:callbacks, [{state.ref_id, from}], fn(callbacks) ->
[{state.ref_id, from} | callbacks]
end)
|> Map.update(:ref_id, 1, &(&1 + 1))
|> add_callback({:call, from})
|> increment_ref_id()

{:noreply, new_state}
end
Expand Down Expand Up @@ -185,14 +195,40 @@ defmodule ChromeRemoteInterface.PageSession do
{:noreply, state}
end

defp send_rpc_request(state, method, params) do
message = %{
"id" => state.ref_id,
"method" => method,
"params" => params
}

json = Poison.encode!(message)
WebSockex.send_frame(state.socket, {:text, json})
end

defp add_callback(state, from) do
state
|> Map.update(:callbacks, [{state.ref_id, from}], fn(callbacks) ->
[{state.ref_id, from} | callbacks]
end)
end

defp increment_ref_id(state) do
state
|> Map.update(:ref_id, 1, &(&1 + 1))
end

defp send_rpc_response(callbacks, id, json) do
error = json["error"]

Enum.find(callbacks, fn({ref_id, _from}) ->
ref_id == id
end)
|> case do
{_ref_id, from} ->
{_ref_id, {:cast, method, from}} ->
event = {:chrome_remote_interface, method, json}
send(from, event)
{_ref_id, {:call, from}} ->
status = if error, do: :error, else: :ok
GenServer.reply(from, {status, json})
_ -> :ok
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule ChromeRemoteInterface.Mixfile do
def project do
[
app: :chrome_remote_interface,
version: "0.0.2",
version: "0.0.3",
elixir: "~> 1.5",
start_permanent: Mix.env == :prod,
deps: deps(),
Expand Down

0 comments on commit a02ae8d

Please sign in to comment.