Webサービスを開発していると、他のサービスと連携し始め、どんどん複雑なシステム構成となっていく。外部サービスとの連携というのは、本当によくある要件で、ログインなどのアカウント連携から、ポイント付与、決済、メール送信、データの相互変換を伴うもの、外部サービスが提供する機能の一部をそのまま利用するものなど多岐に渡る。
そのようなシステム開発を行う時、ローカルで開発する時に外部サービスを利用できなかったり、アカウントや目的のデータを作成することがとても手間だたりということが、問題となる。そのため良く見る構成の一つは、ローカルでの開発に外部サービスを利用せず、モックサーバを用意し、それと通信を行うことで代用するというものだ。モックサーバーはローカルで起動している簡単なサーバーであり、リクエストを受け取り、返して欲しい形式のデータを事前に用意しておくことができる。そのため外部サービスを使う煩雑さを避けることができる。このようなモックサーバーとしてNodeのjson-serverのような汎用的なもの用いたり、又はlocalstackのような対象のプロダクトの専用モックサーバーとして開発されたものを用いることもある。
どれも一長一短あるが、特定プロダクト専用モックサーバーではそのサーバーが全ての機能を提供していなかったり、またjson-serverのような汎用的なもでも表現力が乏しかったりする。また、複数のモックサーバーを管理していくのは、それはそれで手間でもある。それぞれが異なるプロダクトであるため、起動方法やランタイムなど多くの手順が異なるからだ。例え1つのモックサーバーの起動に必要な手順が3つでも、10個のモックサーバーが必要であれば手順は30個になる。それだけで、私の頭の中に情報が入りきらない。docker-composeや、その他のツールを使ってローカルでのサービスオーケストレーションをすることもできるが、それはそれで覚える必要があることを増やしている。問題を解決するために新たな問題を増やしている。ローカルの開発のためだけに設定されたオーケストレーションツールに何の意味があるのだろうか。
しかも各種モックサーバーの使い方を学ぶことはしたくない。そもそも、外部サービスの使っている機能は一部であり、全体を知る必要もない。何度も言うようだが、1つや2つであれば問題ないものも、数が増えると問題になる。一方でその一部の機能が送受信している具体的なデータは、知っておく必要がある。データフローはきちんと押さえておくべきだ。そのため統合的な問題解決ができ、内部の詳細についても知ることができる手法が必要になる。
ClojureやCommon Lispで実装したWebサーバーであれば、このあたりの柔軟性は最強レベルまで向上する。それも一つの手だが、Pythonで実装されたMITMPRoxyも要求に耐えられるほどの柔軟性を保持しているように思う。サービスのドメインを/etc/hostsを使い、ローカルのMITMProxyにアクセスし、必要に応じて適切なデータを返却することで、必要な範囲で十分な深さの知識を得ることができる。
ここまで大袈裟な話をしたが、実施することは難しくない。MITMProxyを443でリッスンするようにし --ssl-insecure
を付けて起動すればよい。
sudo mitmproxy \
--listen-port 443 \
--ssl-insecure \
-s main.py
ホームディレクトリの.mitmproxyにはTLSで使用される証明書とその認証局がある1。認証局をOSにインストールしておけば、TLS接続時にエラーが発生することもない。
sudo security add-trusted-cert \
-d \
-p ssl \
-p basic \
-k /Library/Keychains/System.keychain ~/.mitmproxy/mitmproxy-ca-cert.pem
https://docs.mitmproxy.org/stable/concepts-certificates/
ここまで実施できればあとは必要に応じてmitmproxyに送信したいドメインをローカルに向ければよい。