この記事はkdnaktの1人 Advent Calendar 2020の12日目の記事です。
2020年は会社でKotlin dojoを主催して週1回30分Kotlinと戯れていました。12月はその集大成ということで、Kotlin/NativeでHTTPサーバーを作ってみたいと思います。どこまでできるか、お楽しみ……。
[start-lineをパースする]
10日目の記事で確認した内容をもとに、HTTPリクエストのstart-line
をパースするためのテストコードを書いていきます。
▼事前準備
まずテストコードを書くための準備として、パースした結果を格納するRequestContext.kt
ファイルを以下の内容で作成します。
data class RequestContext( val method: HttpMethod, val requestTarget: String, val httpVersion: HttpVersion) enum class HttpMethod { GET, POST, } enum class HttpVersion() { HTTP_0_9, HTTP_1_0, HTTP_1_1; companion object { fun from(version: String) = when(version) { "" -> HTTP_0_9 "HTTP/1.0" -> HTTP_1_0 "HTTP/1.1" -> HTTP_1_1 else -> HTTP_1_1 } } }
HTTPバージョンは、Kotlin勉強会で学んだcompanion objectとwhen式を利用して実装しました。
テスト対象とするRequestParser.kt
ファイルを作成し、ダミーの実装を追加します。
class RequestParser { fun parse(byteArray: ByteArray): RequestContext { return RequestContext(HttpMethod.POST, "", HttpVersion.HTTP_1_0) } }
▼テストコード
テストコードを実装するRequestParserTest.ktファイルを作成し、シンプルなGETリクエストのバイト配列をパースして、戻り値のHTTPメソッドがGETかどうか判定するテストを実装します。
class RequestParserTest { private val parser: RequestParser = RequestParser() private val crlf = "\r\n" @Test fun shouldParseGetMethod() { val reqByteArray = ("GET /index.html HTTP/1.1" + crlf + "Host: localhost:8080" + crlf + crlf).encodeToByteArray() val context = parser.parse(reqByteArray) assertEquals(HttpMethod.GET, context.method, "should return GET method") } }
この状態でテストを実行すると、ダミーの実装ではPOSTメソッド、期待値はGETメソッドと差異があるため、テストは失敗します。
このテストが成功するように実装を修正します。
▼RequestParserを実装する
HTTPリクエストの1行目、つまり最初のCRLFまでが半角スペースで区切られたstart-lineなので、その部分を取り出し半角スペースで分割します。
val startLineElements = byteArray.toKString() .split("\r\n")[0] .split(" ")
分割した結果の要素が2以下の場合は、HTTP/0.9の仕様にしたがって、HTTPバージョンがないstart-line
となります。
val httpVersion = if (startLineElements.size <= 2) HttpVersion.HTTP_0_9 else HttpVersion.from(startLineElements[2])
以上のデータをもとに、RequestContext
オブジェクトを作成します。
return RequestContext( HttpMethod.valueOf(startLineElements[0]), startLineElements[1], httpVersion )
これでテストを実行すると、成功します。あとは、request-target
とhttp-version
に関するテストを追加して終了です。