Studying HTTP

Help

Uniform Resource Identifiers

URI とは

HTTP で使われる URI とはどのようなものでしょうか? RFC 2616 の section 3.2 では、HTTP で使用される URI について記述されています。

URI とは、既に多くの名前で、例えば WWW address, Universal Document Identifier, Universal Resource Identifier, そして、最終的に Universal Resource Locator (URL) と Name (URN) という名で知られている。 HTTP に限って言えば、Uniform Resource Identifier とはリソースを名前や場所、その他の特徴で識別する簡潔に書式化された文字列の事である。

URI とはリソースを名前や場所、その他の特徴で識別する簡潔に書式化された文字列とありますが、もう少し詳しく見てみる事にしましょう。 URI について定義している RFC のうち、最新のものである RFC 3986 をご覧下さい。

URI は、以下のように特徴付けられる:

統一書式 {Uniform}
統一書式性にはいくつかの利点がある。 異なる型のリソース識別子を同じ状況の中で、例えそれらの資源にアクセスするためのメカニズムが異なっていても、使う事ができる。 異なる型のリソース識別子に共通した構文の慣習を、同一の意味論で解釈できる。 既に存在する識別子が使用する方法を妨げる事無く新たな型のリソース識別子を導入できる。 識別子をさまざま異なった状況で再利用でき、それゆえ新たなアプリケーションやプロトコルは既に存在する、巨大で広く用いられているリソース識別子の集合を活用できる。
リソース {Resource}
この仕様書では、リソースであるものの範囲を制限しない; むしろ、"リソース" という用語は、URI によって識別される全てのものについて一般的な意味において使用される。 よく知られた例としては、電子文書、画像、一貫性のある目的をもつ情報源 (例えば、"本日のロサンゼルスの天気予報")、サービス (例えば、HTTP から SMS へのゲートウェイ)、またその他のリソースの集合等がある。 リソースは、インターネットを通じてアクセス可能である必要はない; 例えば、人間、企業、及び図書館にある装丁された書籍も、リソースとみなす事ができる。 同様に、数学の方程式の演算子やオペランド、関係の種類 (例えば、"親" や "会社員")、あるいは数値 (例えば、"0" や "1", "無限大") のような、抽象的な概念もリソースとなりうる。
識別子 {Identifier}
識別子は、識別の範囲内の他の全てのものから特定されている事を区別するために必要な情報を具体化する。 我々は、"識別" や "識別する" という用語を、あるリソースを他の全てのリソースから区別する目的のために用い、その目的がどのように (例えば、氏名、住所、あるいは状況によって) 達成されるかにかかわらない。 これらの用語は、識別子が、いくつかの識別子の場合ではそうでなくても、参照されるものの識別を定義する、あるいは具体化するという事の前提であると誤用されるべきではない。 また、URI を使用するシステムがリソースを識別してアクセスするという事を仮定すべきではない: 多くの場合、URI は、それらがアクセスされるといういかなる意図もなしにリソースを表すために使用される。 同様に、識別される "一つの" リソースは、本質的に唯一である必要はない (例えば、リソースとは、名付けられた一式のもの、あるいは時間経過と共に変化するものへの写像でありうる)。

すなわち、URI とは統一された書式を持った、リソースを識別するための文字列であると言えます。 「統一された」とある通り、URI は HTTP だけで使用される概念ではなく、他のプロトコルやサービス、あるいはファイルシステムでも使用されます。 RFC 3986 中にあるいくつかの例をご覧下さい。

 ftp://ftp.is.co.za/rfc/rfc1808.txt

 http://www.ietf.org/rfc/rfc2396.txt

 ldap://[2001:db8::7]/c=GB?objectClass?one

 mailto:John.Doe@example.com

 news:comp.infosystems.www.servers.unix

 tel:+1-816-555-1212

 telnet://192.0.2.16:80/

 urn:oasis:names:specification:docbook:dtd:xml:4.1.2

ところで、URI に似た語で URL なる語もありますが、この違いとは何でしょうか。 これも RFC 3986 の Section 1.1.3 に記述されています。

URI は、それが位置指定子か、名前か、あるいはその両方かという点において、更に分類できる。 "Uniform Resource Locator" (URL) という用語は、リソースを識別するのに加えて、その主なアクセスメカニズム (例えば、そのネットワーク上の "位置") を記述する事によってリソースの場所を見つける方法を提供するような、URI の部分集合を指す。 "Uniform Resource Name" (URN) という用語は、例えそのリソースが存在しなくなったり、あるいは利用不可能になっても全体において一意で永続的である事が要求される "urn" スキーム [RFC2141] の下での両方の URI、また名前の特性を持つあらゆる他の URI を参照するために歴史的に使用されている。

個々のスキームは、"名前" あるいは "位置指定子" のどちらか一方に分類される必要はない。 あらゆる与えられたスキームからの URI の例でも、名前、位置指定子、あるいは両方の特性を持っているかもしれないし、これはしばしばスキームのどんな品質よりも、命名の権威{naming authority} による識別子の割り当てにおける永続性と配慮に依存する。 将来の仕様書や関連文書では、より制限された用語である "URL" や "URN" よりも一般的な用語である "URI" を使用すべきである [RFC3305]。

すなわち、URL がリソースのある位置を指定するもので、URN がリソースの名前となります。 これに対し、URI は "Identifiers"、すなわち識別用文字列という名前からもわかる通り、URI は URL 及び URN の上位集合です。 HTTP のみに限って言えば、URI と URL はほぼ同じ意味を持ちますが、HTTP に関する文書を記述する場合は、特に「位置指定子」という意味を強調したい場合以外は、URI を使用すべきです。

パーセントエンコーディング

パーセントエンコーディングとは、ある文字を "%" と 16 進数字 2 つを用いて変換する処理の事を言います。 RFC 3986 の Section 2.1 をご覧下さい。

パーセントエンコーディング{percent-encoding} メカニズムは、オクテットの対応する文字が認められた文字の範囲外にある、あるいは構成要素の中で区切り氏として使用されている場合に使用される。 パーセントエンコードされたオクテットは、パーセント文字 "%" と、そのオクテットの数値を表している二桁の 16 進数字から成る三重語としてエンコードされる。 例えば、"%20" は 2 進オクテット "00100000" (ABNF: %x20) についてのパーセントエンコーディングであり、US-ASCII のスペース文字 (SP) に対応している。 Section 2.4 は、パーセントエンコーディングとデコーディングが適応される時について記述している。

 pct-encoded = "%" HEXDIG HEXDIG

大文字の 16 進数字 'A' から 'F' は、小文字の 'a' から 'f' とそれぞれ等価である。 二つの URI のパーセントエンコードされたオクテット内で使用される 16 進数字の大文字・小文字のみが異なる場合、それらは等価である。 整合性を持たせるため、URI の生成を行うもの{producers} や正規化を行うもの{normalizers} は全てのパーセントエンコーディングについて大文字の 16 進数字を使用すべきである。

この "% HEXDIG HEXDIG" 形式のものは、これまで「URI(URL) エスケープ」とか「URI(URL) エンコード」等と呼ばれてきましたが、RFC 3986 では「パーセントエンコーディング」という言葉をもって定義されましたので、今後はこの言葉を用いるようにしましょう。

パーセントエンコーディングにおいて、16 進数字の大文字と小文字は等価です。 例えば、"=" (等号) をパーセントエンコーディングすると、"%3D" あるいは "%3d" となりますが、これは等価となります。 但し、整合性を持たせるために、例えば HTML 文書内でパーセントエンコーディングを使用する場合は、大文字である "%3D" の方を利用しましょう。

URI 内で使用できる文字

URI 内で使用できる文字について、RFC 3986 の Section 2.2, 2.3 をご覧下さい。

URI は、"予約されている" 集合内の文字によって区切られる構成要素及び副構成要素を含んでいる。 これらの文字は、URI の逆参照アルゴリズムにおける一般的な構文、各スキーム特有の構文、あるいは実装特有の構文によって区切り子として定義される (あるいはされない) ので、"予約されている" と呼ばれる。 URI 構成要素についてのデータがデリミタとして予約されている文字の目的と競合する場合、競合するデータは URI が形成される前にパーセントエンコーディングされなければならない。

 reserved    = gen-delims / sub-delims

 gen-delims  = ":" / "/" / "?" / "#" / "[" / "]" / "@"

 sub-delims  = "!" / "$" / "&" / "'" / "(" / ")"
             / "*" / "+" / "," / ";" / "="

予約されている文字の目的は、URI 中の他のデータから区別可能である区切り文字の集合を提供する事である。 予約文字を、対応するパーセントエンコードされたオクテットに置き換えている点が異なる URI は、等価ではない。 ほとんどのアプリケーションにおいて、予約文字をパーセントエンコーディングする、あるいは予約文字に対応するパーセントエンコードされたオクテットをデコードする事によって、URI がどう解釈されるかが変わるであろう。 従って、予約されている集合内の文字は正規化{normalization} から保護されており、それ故に、それらは URI 内のデータ副構成要素を区切るためにスキーム特有、あるいは生成を行うもの特有のアルゴリズムによって使用されても安全であるだろう。

URI 内に含む事が認められており、予約目的のない文字は、予約されていない (unreserved) と呼ばれる。 これは、大文字と小文字のアルファベット、数字、ハイフン、ピリオド、アンダースコア、チルダが含まれる。

 unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~"

非予約文字を、対応するパーセントエンコードされた US-ASCII オクテットに置き換えている点が異なる URI は、等価である: それらは同じリソースを識別する。 しかし、URI 比較を行う実装が常に比較の前に正規化を行うわけではない (Section 6 参照)。 整合性を持たせるため、URI の生成を行うものは ALPHA (%41-%5A と %61-%7A), DIGIT (%30-%39), hyphen (%2D), period (%2E), underscore (%5F), tilde (%7E) の範囲におけるパーセントエンコードされたオクテットを生成すべきではないし、URI を発見した時に、URI 正規化によってそれらを対応する非予約文字にデコードすべきである。

RFC 3986 では、区切り子として用途が決まっている記号の集合を予約文字としました。 これに対し、それ以外の文字を非予約文字 (予約されていない文字)と呼ばれます。 たとえば、"=" (等号) は予約文字なので、(特に、クエリ部分で) URI として使用する場合には特にパーセントエンコードする必要がありますが、"~" (チルダ) は非予約文字なので、その必要がありません。

HTTP で使われる URI について

HTTP で使う事のできる URI にはどのような決まりがあるのでしょうか? RFC 2616 の section 3.2.2 以降に詳しく記述されています。

"http" スキームは HTTP プロトコル経由でネットワークリソースの位置を指すために使われる。 この節では http URL について、スキーム特有の構文と意味論を定義する。

 http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]

もし、port が空かあるいは与えられていなければ、ポート 80 が仮定される。 その意味論は、識別されるリソースはそのホストのそのポートで TCP 接続のためのリスニングをしているサーバ上にあり、リソースに対すする Request-URI は、abs_path である (section 5.1.2)。 できれば、URL での IP アドレスの使用は避けるべきである (RFC 1900 参照)。 もし、abs_path が URL で与えられていなければ、そのリソースへの Request-URI として使われる時に、"/" が与えられなければならない (section 5.1.2)。 もし、プロクシが fully qualified domain name ではない ホストネームを受けとったならば、プロクシは自分のドメインにそのホストネームを追加する事ができる。 もし、プロクシが fully qualified domain name を受けとったならば、プロクシは絶対にホストネームを書き換えてはならない

上記中の host, port, abs_path, query 等の定義については、RFC 2396 の付録 A. において、拡張 BNF で記述されています。

 URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
 absoluteURI   = scheme ":" ( hier_part | opaque_part )
 relativeURI   = ( net_path | abs_path | rel_path ) [ "?" query ]

 hier_part     = ( net_path | abs_path ) [ "?" query ]
 opaque_part   = uric_no_slash *uric

 uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" |
                 "&" | "=" | "+" | "$" | ","

 net_path      = "//" authority [ abs_path ]
 abs_path      = "/"  path_segments
 rel_path      = rel_segment [ abs_path ]

 rel_segment   = 1*( unreserved | escaped |
                     ";" | "@" | "&" | "=" | "+" | "$" | "," )

 scheme        = alpha *( alpha | digit | "+" | "-" | "." )

 authority     = server | reg_name

 reg_name      = 1*( unreserved | escaped | "$" | "," |
                     ";" | ":" | "@" | "&" | "=" | "+" )

 server        = [ [ userinfo "@" ] hostport ]
 userinfo      = *( unreserved | escaped |
                    ";" | ":" | "&" | "=" | "+" | "$" | "," )

 hostport      = host [ ":" port ]
 host          = hostname | IPv4address
 hostname      = *( domainlabel "." ) toplabel [ "." ]
 domainlabel   = alphanum | alphanum *( alphanum | "-" ) alphanum
 toplabel      = alpha | alpha *( alphanum | "-" ) alphanum
 IPv4address   = 1*digit "." 1*digit "." 1*digit "." 1*digit
 port          = *digit

 path          = [ abs_path | opaque_part ]
 path_segments = segment *( "/" segment )
 segment       = *pchar *( ";" param )
 param         = *pchar
 pchar         = unreserved | escaped |
                 ":" | "@" | "&" | "=" | "+" | "$" | ","

 query         = *uric

 fragment      = *uric

 uric          = reserved | unreserved | escaped
 reserved      = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
                 "$" | ","
 unreserved    = alphanum | mark
 mark          = "-" | "_" | "." | "!" | "~" | "*" | "'" |
                 "(" | ")"

 escaped       = "%" hex hex
 hex           = digit | "A" | "B" | "C" | "D" | "E" | "F" |
                         "a" | "b" | "c" | "d" | "e" | "f"

 alphanum      = alpha | digit
 alpha         = lowalpha | upalpha

 lowalpha      = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" |
                 "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" |
                 "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
 upalpha       = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
                 "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
                 "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
 digit         = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
                 "8" | "9"

では、http://www.studyinghttp.net:80/foo?bar#baz という URL を、その構文に従って解析していきましょう。

scheme (http)
リソースにアクセスするための手法を識別します。 http_URL の場合、schemehttp で確定です。
host (www.studyinghttp.net)
ホスト名、すなわちそのデータを持っているコンピュータの名前を指定します。 IP アドレスによる指定も可能ですが、FQDN による指定が推奨されます (RFC 1900 参照)。
port (80)
ポート番号。 サーバはポート番号別にサービスを振り分けて、通信を待ちうけます。 HTTP 通信の場合は通常 80 を使用しますが、それ以外のポート番号も使用できます。 ポート番号が明示されていない時は 80 を使用します。
abs_path (/foo)
絶対パス、すなわちそのコンピュータ内でのリソースの場所を指定します。 階層的な性質(ディレクトリ)を持つ URI では、/ を階層成分の区分に使います。
query (bar)
クエリ文字列。 絶対パスとの区切りには、? を使います。 指定したリソースに情報を渡す場合、例えば CGI にパラメータを渡す場合等に使います。
fragment (baz)
部分識別子。 絶対パスとの区切りには、# を使います。 RFC 3986 によれば、fragment も URI の中に含まれますが、依然として "http_URL" の中には fragment は含まれません。 HTTP において、fragment はリソース内の特定部分を指すものであり、即ち fragment はリソース取得後にのみ意味を持ちます。

ある URI が与えられた時、それが他のある URI と一致しているかどうかを判別する事はクライアントにとって重要な事です。 section 3.2.3 では、2つの URI の比較の際に注意すべき事について記述されています。

二つの URI が一致しているかどうかを決めるために比較する時、クライアントは URI 全体で大文字・小文字を区別したオクテット同士の比較をすべきであるが、以下は例外とする。

"reserved" あるいは "unsafe" セット (RFC 2396 参照) 以外の全ての文字は、それらを ""%" HEX HEX" エンコーディングしたものと等しい。(※)

(※訳注) "unsafe" セットは、RFC 2396 中には存在しない。

例えば、以下の三つの URI は等価である。

 http://abc.com:80/~smith/home.html
 http://ABC.com/%7Esmith/home.html
 http://ABC.com:/%7esmith/home.html

先の BNF にて示されたように、reservedunreserved 以外の文字は全てパーセントエンコーディングされます。

また section 3.2.1 では、URI の長さの制限について記述されています。

HTTP プロトコルでは、URI の長さにどんな制限も設けていない。 サーバは、自身が持つどんなリソースの URI も扱えなければならないし、もしそのような URI を生成する GET ベースのフォームを用意するなら、無制限の長さの URI を扱えるべきである。 もし、その URI がサーバが処理できるものよりも長ければ、サーバは 414 (Request-URI Too Long) ステータスを返すべきである (section 10.4.15 参照)。

注: いくつかの古いクライアントやプロクシ実装は 255 バイトを超える長さを持つ URI を適切にサポートしていないかもしれないので、サーバはそのような URI に頼る場合は注意を払うべきである。

以上の通り、HTTP の URI に関しての長さ (文字数) の制限は設けられていません。 但し、これを処理できるかどうかはサーバ依存で、サーバが長すぎて処理できないと判断した場合は 414 レスポンスが返されます。

リクエスト URI

リクエストを適用するリソースを指定するための URI をリクエスト URIと呼び、RFC 2616 の section 5.1.2 に記述されています。

Request-URI は、Uniform Resource Identifier (section 3.2) であり、リクエストを適用するリソースを識別する。

 Request-URI    = "*" | absoluteURI | abs_path | authority

Request-URI の四つのオプションは、そのリクエストの性質に依存する。

HTTP/1.1 では、リクエスト URI として 4 つのパターンが定義されています。

アスタリスク "*" は、そのリクエストが特定のリソースではなく、サーバ自身に適用するという事を意味し、使用されるメソッドがリソースに適用される必要がない時にのみ許される。 一例を挙げる。

 OPTIONS * HTTP/1.1

* というリクエスト URI は、OPTIONS というメソッドでのみ使用されます。 この URI は、特定のリソースではなく、サーバそのものを指定する時に使用します。

absoluteURI 形式は、リクエストがプロクシに対して生成されている時に要求される。 プロクシは、そのリクエストを転送するか、有効なキャッシュ内から提供する時に呼び出され、レスポンスを返す。 プロクシは、そのリクエストを別のプロクシか、あるいは absoluteURI によって特定されたサーバに直接送る事ができる事に注意せよ。 リクエストループを避けるために、プロクシはそのサーバ名をエイリアス、ローカルバリエーション、数値 IP アドレスまで含め、すべて理解できなければならないRequest-Line の例は以下の様になる。

 GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1

HTTP の将来のバージョンにおいてすべてのリクエストが absoluteURI へ移行する事ができるように、例え HTTP/1.1 クライアントはプロクシへのリクエストとしてのみしか生成しないものであったとしても、全ての HTTP/1.1 サーバはリクエストにおける absoluteURI 形式を受け入れなければならない

absoluteURI 形式は、文字通り絶対 URI、すなわち "http://" で始まる形式の URI です。 このタイプは、主にプロクシへのリクエスト時に使用されますが、将来のバージョンで absoluteURI 形式が使用されるかもしれないので、HTTP/1.1 サーバは absoluteURI 形式を受け入れなければなりません。

authority 形式は、CONNECT メソッド (section 9.9) のみで使用する。

CONNECT メソッドは、プロクシにトンネル接続の確立を要求する。 リクエストラインの Request-URI の部分は、常に URI 一般構文 [2] によって定義されるような 'authority' であり、リクエストされるコネクションの目的地となるホスト名とポート番号とをコロンで区切ったものである。

 CONNECT server.example.com:80 HTTP/1.1
 Host: server.example.com:80

authority 形式は、CONNECT メソッドで使用されます。 CONNECT メソッドは、プロクシにトンネル接続の確立を要求するためのメソッドなので、指定する authority はリソースのある場所ではなく、トンネル接続をするサーバとなります。

Request-URI の最も一般的な形式は、オリジンサーバやゲートウェイ上のリソースを識別するために使用される事である。 この場合、URI の絶対パスは Request-URI として通信されなければならない (section 3.2.1, abs_path 参照) し、URI のネットワークロケーション(authority) は、Host ヘッダフィールドにおいて転送されなければならない。 例えば、オリジンサーバから直接上記のリソースを回収する事を望むクライアントは、ホスト "www.w3.org" のポート 80 に TCP 接続を確立し、その行を送る。

 GET /pub/WWW/TheProject.html HTTP/1.1
 Host: www.w3.org

残りのリクエストをその後に送る。 絶対パスは空ではない事に注意せよ。 もし、元の URI で何も与えられていなければ、それは "/" (サーバのルート) が与えられなければならない

Request-URI は、section 3.2.1 に記された形式で送られる。 もし、 Request-URI に "% HEX HEX" エンコード [42] が使用されていたら、オリジンサーバはそのリクエストを適切に解釈するためにその Request-URIをデコードしなければならない。 サーバは、不正な Request-URI には、適切なステータスコードをもって応答すべきである

クライアントがオリジンサーバに直接接続するような場合では、通常 リクエスト URI には abs_path、すなわち絶対パスを用います。 (但し、前述の通り、HTTP/1.1 サーバは absoluteURI、すなわち "http://" から始まる絶対 URI 形式も受け入れなければなりません。)

abs_path は、必ず / で始まります。 ユーザエージェントがユーザにリクエスト URI を入力させた時に、もし空の値を入力したとしても、ユーザエージェントは / を補いましょう。

クエリ文字列の取り扱いによる危険性

Request-URI のクエリ文字列は、指定したリソースに情報を渡す場合に使いますが、この文字列は URI 中に表れるので、第三者が容易に閲覧可能なものになっています。 この文字列の不用意な取り扱いによる危険性について、section 15.1.3 に記述されています。

HTTP プロトコルを使うサービスの作者は、その Request-URI にエンコードされたデータが現れるので、機密性の高いデータの提出に GET を使ったフォームを使うべきではない。 多くの現存のサーバやプロクシ、ユーザエージェントは、第三者が見るかもしれない場所にその Request-URI を記録するだろう。 サーバは、代わりに POST を使ったフォームを使うべきである。

もしかしたら、例えば Web メール等、Web 中のあるサービスにログインしている場合に、クエリ中に ID やパスワードを含ませた URI があるかもしれません。 しかし、これはとても危険であるというはおわかりいただけると思います。 この場合、サービスの提供者は、POST メソッドによるフォームを用いた逐一の認証や、例えば状態維持が目的ならば HTTP Cookies 等を利用すべきです。

参照文献


Copyright © 1999-2005 H-Hash, All Rights Reserved.