LoginSignup
52
48

More than 5 years have passed since last update.

Ruby 2.1 と 2.2 における、URI#parseの挙動の違い

Last updated at Posted at 2015-08-02

症状

Ruby 2.1では、URIに使用できない文字(アンダースコア、アンダーバー)を含んだ文字列( http://abc_def.com/foobar/ ※1)をURI#parseに与えた際にURI::InvalidURIErrorの例外が発生する。

2015/08/15追記:
※1…この文字列はURIとしては RFC違反 です。ホスト名にアンダースコア、アンダーバーを含むことはできません。@key-amb様、ご指摘頂きありがとうございます。

なお、この記事では
「RFC違反の文字列に対して、同じURI#parseを使っているが、Rubyのマイナーバージョンに依って挙動が違う」
という点にのみ焦点を当てて、実際にどのように異なっているのか、Ruby2.2で2.1の挙動が欲しい場合にどうすれば良いのかについて論じます。

pry
[1] pry(main)> require 'uri'
=> true
[2] pry(main)> url="http://abc_def.com/foobar/"
=> "http://abc_def.com/foobar/"
[3] pry(main)> URI.parse url
URI::InvalidURIError: the scheme http does not accept registry part: abc_def.com (or bad hostname?)

しかし、Ruby 2.2では、そのような文字列も問題なくパースできてしまう。

pry
[1] pry(main)> require 'uri'
=> true
[2] pry(main)> url="http://abc_def.com/foobar/"
=> "http://abc_def.com/foobar/"
[3] pry(main)> URI.parse url
=> #<URI::HTTP http://abc_def.com/foobar/>

原因

標準ライブラリのURIが参照するRFCが変更されたため。
古い RFC2396ではなく、新しい RFC3986 を参照し、Ruby 2.2からはこれに準拠するようになったため。

RFC2396に準拠したパースを行うには

URI::RFC2396_Parserを使用する。またはURI::Parserを使う。

URI::RFC2396_Parserを使う

pry
[1] pry(main)> require 'uri'
=> true
[2] pry(main)> url="http://abc_def.com/foobar/"
=> "http://abc_def.com/foobar/"
[3] pry(main)> p=URI::RFC2396_Parser.new
=> #<URI::RFC2396_Parser:0x007fb8ac036eb8>
[4] pry(main)> p.parse url
URI::InvalidURIError: the scheme http does not accept registry part: abc_def.com (or bad hostname?)

URI::Parserを使う

pry
[1] pry(main)> require 'uri'
=> true
[2] pry(main)> url="http://abc_def.com/foobar/"
=> "http://abc_def.com/foobar/"
[3] pry(main)> p=URI::Parser.new
=> #<URI::RFC2396_Parser:0x007f96ad317e50>
[4] pry(main)> p.parse url
URI::InvalidURIError: the scheme http does not accept registry part: abc_def.com (or bad hostname?)

参考

補足

Ruby 2.2, 2.1 標準のURIライブラリでは、日本語などのマルチバイト文字を含むURIをparseすることができません。

pry
[1] pry(main)> require 'uri'
=> true
[2] pry(main)> URI.parse 'http://example.com/日本語'
URI::InvalidURIError: URI must be ascii only "http://example.com/\u{65e5}\u{672c}\u{8a9e}"
[3] pry(main)>
[4] pry(main)> p = URI::Parser.new
=> #<URI::RFC2396_Parser:0x007fb3c1341300>
[5] pry(main)> p.parse 'http://example.com/日本語'
URI::InvalidURIError: bad URI(is not URI?): http://example.com/日本語


[6] pry(main)> URI.parse 'http://example.com/Ω'
URI::InvalidURIError: URI must be ascii only "http://example.com/\u{3a9}"
[7] pry(main)> p.parse 'http://example.com/Ω'
URI::InvalidURIError: bad URI(is not URI?): http://example.com/Ω

addressableというgemを使用すると、マルチバイト文字もparseできます。RFC 3986, RFC 3987, RFC 6570に準拠しているようです。

pry
[1] pry(main)> require 'addressable/uri'
=> true
[2] pry(main)> url = 'http://example.com/日本語'
=> "http://example.com/日本語"
[3] pry(main)> Addressable::URI.parse url
=> #<Addressable::URI:0x3fe81a09b900 URI:http://example.com/日本語>

謝辞

修正前の記事は、「RFC3986によって、ホスト名にアンダーバーを含むことができるようになった」との誤解を与えかねない記事でした。また、更新によって有用なgemの紹介もすることができました。以下の皆様のご指摘で記事を修正、更新することができました。感謝の意を表明致します。

@key-amb
id:tmatsuu
id:tohokuaiki
id:toyama0919

Original

Back of Flier: Ruby 2.1 と 2.2 における、URI#parseの挙動の違い

52
48
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
52
48