この記事はkdnaktの1人 Advent Calendar 2020の14日目の記事です。
2020年は会社でKotlin dojoを主催して週1回30分Kotlinと戯れていました。12月はその集大成ということで、Kotlin/NativeでHTTPサーバーを作ってみたいと思います。どこまでできるか、お楽しみ……。
[モックレスポンスを返す]
リクエストとして送られてきた文字列をそのままレスポンスとして送り返すエコーサーバーをベースにHTTPサーバーを実装してきました。
最終的にはリクエストされたファイルを公開用ディレクトリから探してレスポンスを返す、という形を目指しますが、今回はソース上にハードコーディングしたHTMLをレスポンスとして返す機能を追加します。
▼send()関数を理解する
リクエストを受け取ってエコーレスポンスを返している部分は以下のようなコードになっています。recv()
でリクエストを読み込み、send()
でレスポンスを返しています。
while (true) { val length = recv(commFd, pinned.addressOf(0), buffer.size.convert(), 0).toInt() .ensureUnixCallResult("read") { it >= 0 } if (length == 0) { break } send(commFd, pinned.addressOf(0), length.convert(), 0) .ensureUnixCallResult("write") { it >= 0 } }
send()
関数の説明をIntelliJで確認すると、以下のような内容を確認できる。@CCall
というアノテーションがついていることから、C言語のsend()
を直接呼び出しているようです。
send()
システムコールの説明を見ると引数が4つあり、順番にint型のファイルディスクリプター、void型ポインターのメッセージ、size_t型のメッセージの長さ、int型のフラグとなっており、IntelliJの説明と型が一致しています。
エコーレスポンスではなく、ハードコーディングされたHTMLをレスポンスとして返すためには、send()
関数の2番目と3番目の引数を修正すれば良さそうです。
文字列をC言語の世界に渡すときは、String.cstr
という拡張プロパティを利用します。
Mapping Strings from C - Kotlin Programming Language
h1タグで囲まれたHello Worldという文字列だけのHTMLを返すレスポンスを返す場合、以下のような実装になります。IntelliJで表示したヘルプの結果から、size_t型はULong型を利用しています。
val contents = "HTTP/1.1 200 OK\r\nContent-Length: 33\r\n\r\n<html><h1>Hello World</h1></html>\r\n".cstr send(commFd, contents, contents.size.toULong(), 0) .ensureUnixCallResult("write") { it >= 0}
▼curlで動作確認
ビルドして動作を確認します。
curlでアクセスすると、以下のように表示されます。
$ curl -i localhost:8080 HTTP/1.1 200 OK Content-Length: 33 <html><h1>Hello World</h1></html>
▼ブラウザの開発者ツールで動作確認
次に、Chromeでアクセスしてみると、次のように表示されます。
開発者ツールでレスポンスを確認すると、レスポンスヘッダーが正しく解釈されています。