From a02ae8d1153560876e55e2a558ab6f7e44f240b8 Mon Sep 17 00:00:00 2001 From: Andrew Vy Date: Sat, 30 Sep 2017 20:29:26 -0700 Subject: [PATCH] :sparkles: Add `async` option to RPC 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}` --- lib/chrome_remote_interface.ex | 31 +++++++++++++--- lib/page_session.ex | 66 ++++++++++++++++++++++++++-------- mix.exs | 2 +- 3 files changed, 78 insertions(+), 21 deletions(-) diff --git a/lib/chrome_remote_interface.ex b/lib/chrome_remote_interface.ex index 47df490..3b1ab34 100644 --- a/lib/chrome_remote_interface.ex +++ b/lib/chrome_remote_interface.ex @@ -3,6 +3,8 @@ defmodule ChromeRemoteInterface do Documentation for ChromeRemoteInterface. """ + alias ChromeRemoteInterface.PageSession + protocol = File.read!("priv/protocol.json") |> Poison.decode!() @@ -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 diff --git a/lib/page_session.ex b/lib/page_session.ex index b72fbd2..3948c32 100644 --- a/lib/page_session.ex +++ b/lib/page_session.ex @@ -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 # --- @@ -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 @@ -185,6 +195,29 @@ 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"] @@ -192,7 +225,10 @@ defmodule ChromeRemoteInterface.PageSession do 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 diff --git a/mix.exs b/mix.exs index f96bc32..5b1abb3 100644 --- a/mix.exs +++ b/mix.exs @@ -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(),