2019.11.20時点のHTTPoisonの最新版v1.6.2で動作を確認しています。
Proxy環境下でElixirのHTTPoisonを利用する場合、Proxyの値を適切に設定しないとリクエストがProxy環境下のネットワークから出ていかない場合があります。
古いバージョンと新しいバージョンで動作が違ったので、簡単に整理しておきます。
前提
- 上述の通り、HTTPoisonの最新版v1.6.2で動作確認をしています。
- 本文中でのProxyサーバの設定値は「 http://proxyserver.jp:8080 」です。
- リクエストを発行する関数については、「HTTPoison.get!/3」を例にして記述しています。ただし、post/3やhead/3などでも同じ対応です。
- Elixirはv.1.9.0を利用しました。
設定方法
v.1.6.2の時点では、2種類ありました。
- 環境変数にHTTP_PROXYの値を設定します
- HTTPoison.get!/3 の関数の第3引数「options」にProxyを設定して渡します
1. 環境変数にHTTP_PROXYの値を設定します
CHANGELOG をみると、v.1.1.0 からの機能です。
それ以前のバージョンでは利用できません。
Elixir側ではなく、Elixirを動作させるサーバ側の設定です。
環境変数「HTTP_PROXY」「http_proxy」「HTTPS_PROXY」「https_proxy」にProxyサーバの設定値を入れておけば、自動でProxyを設定してリクエストを発行してくれます。
ただし、HTTPoison.get!/3 の第3引数「options」に対して「:proxy」を設定していない無い場合にのみ動作します。
なお、環境変数の設定値には優先度があります。
ソースコードをみると以下のようになっていました。
- HTTP_PROXYとhttp_proxyでは、HTTP_PROXYを優先
- HTTPS_PROXYとhttps_proxyでは、HTTPS_PROXYを優先する。
また、HTTPoison.get!/3の第1引数urlのスキーマが「http」か「https」かで、HTTP_PROXYとHTTPS_PROXYのどちらを利用するか判断されています。
2. HTTPoison.get!/3の関数の第3引数「options」にProxyを設定して渡します
明示的にProxyの値を渡したい場合に利用します。
v.1.1.0より以前のバージョンでは、こちらがメインです。
CHANGELOGをみると、v.0.6.1から利用できていたようです。
HTTPoison.get!/3 の第3引数「options」に対して「:proxy」を設定します。
設定値は、「{プロキシサーバ, ポート番号}」のようにタプル形式で指定します。
なお、プロキシサーバはString、ポート番号はIntegerで設定します。
設定例:
[ proxy: {"proxyserver.jp", 8080} ]
この方法は、古いバージョン(例:v0.7.5やv0.6.1)でも動作します。
https://hexdocs.pm/httpoison/0.7.5/HTTPoison.html#request!/5
https://hexdocs.pm/httpoison/0.6.1/HTTPoison.html#request!/5
なお、HTTPプロキシではなく「SOCKSプロキシ」も指定できます。
こちらは、v1.2.0からドキュメントに記載されていました。
[ proxy: {:socks5, "proxyserver.jp", 8080} ])}
それと、詳しいバージョンまでは不明ですが、タプルの部分は「"http(s)://プロキシサーバ:ポート番号"」としても動作します。
こちらの方が直感的に分かりやすいかもしれません。
[ proxy: "http://proxyserver.jp:8080" ]
おまけ1:リクエストのログについて
iex -S mix
で動作確認してた際に、リクエストの値がログに出力されていました。
optionsの値の違いが確認できたので、参考までに載せておきます。
- Proxyを明示的に指定していないとき:
request: %HTTPoison.Request{
body: "",
headers: [],
method: :get,
options: [],
params: %{},
url: "https://qiita.com/api/v2/items?query=Elixir"
},
request_url: "https://qiita.com/api/v2/items?query=Elixir",
status_code: 200
}
- Proxyを明示的に指定したとき(タプルの場合):
request: %HTTPoison.Request{
body: "",
headers: [],
method: :get,
options: [proxy: {"proxyserver.jp", 8080}],
params: %{},
url: "https://qiita.com/api/v2/items?query=Elixir"
},
request_url: "https://qiita.com/api/v2/items?query=Elixir",
status_code: 200
}
- Proxyを明示的に指定したとき(文字列の場合):
request: %HTTPoison.Request{
body: "",
headers: [],
method: :get,
options: [proxy: "http://proxyserver.jp:8080"],
params: %{},
url: "https://qiita.com/api/v2/items?query=Elixir"
},
request_url: "https://qiita.com/api/v2/items?query=Elixir",
status_code: 200
}
おまけ2: 動作確認に利用したソースコード
ソースコードは、こちらのスライドの内容を参考にして作成しました。
defmodule Crawl do
def get() do
url = "https://qiita.com/api/v2/items?query=Elixir"
### Proxyを明示しないで、環境変数から取得するパターン
# HTTPoison.get!(url)
### タプル設定の場合
# HTTPoison.get!(url, [], [ proxy: {"proxyserver.jp", 8080} ])
### SOCKSプロキシの場合
# HTTPoison.get!(url, [], [ proxy: {:socks5, "proxyserver.jp", 8080} ])
### 文字列を渡す場合(+環境変数から取得する場合)
# px = System.get_env("HTTP_PROXY")
# HTTPoison.get!(url, [], [ proxy: px ])
## QiitaAPIを利用してデータを取得してtitleだけにします。
HTTPoison.get!(url) |> body() |> Poison.decode!() |> Enum.map( fn( %{"title" => title} ) -> title end )
end
def body( %{status_code: 200, body: json_body} ), do: json_body
end
こちらは、mix.exs
に設定したhttpoison
のバージョン。
defmodule Crawl.MixProject do
use Mix.Project
def project do
[
app: :crawl,
version: "0.1.0",
elixir: "~> 1.9",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger]
]
end
# Run "mix help deps" to learn about dependencies.
defp deps do
[
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
{ :httpoison, " ~> 1.6" },
{ :poison, " ~> 1.5 " }
]
end
end
まとめ
HTTPoisonのバージョンがv.1.1.0以上の場合ならば、環境変数(HTTP_PROXY、http_proxy、HTTPS_PROXY、 https_proxy)を利用した方法が良い考えます。
コード内に環境依存の情報を記述しませんので。