NaClの中村です。
OSSに関するイベントの会場などでたまに「OSSにコードコントリビュートしたいんですけどなかなかできなくて…」と聞くことがあります。
私も含め、OSSのイベントに参加されるような方は単にOSSを利用するだけでなく、コードコントリビュートしたい人が多いようです。
確かに自分が書いたコードが広く普及しているOSSに入り、いろんなところで動くのは気持ちのよいものです。
しかし、我々プログラマは日頃のお仕事もあるし、家に帰れば猫の世話に追われ、見たいアニメもある。
ツイッターでは「すごーい!」とつぶやきたいし、とても多忙な日々を過ごしています。
パッチを書いている暇はないのです。
そこでお仕事の時間を利用してパッチを書いてみようというのが今回の趣旨です。
この記事では私が実際にお仕事をしつつパッチを書いた例を見つつ、どのようにOSSにコードコントリビュートするのか紹介したいと思います。
お仕事で以下のような状況がありました。

外部にでるときはプロキシ経由を利用してHTTPアクセスし、内部へのHTTPアクセスはプロキシを経由せずに直接つなぎに行きます。
HTTPアクセスにはfaradayというライブラリを使っており、イメージとしては以下のようなコードを動かしていました。
ところが実際に動かしてみると環境変数のno_proxyがうまく動いていないことがわかりました。
ここで仕事の時間を利用してno_proxyについて調査をはじめます。
環境変数のno_proxyは指定したIPアドレスに対しプロキシ経由させない設定のようです。
またno_proxyの対応状況はHTTPクライアントによって異なるみたいです。
ということで 今回はRubyの対応状況について調べてみましょう。
Ruby2.0より前のNet::HTTPでは以下のように明示的にプロキシを設定する方法しかありませんでした。
Ruby2.0からFeature #6546が取り込まれ、明示的にプロキシを指定しない場合にはそれぞれの環境変数を見てくれるようになりました。
調べてみた結果、Net::HTTPはno_proxyをサポートしていることがわかりました。
faradayは現存するRubyのHTTPクライアントに共通のインターフェースを設けるラッパーです。
faradayを使っていさえいれば中身にNet::HTTPを使ってもいいし、途中でHTTPClient乗り換えてもいいというものです。
はじめに述べた問題のコードではfaradayを経由してNet::HTTPを利用していました。
Net::HTTPではno_proxyをサポートしているので、どうもfaradayが何かしていそうです。
ここでfaradayのコードを眺めに行きます。
どうやらコネクションインスタンスの初期化で環境変数のhttp_proxyを見て@proxyに設定するみたいです。
次にNet::HTTPとのアダプター部分を見てみましょう。
lib/faraday/adapter/net_http.rb:88
env[:request][:proxy]には環境変数のhttp_proxyが入ります。
コードを読むとhttp_proxyを指定した時は明示的なプロキシの指定(Net::HTTP::Proxy)をしているようです。
上記の明示的な指定にはno_proxyの考慮がないため、うまく動いていなかったようです。
ここまでライブラリを調べたあと、私の場合は利用者側で回避できないか検討します。
というのはライブラリ側としてはこれが実は正しい挙動で、別の方法が利用者側に提供されているかもしれないからです。
faradayの利用側で、no_proxy見てくれない問題を回避する方法は以下のとおりです。
なんやかんやあって引数のproxyに空文字列いれたらよさそうですが…。
このコードをパッと見てもなんでこうなるのかわからないし、これはライブラリ側を直したほうがよさそうという結論にいたります。
ここでfaradayをどういう風に直したらいいのか考えます。
faradayは様々なHTTPクライアントのラッパーです。
環境変数http_proxyやno_proxyの扱いは各HTTPクライアントでまちまちですが、ここの差分も吸収したいのでしょう。
ですが、faradayはその扱いが雑でno_proxyの考慮ができていませんでした。
この辺りを独自で実装するのは面倒ですので色々と調べてみます。
そうするとRubyにURI::Generic#find_proxyという便利なメソッドを見つけました。
これをうまくfaradayに組み込めばうまく行きそうですね。
ということでパッチを書いてみます。
実際には互換性なども考慮してもう少し複雑ですが、大体こんな感じです。
接続先のurlに対してfind_proxyを呼んでやることで以下のように
no_proxyの対象ならnilが@proxyに設定される@proxyに設定されるno_proxyを考慮したコードになります。
利用側でも
と謎の空文字を使わなくて済みました。
あとは頑張って英語とかGoogle翻訳を駆使して本家にプルリクエストをおくりましょう。
Support no_proxy via URI::Generic#find_proxy
議論の結果、ちゃんと取り込まれたみたいですね!
今回は仕事中に困ったことからOSSへ実際にパッチを送るまでの流れをみました。
仕事でおやっ?と思ったことがあれば調べてみて、隙があればパッチを送ってみましょう。
そしてマージされたらどんどん自慢しましょう。ちょっとした自信につながるはずです。