Studying HTTP

Help

Backus-Naur Form

Backus-Naur Form について

まずは、以下をご覧下さい。

 Request       = Request-Line              ; Section 5.1
                 *(( general-header        ; Section 4.5
                  | request-header         ; Section 5.3
                  | entity-header ) CRLF)  ; Section 7.1
                 CRLF
                 [ message-body ]          ; Section 4.3

これは、リクエストについての決まり、つまり文法を示したものですが、このような表記の仕方を考案者 (J.W. Backus, Peter Naur) の名をとって Backus-Naur Form (BNF) と言います。 RFC 2616 では、プロトコルメカニズムを BNF を用いて説明しています。

この文書において詳述されるメカニズムのすべては、単調 Backus-Naur Form (BNF) と、RFC 822 で使用されているものに似た拡張 BNF との両方で記述されている。 実装者は、この仕様書を理解するためにこの表記法に精通している必要があるだろう。

RFC 2616 で使用される BNF の知識は section 2.1, 2.2 をご覧下さい。

BNF を読む

以下からは、実際に RFC 2616 で用いられている BNF を読んでみましょう。

例1: リクエスト

 Request       = Request-Line              ; Section 5.1
                 *(( general-header        ; Section 4.5
                  | request-header         ; Section 5.3
                  | entity-header ) CRLF)  ; Section 7.1
                 CRLF
                 [ message-body ]          ; Section 4.3

これは上で示した通り、リクエストについての BNF です。 これを読んでみましょう。 これによると、Request は、以下のようになる事がわかります。

  1. Request-Line が必ず1つ存在し
  2. (general-header, request-header, entity-header のいずれかの後に CRLF) という組みが0個以上存在し (※)
  3. CRLF が必ず1つ存在する
  4. message-body はオプショナルである

(※) 実際には、HTTP/1.1 リクエストには Host ヘッダが含まれなければならないので、この記述は不十分です。

それでは、Request-Linegeneral-header 等の各要素はどのように定義されているのでしょうか? 各行の後に ";" によってコメントアウトされている各節をご覧下さい。 例えば、section 5.1 を見てみましょう。

Request-Line   = Method SP Request-URI SP HTTP-Version CRLF

これによると、Request-Line は、Method, SP, Request-URI, SP, HTTP-Version, CRLF が順番に1つずつ存在するという事がわかります。

Method について見てみましょう。 Method は、section 5.1.1 に記述されています。

 Method         = "OPTIONS"                ; Section 9.2
                | "GET"                    ; Section 9.3
                | "HEAD"                   ; Section 9.4
                | "POST"                   ; Section 9.5
                | "PUT"                    ; Section 9.6
                | "DELETE"                 ; Section 9.7
                | "TRACE"                  ; Section 9.8
                | "CONNECT"                ; Section 9.9
                | extension-method
 extension-method = token

Method は、" " で囲まれている OPTIONGET というリテラル (文字列)か、token で書かれる extension-method であれば良いという事がわかります。

Request-URI については、section 5.1.2 に記述されています。

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

"*" 以外の 3 つは、RFC 2396 の付録 A. に記述されています。

HTTP-Version については、section 3.1 に記述されています。

 HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT

(※) 現在使用されている HTTP-Version は、HTTP/0.9, HTTP/1.0, HTTP/1.1 です。

最後に、CRLF, SP, token, DIGIT ですが、これらはよく使用される物なので、section 2.2 にてまとめて定義されています。

以下の規定は、基本的な構造概念を記述するためにこの仕様書全体に渡って使用される。 文字セットとしてコード化された US-ASCII は ANSI X3.4-1986 [21] にて定義されている。

 OCTET          = <8bit のデータシーケンス>
 CHAR           = <US-ASCII 文字 (0 - 127 オクテット)>
 UPALPHA        = <US-ASCII 大文字 "A".."Z">
 LOALPHA        = <US-ASCII 小文字 "a".."z">
 ALPHA          = UPALPHA | LOALPHA
 DIGIT          = <US-ASCII 数字 "0".."9">
 CTL            = <US-ASCII 制御文字
                  (0 - 31 オクテット) と DEL (127)>
 CR             = <US-ASCII CR, キャリッジリターン (13)>
 LF             = <US-ASCII LF, ラインフィード (10)>
 SP             = <US-ASCII SP, スペース (32)>
 HT             = <US-ASCII HT, 水平タブ (9)>
 <">            = <US-ASCII ダブルクォート記号 (34)>
 CRLF           = CR LF
 LWS            = [CRLF] 1*( SP | HT )
 TEXT           = <CTL を除き、LWS を含むすべての OCTET>
 HEX            = "A" | "B" | "C" | "D" | "E" | "F"
                | "a" | "b" | "c" | "d" | "e" | "f" | DIGIT
 token          = 1*<CTL や separators を除いたあらゆる CHAR>
 separators     = "(" | ")" | "<" | ">" | "@"
                | "," | ";" | ":" | "\" | <">
                | "/" | "[" | "]" | "?" | "="
                | "{" | "}" | SP | HT
 comment        = "(" *( ctext | quoted-pair | comment ) ")"
 ctext          = <"(" と ")" を含めたあらゆる TEXT>
 quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
 qdtext         = <<"> を除いたあらゆる TEXT>
 quoted-pair    = "\" CHAR

例2: 製品トークン

User-AgentServer 等のヘッダでは製品トークンが使われます。 製品トークンについては、section 3.8 にて記述されています。

製品トークンは、ソフトウェアの名前とバージョンによってその製品である事を識別するアプリケーションと通信する事ができるようにするために使われる。 製品トークンで使用されるほとんどのフィールドは、アプリケーションの重要な部分を形成する部分製品{sub-product} を空白で区切るように列挙できるようになっている。 慣習では、製品はそのアプリケーションを識別するために重要なものの順に列挙される。

 product         = token ["/" product-version]
 product-version = token

例を見よ。

 User-Agent: CERN-LineMode/2.15 libwww/2.17b3
 Server: Apache/0.8.4

製品トークンは短く要点のみであるべきである。 宣伝や別の本質的でない情報のために使用してはならない。 製品バージョンにはあらゆるトークン文字を使用できるが、このトークンはバージョン識別子に対してのみ使われるべきである。 (例えば、同じ製品の連続したバージョンは、製品値の製品バージョン部分のみが異なるべきである)。

また、User-AgentServer の BNF は、section 14.38, 14.43 にそれぞれ記述されています。

 Server         = "Server" ":" 1*( product | comment )
 User-Agent     = "User-Agent" ":" 1*( product | comment )

上記の通り、ServerUser-Agent に使用できる値は productcomment です。 comment は、section 2.2 にあるので、product を見てみましょう。

 product         = token ["/" product-version]
 product-version = token

このように、product として使用できるのは token、 及び / の後に続けられる token のみであるという事がわかります。 従って、実は以下のような User-Agent ヘッダは、文法違反である事がわかります。

 User-Agent: Mozilla/4.7 [ja]C-{C-UDP; EBM-SONY2}  ← "[", "]", "{", "}" を使っている
 User-Agent: DoCoMo/1.0/N503i/c10                  ← "/" を二回以上使っている
 User-Agent: null@StudyingHTTP.NET                 ← "@" を使っている
 User-Agent: HttpGet/1.0(Win32; GUI; ix86)         ← product と comment を SP で区切っていない
 User-Agent: ユーザエージェント                    ← "日本語" を使用している (※)

(※) この場合の "日本語" とは、ISO-2022-JP 等の文字セットを用いて記述されたものを指します。

以上のような User-Agent ヘッダは、通信相手に正しく伝わらない可能性があります。 User-Agent ヘッダは、サーバがユーザエージェントを判別するためだけのヘッダですが、現実にはその他にも様々な目的に使われているヘッダですので、HTTP クライアントの作成者は特に注意して値を決定しなければいけません。 特に User-Agent ヘッダに "日本語" を用いたい場合は、例えば Base64 エンコードパーセントエンコーディング等の適切なエンコードを施すべきです。

このように順に辿って行けば、RFC 2616 中にある BNF は全て読む事ができます。 また BNF は、RFC 2616 以外にも、例えば XML の仕様書等、広く使用されている記述方法なので、一度覚えておけばその知識は広く活用できるでしょう。

参照文献

[an error occurred while processing this directive]