diff --git a/CHANGELOG.md b/CHANGELOG.md index b21771668a..b568c5b1fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- [#7898](https://github.com/blockscout/blockscout/pull/7898) - Add possibility to add extra headers with JSON RPC URL - [#7836](https://github.com/blockscout/blockscout/pull/7836) - Improve unverified email flow - [#7784](https://github.com/blockscout/blockscout/pull/7784) - Search improvements: Add new fields, light refactoring - [#7811](https://github.com/blockscout/blockscout/pull/7811) - Filter addresses before insertion diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex index 45b54cdd58..f6e48de89a 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex @@ -14,7 +14,7 @@ defmodule EthereumJSONRPC.HTTP do @doc """ Sends JSONRPC request encoded as `t:iodata/0` to `url` with `options` """ - @callback json_rpc(url :: String.t(), json :: iodata(), options :: term()) :: + @callback json_rpc(url :: String.t(), json :: iodata(), headers :: [{String.t(), String.t()}], options :: term()) :: {:ok, %{body: body :: String.t(), status_code: status_code :: pos_integer()}} | {:error, reason :: term} @@ -26,7 +26,7 @@ defmodule EthereumJSONRPC.HTTP do url = url(options, method) http_options = Keyword.fetch!(options, :http_options) - with {:ok, %{body: body, status_code: code}} <- http.json_rpc(url, json, http_options), + with {:ok, %{body: body, status_code: code}} <- http.json_rpc(url, json, headers(), http_options), {:ok, json} <- decode_json(request: [url: url, body: json], response: [status_code: code, body: body]), {:ok, response} <- handle_response(json, code) do {:ok, response} @@ -66,7 +66,7 @@ defmodule EthereumJSONRPC.HTTP do json = encode_json(batch) - case http.json_rpc(url, json, http_options) do + case http.json_rpc(url, json, headers(), http_options) do {:ok, %{status_code: status_code} = response} when status_code in [413, 504] -> rechunk_json_rpc(chunks, options, response, decoded_response_bodies) @@ -203,4 +203,8 @@ defmodule EthereumJSONRPC.HTTP do ArgumentError -> :error end + + defp headers do + Application.get_env(:ethereum_jsonrpc, __MODULE__)[:headers] + end end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http/httpoison.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http/httpoison.ex index c3e466419e..d7f5610751 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http/httpoison.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http/httpoison.ex @@ -8,8 +8,8 @@ defmodule EthereumJSONRPC.HTTP.HTTPoison do @behaviour HTTP @impl HTTP - def json_rpc(url, json, options) when is_binary(url) and is_list(options) do - case HTTPoison.post(url, json, [{"Content-Type", "application/json"}], options) do + def json_rpc(url, json, headers, options) when is_binary(url) and is_list(options) do + case HTTPoison.post(url, json, headers, options) do {:ok, %HTTPoison.Response{body: body, status_code: status_code}} -> {:ok, %{body: body, status_code: status_code}} @@ -18,5 +18,5 @@ defmodule EthereumJSONRPC.HTTP.HTTPoison do end end - def json_rpc(url, _json, _options) when is_nil(url), do: {:error, "URL is nil"} + def json_rpc(url, _json, _headers, _options) when is_nil(url), do: {:error, "URL is nil"} end diff --git a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/http/mox_test.exs b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/http/mox_test.exs index 052667657d..09eee029ba 100644 --- a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/http/mox_test.exs +++ b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/http/mox_test.exs @@ -38,12 +38,12 @@ defmodule EthereumJSONRPC.HTTP.MoxTest do } do if json_rpc_named_arguments[:transport_options][:http] == EthereumJSONRPC.HTTP.Mox do EthereumJSONRPC.HTTP.Mox - |> expect(:json_rpc, 2, fn _url, json, _options -> + |> expect(:json_rpc, 2, fn _url, json, _headers, _options -> assert IO.iodata_to_binary(json) =~ ":13000" {:ok, %{body: "413 Request Entity Too Large", status_code: 413}} end) - |> expect(:json_rpc, fn _url, json, _options -> + |> expect(:json_rpc, fn _url, json, _headers, _options -> json_binary = IO.iodata_to_binary(json) refute json_binary =~ ":13000" @@ -58,7 +58,7 @@ defmodule EthereumJSONRPC.HTTP.MoxTest do {:ok, %{body: body, status_code: 200}} end) - |> expect(:json_rpc, fn _url, json, _options -> + |> expect(:json_rpc, fn _url, json, _headers, _options -> json_binary = IO.iodata_to_binary(json) refute json_binary =~ ":6499" @@ -107,10 +107,10 @@ defmodule EthereumJSONRPC.HTTP.MoxTest do if json_rpc_named_arguments[:transport_options][:http] == EthereumJSONRPC.HTTP.Mox do EthereumJSONRPC.HTTP.Mox - |> expect(:json_rpc, fn _url, _json, _options -> + |> expect(:json_rpc, fn _url, _json, _headers, _options -> {:ok, %{body: "504 Gateway Timeout", status_code: 504}} end) - |> expect(:json_rpc, fn _url, json, _options -> + |> expect(:json_rpc, fn _url, json, _headers, _options -> json_binary = IO.iodata_to_binary(json) refute json_binary =~ "0xD2849" @@ -141,7 +141,7 @@ defmodule EthereumJSONRPC.HTTP.MoxTest do {:ok, %{body: body, status_code: 200}} end) - |> expect(:json_rpc, fn _url, json, _options -> + |> expect(:json_rpc, fn _url, json, _headers, _options -> json_binary = IO.iodata_to_binary(json) refute json_binary =~ "0xD2844" @@ -199,10 +199,10 @@ defmodule EthereumJSONRPC.HTTP.MoxTest do if json_rpc_named_arguments[:transport_options][:http] == EthereumJSONRPC.HTTP.Mox do EthereumJSONRPC.HTTP.Mox - |> expect(:json_rpc, fn _url, _json, _options -> + |> expect(:json_rpc, fn _url, _json, _headers, _options -> {:error, :timeout} end) - |> expect(:json_rpc, fn _url, json, _options -> + |> expect(:json_rpc, fn _url, json, _headers, _options -> json_binary = IO.iodata_to_binary(json) refute json_binary =~ "0xD2849" @@ -233,7 +233,7 @@ defmodule EthereumJSONRPC.HTTP.MoxTest do {:ok, %{body: body, status_code: 200}} end) - |> expect(:json_rpc, fn _url, json, _options -> + |> expect(:json_rpc, fn _url, json, _headers, _options -> json_binary = IO.iodata_to_binary(json) refute json_binary =~ "0xD2844" @@ -293,7 +293,7 @@ defmodule EthereumJSONRPC.HTTP.MoxTest do json = Jason.encode_to_iodata!(payload) http_options = Keyword.fetch!(transport_options, :http_options) - assert {:ok, %{body: body, status_code: 413}} = http.json_rpc(url, json, http_options) + assert {:ok, %{body: body, status_code: 413}} = http.json_rpc(url, json, [], http_options) assert body =~ "413 Request Entity Too Large" end diff --git a/config/config_helper.exs b/config/config_helper.exs index 9d51608a26..23224e698b 100644 --- a/config/config_helper.exs +++ b/config/config_helper.exs @@ -119,4 +119,13 @@ defmodule ConfigHelper do transformer end end + + @spec parse_json_env_var(String.t(), String.t()) :: any() + def parse_json_env_var(env_var, default_value) do + env_var + |> safe_get_env(default_value) + |> Jason.decode!() + rescue + err -> raise "Invalid JSON in environment variable #{env_var}: #{inspect(err)}" + end end diff --git a/config/runtime.exs b/config/runtime.exs index 0c28db6180..0619c6ad37 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -150,6 +150,12 @@ config :ethereum_jsonrpc, ipc_path: System.get_env("IPC_PATH"), disable_archive_balances?: ConfigHelper.parse_bool_env_var("ETHEREUM_JSONRPC_DISABLE_ARCHIVE_BALANCES") +config :ethereum_jsonrpc, EthereumJSONRPC.HTTP, + headers: + %{"Content-Type" => "application/json"} + |> Map.merge(ConfigHelper.parse_json_env_var("ETHEREUM_JSONRPC_HTTP_HEADERS", "{}")) + |> Map.to_list() + config :ethereum_jsonrpc, EthereumJSONRPC.Geth, debug_trace_transaction_timeout: System.get_env("ETHEREUM_JSONRPC_DEBUG_TRACE_TRANSACTION_TIMEOUT", "5s"), tracer: System.get_env("INDEXER_INTERNAL_TRANSACTIONS_TRACER_TYPE", "call_tracer") diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 37afc533b5..7ec20be1fc 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -12,6 +12,7 @@ LOGO=/images/blockscout_logo.svg # ETHEREUM_JSONRPC_WS_URL= ETHEREUM_JSONRPC_TRANSPORT=http ETHEREUM_JSONRPC_DISABLE_ARCHIVE_BALANCES=false +# ETHEREUM_JSONRPC_HTTP_HEADERS= IPC_PATH= NETWORK_PATH=/ BLOCKSCOUT_HOST= diff --git a/docker/Makefile b/docker/Makefile index 6076047bd3..ac4c84a6a8 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -60,6 +60,9 @@ endif ifdef ETHEREUM_JSONRPC_DISABLE_ARCHIVE_BALANCES BLOCKSCOUT_CONTAINER_PARAMS += -e 'ETHEREUM_JSONRPC_DISABLE_ARCHIVE_BALANCES=$(ETHEREUM_JSONRPC_DISABLE_ARCHIVE_BALANCES)' endif +ifdef ETHEREUM_JSONRPC_HTTP_HEADERS + BLOCKSCOUT_CONTAINER_PARAMS += -e 'ETHEREUM_JSONRPC_HTTP_HEADERS=$(ETHEREUM_JSONRPC_HTTP_HEADERS)' +endif ifdef IPC_PATH BLOCKSCOUT_CONTAINER_PARAMS += -e 'IPC_PATH=$(IPC_PATH)' endif