Open5
Erlang/OTP の HTTP クライアントで IPv6
概要
- Erlang/OTP の IPv6 で HTTP を送信するためには、明示的に inet6 を指定する必要があり使いづらい
- 事前に inet_res で AAAA が引けるかどうかを確認するといった仕組みが必要になる
Gun
で、これだと普通にアクセスできない。やっかいすぎる。
gun の tcp_opts で inet6 を指定
gun:open の opts で #{tcp_opts => [inet6]} を指定する
ただしこの設定をすると、ipv4 へのアクセスが微妙な挙動をする。
httpc の set_options で ipfamily に inet6 を指定
httpc:set_options([{ipfamily, inet6}])
Gun では Hostname からの IPv6 判定はできない
Gun のソースコードを追いかけると domain_lookup
に inet:tcp_module/2
というのが見つかる。
この Mod で AAAA レコードのみが設定されたホスト名で inet6_tcp
が返ってくるが良いのだが、 inet_tcp
が返ってきてしまうのが問題。
OTP 側の inet:tcp_module/2
を見ると、どうやら IPv6 かどうかの判断をしているが、
inet:hostanme()
の場合は tcp_opts に依存することがわかった。
つまり現時点では inet6 を gun:open
の Opts の tcp_opts に [inet6] として指定する必要がある。
inet6 を指定すると inet6_tcp が返ってくるのは確認できた。
inet:tcp_module/2 の挙動
ipv6.example.com
というのが AAAA レコードだけ登録されたホスト名とする。
> inet:tcp_module([], "ipv6.example.com").
{inet_tcp,[]}
> inet:tcp_module([inet6], "ipv6.example.com").
{inet6_tcp,[]}
つまりホスト名から inet6_tcp を取得することはできず、ホスト名から inet_res:lookup などで IPv6 が存在するかどうかをチェックして inet6 を渡す必要がある。
だるい。
httpc:request/1 の挙動
- ipv6.example.com というのが AAAA レコードだけ登録されたホスト名とする。
- OTP 標準の httpc も set_options で inet6 を指定しないと正常に動作しない
$ erl
Erlang/OTP 26 [erts-14.1.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit:ns]
Eshell V14.1.1 (press Ctrl+G to abort, type help(). for help)
1> application:ensure_all_started(ssl).
{ok,[crypto,asn1,public_key,ssl]}
2> application:ensure_all_started(inets).
{ok,[inets]}
3> httpc:request("https://ipv6.example.com/").
{error,{failed_connect,[{to_address,{"ipv6.example.com",443}},
{inet,[inet],nxdomain}]}}
4> httpc:set_options([{ipfamily, inet6}]).
ok
5> httpc:request("https://ipv6.example.com/").
{ok,{{"HTTP/1.1",200,"OK"},
[{"connection","keep-alive"},
{"date","Sat, 25 Nov 2023 04:06:13 GMT"},
{"server","nginx/1.18.0 (Ubuntu)"},
{"content-length","2"},
{"content-type","application/json"}],
"{}"}}