Help us understand the problem. What is going on with this article?

KtorでHTTPでのリクエストを弾く

More than 1 year has passed since last update.

その名の通りに、KtorでHTTPでのリクエストを弾くFeatureをシンプルに実装します。

REST API とかだとHTTPへのリクエストをHTTPSにリダイレクトさせるよりはエラーを吐かせたほうが良い気がしました。

Featureとは

Ktorアプリケーションを構成する一連の機能の事で基本的にはリクエストとレスポンスのパイプラインに組み込まれるものです。
Webアプリを作る上で欠かせない主要な機能であるルーティングもそのFeatureの一つとして定義されています。

Featureの詳細(公式リファレンス)

Featureを定義するためには、そのFeatureを表すクラスと、それを組み立てるために必要な情報を詰めるクラス、ApplicationFeatureを継承したcompanion objectが必要です。(別にcompanion objectじゃなくても動くけど公式のFeature達に倣う)

今回はHTTPでのリクエストを弾いてしまうFeatureを作るのでHttpsOnlyって名前にしたいと思います。

HttpsOnlyのざっくりした機能

とにかくシンプルにHttpでのリクエストを拒否したいだけなので要件は2つだけです。

  • Httpでのリクエストを受け取った際にHttpStatusCode:403を返す。
  • Httpでのリクエストを受け取った際に、任意の型のレスポンスボディを返す。

コード

解説はコード内コメントで行います。

//Httpでのリクエストを拒否するためのFeature
class HttpsOnly(configuration: Configuration) {
    //Httpでのリクエストが有った際に返すレスポンスのボディ
    val response = configuration.responseObject

    //HttpsOnlyを組み立てるための設定
    class Configuration {
        //Httpでのリクエストが有った際に返すレスポンスのボディ
        var responseObject: Any = "https only"
    }

    companion object Feature : ApplicationFeature<ApplicationCallPipeline, Configuration, HttpsOnly> {
        //Featureを一意に表すキー
        override val key: AttributeKey<HttpsOnly> = AttributeKey("HttpsOnly")

        //Featureのインストールを行う関数
        override fun install(pipeline: ApplicationCallPipeline, configure: Configuration.() -> Unit): HttpsOnly {
            //configureを元にHttpsOnlyを組み立て
            val feature = HttpsOnly(Configuration().apply(configure))


            /*
             ApplicationCallPipelineに、リクエストがHttpではないかをチェックして
             そうだったときにHttpStatusCode 403とHttpsOnly#responseを返す処理を挟む
            */
            pipeline.intercept(ApplicationCallPipeline.Features) {
                if (call.request.origin.scheme != "http") return@intercept
                call.respond(HttpStatusCode.Forbidden, feature.response)
                finish()
            }
            return feature
        }
    }
}

実際に使う際には

fun Application.installHttpsOnly() = install(HttpsOnly) {
    responseObject = ErrorResponse(
        statusCode = HttpStatusCode.Forbidden.value,
        reason = "sslRequired",
        message = "SSL is required for requests to this server"
    )
}

のようにしてやればHttpでのリクエストを受け取った際にエラーレスポンスを任意のオブジェクトをレスポンスボディにして返してやることが出来ます。
ktor-jackson等と組み合わせればJSONを返すことも可能です。

間違ったことや、おかしな点があればご指摘して頂けれると嬉しいです。

参考

https://ktor.io/servers/features.html
https://ktor.io/advanced/pipeline.html
https://ktor.io/servers/lifecycle.html
https://github.com/ktorio/ktor/blob/43eef9ed31ba33137501820a1b292af946ede6f8/ktor-server/ktor-server-core/jvm/src/io/ktor/features/DefaultHeaders.kt

naito-y
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away