この記事はkdnaktの1人 Advent Calendar 2020の10日目の記事です。
2020年は会社でKotlin dojoを主催して週1回30分Kotlinと戯れていました。12月はその集大成ということで、Kotlin/NativeでHTTPサーバーを作ってみたいと思います。どこまでできるか、お楽しみ……。
[RFC7230]
これまで、Kotlin/Nativeでリクエストを受け取ってそのまま返すだけのエコーサーバーを実装し、テストを書くための準備をしました。
HTTPリクエストをKotlinのオブジェクトにパースするには、どのようなテストを書けばよいのか理解するために、RFC7230を確認します。
HTTPでやりとりされるメッセージの基本的な形式は次のようになっています(Section 3. Message Format)。HTTP-messageはHTTPリクエストまたはHTTPレスポンスのいずれかを表します。
HTTP-message = start-line
*( header-field CRLF )
CRLF
[ message-body ]
start-lineで始まり、CRLF(改行コード)で区切られたヘッダーが0個以上続いて、CRLFだけの空行があり、メッセージボディへと続く構成になっています。
start-lineは、HTTPリクエストの場合にはrequest-line、HTTPレスポンスの場合にはstatus-lineとそれぞれ異なります。
[HTTPリクエストのrequest-line]
request-lineは次の形式で表現されます。SPはシングルスペース(single space)を表しています。
request-line = method SP request-target SP HTTP-version CRLF
最初は、シンプルなGETリクエストを受け取る場合を考えていきます。
▼method
methodはHTTPメソッドを表し、RFC7231のSection 4で定義されたHTTPメソッドは以下の8つです。
- GET
- HEAD
- POST
- PUT
- DELETE
- CONNECT
- OPTIONS
- TRACE
▼request-target
request-targetは取得対象のリソースを表し、以下の形式で表現されます。
request-target = origin-form
/ absolute-form
/ authority-form
/ asterisk-form
authority-formはCONNECTリクエストで、asterisk-formはOPTIONSリクエストでのみ利用されるので、今回はいったん無視して進みます。
absolut-formも、プロキシサーバーへのリクエストを行う場合に利用されるので、当分は利用しません。
GETリクエストを構成するorigin-formは、以下の形式で表現されます。
origin-form = absolute-path [ "?" query ]
absolute-pathは空文字にはならず、少なくとも/を含みます。
queryはRFC3986で詳細が定義されていますが、ここではいったん雑に?name1=value1&name2=value2のようにnameとvalueのペアと理解しておきます。
▼HTTP-Version
HTTP-Versionはとりあえず固定でHTTP/1.1としておきます。それ以前のバージョンに対応する予定はありませんし、HTTP/2に対応するのは当分先になりそうだからです。
これで少なくともstart-lineをパースすることはできそうです。
残るHTTPヘッダー部分は、field-name ":" OWS field-value OWSという形式で定義されています。OWSは半角スペースまたはタブ文字を任意の数含みます(ゼロ個の場合もあります)。
[まとめ]
- HTTPリクエストの仕様をRFC7230に沿って簡単に確認した
- 1行目は
method+request-target+HTTP-version - 2行目以降空行までリクエストヘッダが
field-name+:+field-value形式で任意の行数続く
明日以降はここで確認した仕様に従ってHTTPリクエストをパースするテスト、実装を進めていきます。