Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

症状

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の挙動の違い

dogwood008
株式会社CUUSOO SYSTEMのCTO兼フルスタックエンジニアです。Rails, 機械学習, Androidが好きです。AIで株取引するやつやってます。 https://ai-deep-kabu.com https://blog.ai-deep-kabu.com 発言は個人の見解であり、所属する団体等の公式見解ではありません。
https://blog.dogwood008.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした