RubyではOpenURIを使うとウェブページを開くことができる。OpenURIでは引数に与えたURIのプロトコルがHTTPリダイレクト先がHTTPSの場合、エラーが発生するという作りになっている。
ところで、HTTPからHTTPSへのリダイレクトは意外とたくさんある。
例えばGoogleで短縮URL。
リダイレクト先はここ。
https://github.com/pi-chan/open_uri_allow_redirect
% irb
irb(main):001:0> require "open-uri"
=> true
irb(main):002:0> open "http://goo.gl/84556T"
RuntimeError: redirection forbidden: http://goo.gl/84556T -> https://github.com/pi-chan/open_uri_allow_redirect
from /Users/hiromasa/.rbenv/versions/2.1.0/lib/ruby/2.1.0/open-uri.rb:223:in `open_loop'
from /Users/hiromasa/.rbenv/versions/2.1.0/lib/ruby/2.1.0/open-uri.rb:149:in `open_uri'
from /Users/hiromasa/.rbenv/versions/2.1.0/lib/ruby/2.1.0/open-uri.rb:703:in `open'
from /Users/hiromasa/.rbenv/versions/2.1.0/lib/ruby/2.1.0/open-uri.rb:34:in `open'
from (irb):2
from /Users/hiromasa/.rbenv/versions/2.1.0/bin/irb:11:in `<main>'
irb(main):003:0>
このようなケースでは、そのままのOpenURIは使えない。
open_uri_redirections
Rubyだから既存のクラスを拡張してしまえばよい。探してみるとOpenURIでHTTPSへのリダイレクトを許可するような拡張をしているgemがあった。
jaimeiniesta/open_uri_redirections
このgemにより、openメソッドでallow_redirections
というオプションを扱えるようになる。HTTPからHTTPSのリダイレクトやその逆のリダイレクトを許可するかどうかを呼び出し元が制御できるようになるというわけ。
require "open-uri"
require "open_uri_redirections"
open('http://github.com', :allow_redirections => :safe)
open('http://github.com', :allow_redirections => :all)
open
の中で使われるredirectable?
というメソッドを置き換えることによってこれを実現している。
このgemの問題点
open
の呼び出しを自分で書くときはいいが、ライブラリなどの自分で手を加えにくいコードがopen
を呼んでいる場合にはこのgemでは対応しきれない。
open_uri_allow_redirect作った
その問題を解決するために、少しばかり改変したgemを作った。
pi-chan/open_uri_allow_redirect
このgemではopen
の引数を使わず、OpenURI.allow_redirect
に渡したブロック内のopen
呼び出しは全てのリダイレクトを許可するという作りになっている。
こんな感じで使える。
require "open_uri_allow_redirect"
open("http://github.com") # => raises RuntimeError, redirected to https://github.com
OpenURI.allow_redirect do
open("http://github.com") # => no error
end
ライブラリのコードがopen
を呼んでいるときにも対応できる。
# in other library
module MyLibrary
def use_open_method_internally(uri)
open(uri)
end
end
# main
require "open_uri_allow_redirect"
OpenURI.allow_redirect do
MyLibrary.use_open_method_internally("http://github.com/") # => no error
end
いえい。