HTTP
Nginx
自作サーバー
前回に引き続きHTTP/1.1の仕様とNginxの実装を読んでいく。 今回はHeaderのパース周りについて。RFC9110と9112に関連する記載がある。
RFC9110ではHeader, Trailerで使われるName-Value型の形式をFieldと呼んでいる。 5章がまるまるFieldの説明になっていて、Field Name, Field Valueで利用可能な文字や細則について記載がある。 一方RFC9112の5章にもHTTP/1.1におけるFieldの記法について記載がある。 RFC9112→RFC9110という順で読むと概観→細則という流れで読めるので読みやすいかも知れない。 あまり英語が得意でなく誤訳があるかもしれないので、この記事をなにかの参考にする場合には原典を合わせて確認してほしい。
field-line = field-name ":" OWS field-value OWS
冒頭にfield-lineの定義がある。field-nameとfield-valueを:
で繋げたものをfield-lineと読んでいて、ブラウザの開発者ツールなどで見慣れた形式をしている。
OWSはOptional White Spaceの略でRFC9110の5,6,3で定義されていて0個以上の空白(スペースか水平タブ)を指す。
field-lineにおけるOWSはfield-valueではないので、冒頭と末尾の空白は除去してあげる必要がある。
逆にfield-nameとコロンの間には空白が許可されていない。もしそのようなリクエストが送られてきた場合にはサーバーは400で拒否する必要がある。
過去field-nameの前に1つ以上のスペースを挟むことで、複数行のヘッダを記載することが出来たようだがHTTP/1.1では基本的に非推奨になっている。 クライアントがそのようなヘッダを生成することは禁止されているし、サーバー側も特定の場合を覗いて400で拒否する必要がある。
field-nameやfield-valueの詳細についてはこちらに記載がある。
field-name = token
これだけではどの文字の利用が許可されているかわからないが、細かいことはAppendix Aに大体書いてある。
tokenは1*tchar
と定義されていて、tcharは以下の通り。
tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
"^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
次にfield-valueだが以下のように定義されている。直感的には空白混じりの文字列という理解。 VCHARは視認可能なUS-ASCII文字を指す。 過去US-ASCII以外の文字を仕様としてサポートしていた名残でobs-textという文字も許可されている。 サーバーはobs-textを不透明なデータとして処理するべきという記載があるが、具体的にどう扱うべきかよくわからない。
field-value = *field-content
field-content = field-vchar
[ 1*( SP / HTAB / field-vchar ) field-vchar ]
field-vchar = VCHAR / obs-text
obs-text = %x80-FF
あとはfield-valueに関する細かい細則などが記載されている。
\
はエスケープ文字として扱う。ngx_http_parse_header_line
という関数がfield-lineのパースを担っている。
あまり処理内容は前回と差がない。状態を表す変数を定義して、1文字ずつ読み込んで状態を遷移させていく。
まだRustでNginxライクなHeaderパーサーを作成してみたが、NginxはEAGAINを考慮してリエントラントに書かれていて偉い。