2016年6月3日
私のURLはあなたのURLとは違う : curl作者の語る、URLの仕様にまつわる苦言
(2016-05-11)by Daniel Steinberg
本記事は、原著者の許諾のもとに翻訳・掲載しております。
1996年にcurlプロジェクトの先駆けとなるhttpgetを始めたとき、私は初めてURLパーサを書きました。当時はまだ、ユニバーサルアドレスは URL : Uniform Resource Locators と呼ばれていました。その仕様は1994年にIETFによって発行されたものでした。この”URL”という用語からインスピレーションを得てツールとプロジェクトに命名したのが curl でした。
URLという用語は後に事実上、 URI : Uniform Resource Identifiers (2005年発行)に変わりましたが、「オンラインでリソースを指定する文字列のための構文と、そのリソースを得るためのプロトコル」という、基本的な点は変わりませんでした。curlでは、この構文仕様RFC 3986の定義に従う”URL”を許容するとうたっていますが、それは厳密には正しくありません。その理由を、これから説明します。
また、この仕様の仲間に、 IRI :Internationalized Resource Identifiersのために発行されたRFCがありました。IRIは基本的にはURIですが、非ASCII文字列の使用が許可されます。
後に、 WHATWGコンソーシアム が独自の URL仕様 を作成し、これは基本的にはURIとIRIの書式と概念を、(驚くようなことではありませんが)ブラウザを強く意識して組み合わせたものでした。WHATWGが 表明した目標 の1つは、 「現時点の実装にRFC3986とRFC3987を沿わせ、その過程の中で廃止していく」 ということです。URIとIRIという用語は混同しやすく、きちんと理解している人は誰もいない(多くの人は、その存在すら知らない)という、彼らの主張は正当でした。
WHATWGの仕様は、「何を許容するかについて寛容であれ、ユーザの意図を推察してそれを満たせるよう尽力せよ」という、古き良きブラウザの信条に従っています。(今では誰もが、この場合には ポステルの法則 は間違ったやり方だと知っていますが)。つまり、多すぎるスラッシュ、埋め込まれた空白、それに非ASCII文字を処理しなければならないのです。
私の考えでは、この仕様もまた読み辛く、使いにくいものです。その理由は、この仕様は構文や書式の説明は少ないのに、 ある構文解析アルゴリズムを強制する からです。試しに、この仕様ではURL内のホスト名にドットが続くとどうなのか考えてみれば、私の主張がお分かりになると思います。
このような標準と仕様に加えて、ブラウザには「アドレスバー」(UIの一部で、他の名前で呼ばれることもある)があり、ユーザはこれを使って、ありとあらゆる変な文字列を入力することができ、その文字列がURLに変換されるのです。アドレスバーに”http://localhost/%41″と入力するとパーセントエンコーディングされた部分が’A’に変換されます(16進数の41はASCIIでは大文字のAだからです)が、”http://localhost/A A”と入力すると、実際には、”/A%20A”(パーセントエンコーディングされた空白が入る)がHTTP GET要求で送信されます。私がこの点についてふれた理由は、人は普通、アドレスバーに入力が可能なものを”URL”と考えがちだからです。
ここまでは基本的に、現在までに使ってきた仕様と標準についての私の(歪んだ)考えです。ここで現実性を考えて、 私のURLがあなたのURLと違う とどんな問題が起こるのかについて考えてみましょう。
そもそもURLとは何か?
もっと具体的に言うなら、どうやってURLを書けばいいのでしょうか。どんな構文を使うのでしょうか。
WHATWGの仕様が犯した最大の過ち(そして、「 間違っている のは彼らだ」という強烈な信念をもって、私が現在の形の仕様に反対する理由)の1つは、彼らがURLを「自分たちが取り組んで規定する、自分たちのもの」であると信じ、URLをブラウザ、HTML、およびアドレスバー向けに限定して考えていることだと思います。もちろん、WHATWGのメンバーはほとんどの人々が使うブラウザの背後にある大企業であり、URLはブラウザによって広く使われていますが、URLには、それより ずっと 大きな目的があります。
URLに対するWHATWGの考え方はブラウザ以外では広く受け入れられていません。
コロン-スラッシュ-スラッシュ
プロトコルやウェブの経験を特に持たない一般のユーザは、URLとは何かと問われると、何と答えるでしょうか? ブラウザがURLをよりはっきりと表示したのはおそらく何年も前ですが、://(コロン-スラッシュ-スラッシュ)は回答リストの上位になるでしょう。それを見れば、その文字列をURLだと思うのです。
ユーザだけではありません。ユーザの代わりにURLを検出して、そのURLに対する操作を可能にしてくれるEメールクライアント、ターミナルエミュレータ、テキストエディタ、Perlスクリプト、その他のものが世界中に既に無数に存在します。ブラウザ内でURLを開いたり、生成したHTML内でクリック可能なリンクにURLを変換したり、多くの操作があるでしょう。そういった膨大な量のスクリプトやプログラムが、URLの目印としてコロン-スラッシュ-スラッシュ文字列を使うのです。
WHATWGの仕様では、スラッシュ-スラッシュは ひとつ とみなす必要があり、構文解析プログラムは無限にスラッシュを許容しなければならないのです。”http:/example.com”と”http:////////////////////////////////////example.com “はどちらも問題ありません。しかし、RFC3986や多くの人はそうは思っていないようです。Web関連の仕事をしている人も中にいるのですが、最近これについて話した人の多くは、URLにはスラッシュが2つあると言ったり、思ったり、信じたりしているのです。この記事の上部にあるGoogleの画像検索のスクリーンショットを見てください。”URL”の画像検索の結果をスクリーンショットにしています。
URLに2つスラッシュがあるのは分かっています(file:パスの場合のURLはスラッシュが3つありますが、ここでは無視しましょう)。1つでもなく、3つでもなく、2つなのです。しかし、WHATWGでは異を唱えています。
“file:以外のURLに3つ以上のスラッシュを許容する根拠は本当にあるのでしょうか? ”(WHATWGに対するいら立ちを質問にしました)。
“事実上、すべてのブラウザが3つ以上のスラッシュを許容している”
ブラウザは仕様どおりに実装されているので、仕様にはそう書かれているはず。
多くのブラウザが3つのスラッシュを許容していないことを指摘した後も、納得できる説明はされていません。上のスレッドは勉強になると思いますよ。
curlプロジェクトでは、スラッシュが2つではない”URL”をどのように扱うべきなのか、最近になって話し始めました。実際にサーバによっては、2つではないURLを Location:ヘッダ で返し、ブラウザによっては許容しています。ブラウザの中にはスラッシュが2つではないURLを許容するものもありますが、curlでは許容していませんし、他の多くのライブラリやコマンドラインツールでも許容されていません。curlプロジェクトはスラッシュが2つ以外でも許容するべきなのでしょうか、それともスラッシュが2つ以外のURLは許容しないとするべきなのでしょうか。
空白
空白文字(ASCIIコード32、16進数では0x20)はURLに含むことができません。送信したい場合は他の禁則文字を含む時と同じようにパーセントエンコーディングをします。パーセントエンコーディングとは、16進数のバイトの値の前に%を付けて表記する方法です。空白の場合は%20となります。つまり、構文解析プログラムがURLのテキストをスキャンした時、空白などの禁則文字に到達すればURLの最後であることが分かります。
大抵の場合ブラウザは見栄えのため、アドレスバーに全ての%20インスタンスを空白に変換してアドレスを表示します。しかし、アドレスバーに表示されたアドレスをクリップボードにコピーし、テキストエディタに貼り付けても、期待どおりに空白は%20として表示されます。
これが理由かは定かではありませんが、ブラウザがURLの一部として空白を許容する場合があります。例としては、HTTPレスポンスでリダイレクトを受信する場合などです。サーバからクライアントに渡す時にURLを含むLocation:ヘッダを使用します。この場合、ブラウザは問題なくURLの空白を許容し、%20と符号化し、次のリクエストを送信します。このことから、curlでもリダイレクトされた”URL”の空白を許容せざるを得なくなりました。
非ASCII
非ASCII言語もURLでサポートする必要はもちろんあります。特に、非西洋社会においては、IRI仕様では十分ではないことを理解しています。私はこれらにおける国際化(i18n)のエキスパートとは到底言えないので、他の人から聞いたことしか分かりません。しかし、アルファベット以外の文字を使用、タイプする場合でも”インターネットアドレス”をリソースに付けリンクとして使用できないといけません。
理想的な世界においては、i18nバージョンはユーザには見え、ユーザの見えないところでは、送受信を可能にするべくASCIIベースのバージョンに符号化されるのです。
国際化ドメイン名(IDN)では、非ASCII文字の名前の取り扱いができない通常のシステムネームリゾルバを使っても解決できるように、ドメイン名は”punycode”に変換されます。URIには国際化ドメイン名(IDN)はなく、IRIやWHATWG URLにはあります。curlでもIDNホスト名をサポートしています。
WHATWGでは、URLはUTF-8とされていますが、URIは単なるASCIIとしています。curlはパス部分に非ASCII文字があれば混乱しますが、ASCII文字のバイト値はパーセントエンコードしてリクエストとして送信します。すると、Windowsでは標準となっているUTF-8以外の方式で非ASCII文字が符号化された場合、”面白い”副作用が引き起こされます。
非ASCIIのバイト値をパーセントエンコードして送ると、サーバも同様にブラウザが許容できるようHTTPヘッダの中に非ASCIIバイトコードを入れて返します。ブラウザ以外のものは苦労してそれを処理しなければなりません。
URL標準は存在しない
問題や相違点の決定的なリストを書くというよりは、最近ぶち当たった多くの問題を挙げてみただけです。1つの場所に与えられた”URL”は”URL”として他の場所で必ずしも許容される、理解されるとは限りません。
curlでさえ最近では仕様書に厳密ではなく、”Webの互換性”のために少しずつ脱線してきています。
URLの統一された標準はなく、統一に向けた進捗も見られません。WHATWGの仕様も閉鎖的なグループで書かれていて、広いコミュニティを巻き込む試みもないため、統一に向けた取り組みとは言えません。
所属
WHATWGのメンバーでもあるMozillaで働いて、WHATWGのURL仕様や関係のWHATWGに関係のある作業をしている同僚もいますが、ここに書いた内容に変わりはありません。IETFにも参加していて、RFC 1738やRFC 3986の著者との交友関係もありますが、それでも内容に変わりはありません。これはあくまでも私個人の意見であり、私個人のブログなのです。
株式会社リクルート プロダクト統括本部 プロダクト開発統括室 グループマネジャー 株式会社ニジボックス デベロップメント室 室長 Node.js 日本ユーザーグループ代表
- Twitter: @yosuke_furukawa
- Github: yosuke-furukawa