Kotlin/NativeでHTTPサーバーを作るアドベントカレンダー(10日目:HTTPリクエストの仕様)

この記事はkdnaktの1人 Advent Calendar 2020の10日目の記事です。

 

2020年は会社でKotlin dojoを主催して週1回30分Kotlinと戯れていました。12月はその集大成ということで、Kotlin/NativeでHTTPサーバーを作ってみたいと思います。どこまでできるか、お楽しみ……。

 

 

[RFC7230]

これまで、Kotlin/Nativeでリクエストを受け取ってそのまま返すだけのエコーサーバーを実装し、テストを書くための準備をしました。

 

HTTPリクエストをKotlinのオブジェクトにパースするには、どのようなテストを書けばよいのか理解するために、RFC7230を確認します。

tools.ietf.org

 

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リクエストをパースするテスト、実装を進めていきます。