Ktor HttpClientでGET

[入門Kotlin coroutines](https://qiita.com/k-kagurazaka@github/items/8595ca60a5c8d31bbe37)で coroutine を使った並行処理を勉強させていただきました。その後 Ktor に HTTPクライアントがあることを知り、これを使うと HTTP通信を非同期I/O化できるのではないかと思いました。そこで Ktor の [Client](http://ktor.io/clients/index.html) のページを開くと「under construction」でしたので、ソースを見ながら動かしてみました。
***
HttpClient クラスのコンストラクターには HttpClientEngineFactory インターフェースの実装クラスを渡す必要があります。
HttpClientEngineFactory インターフェースの実装クラスは次の3つが用意されていました。

- io.ktor.client.engine.cio.CIO
- io.ktor.client.engine.apache.Apache
- io.ktor.client.engine.jetty.Jetty

CIO は HTTP 接続できましたが、HTTPS で接続する場合は Apache を使う必要がありました。
(CIO は HTTP だけ対応、Apache は HTTP と HTTPS に対応しているようです。)
Jetty は HTTP2 接続できるようですが、まだ動かせていません。。

## CIO で GET
build.gradle

```gradle
group 'HttpClientCIO'
version '1.0-SNAPSHOT'

buildscript {
    ext {
        kotlin_version = '1.2.21'
        ktor_version = '0.9.1'
    }

    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

apply plugin: 'kotlin'
apply plugin: 'application'

sourceCompatibility = 1.8

mainClassName = 'HttpClientCIOKt'

repositories {
    mavenCentral()
    maven { url  "http://dl.bintray.com/kotlin/ktor" }
    maven { url "https://dl.bintray.com/kotlin/kotlinx" }
}

dependencies {
    // Kotlin
    compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"

    // Ktor
    compile "io.ktor:ktor-client-cio:$ktor_version"

    // Log
    compile "ch.qos.logback:logback-classic:1.2.1"

    // Testing
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

compileKotlin {
    kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

kotlin {
    experimental {
        coroutines "enable"
    }
}
```

HttpClientCIO.kt

```kotlin
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.request.*
import kotlinx.coroutines.experimental.*

fun main( args: Array<String> ) {
    val client = HttpClient( CIO )

    val response = runBlocking {
        client.get<String>( host = "www.example.com" )
    }

    println( response )

    client.close()
}
```

```client.get<String>( host = "www.example.com" )```
このように get メソッドに &lt;String&gt; を指定しているので、HTTPサーバーから受信した文字列が返ります。
受信した HTTP ヘッダーなどを参照したい場合は、下で説明しているように別の型を指定すると取得することができます。

## Apache で GET
build.gradle

```gradle
group 'HttpClientApache'
version '1.0-SNAPSHOT'

buildscript {
    ext {
        kotlin_version = '1.2.21'
        ktor_version = '0.9.1'
    }

    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

apply plugin: 'kotlin'
apply plugin: 'application'

sourceCompatibility = 1.8

mainClassName = 'HttpClientApacheKt'

repositories {
    mavenCentral()
    maven { url  "http://dl.bintray.com/kotlin/ktor" }
    maven { url "https://dl.bintray.com/kotlin/kotlinx" }
}

dependencies {
    // Kotlin
    compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"

    // Ktor
    compile "io.ktor:ktor-client-apache:$ktor_version"

    // Log
    compile "ch.qos.logback:logback-classic:1.2.1"

    // Testing
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

compileKotlin {
    kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

kotlin {
    experimental {
        coroutines "enable"
    }
}
```
dependencies の ktor-client-cio が ktor-client-apache になっている以外はほぼ同じです。

```kotlin
import io.ktor.client.HttpClient
import io.ktor.client.engine.apache.Apache
import io.ktor.client.request.*
import kotlinx.coroutines.experimental.*

fun main( args: Array<String> ) {
    val client = HttpClient( Apache )

    val response = runBlocking {
        client.get<String>( scheme = "https", host = "www.example.com", port = 443 )
    }

    println( response )

    client.close()
}
```
こちらも HttpClient のコンストラクタに渡すクラスが CIO から Apache になっている以外はほぼ同じですが、scheme に https を指定しています。
CIO は HTTPS に対応していないようで、CIO を使っている時に https を指定すると実行時エラーが発生します。

## 接続パラメーター
HttpClient.get() に指定可能なパラメータは、[io/ktor/client/request/builders.kt](https://github.com/ktorio/ktor/blob/master/ktor-client/ktor-client-core/src/io/ktor/client/request/builders.kt) で確認することができます。
具体的には次のようになっています。

|名前    | 型      | デフォルト値  |
|:-------|:-------|:------------|
| scheme | String | "http"      |
| host   | String | "localhost" |
| port   | Int    | 80          |
| path   | String | "/"         |
| body   | Any    | EmptyContent |
| block  | HttpRequestBuilder.() -> Unit | {} |

port のデフォルトが 80 なので、scheme に https を指定した場合、port には 443 を指定する必要があります。(scheme に https を指定しても自動的に port 443 にはなりません。)

EmptyContent は OutgoingContent.NoContent と定義されていて、body が NoContent の場合 HTTPボディは送信されないようになっていました。
body は POST でデータを渡す時に使うと思うので [POST](https://qiita.com/keijidosha/items/173cd002f3eed7e29374) で使ってみようと思います。
HTTPヘッダーを指定する場合は block を使います。

## HTTPスターテス、HTTPヘッダーを参照

```kotlin
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.request.*
import io.ktor.client.response.*
import kotlinx.coroutines.experimental.*


fun main( args: Array<String> ) {
    val client = HttpClient( CIO )

    val response = runBlocking {
        val res = client.get<HttpResponse>( host = "www.example.com" )
        println( res.version )    // "HTTP/1.1"(HTTP バージョン)
        println( res.status )     // "301 Moved Permanently"(HTTP ステータス)
        // HTTPヘッダー
        res.headers.forEach{ k, v ->
            println( "$k: $v" )
        }
        println()       // 改行
        res.readText()  // HTTPボディ
    }

    println( response )

    client.close()
}
```

これまでは get メソッドに &lt;String&gt; を指定してしていたのに対して、&lt;HttpResponse&gt; を指定すると、HTTP ステータスや HTTP ヘッダーを参照することができます。
HttpResponse のプロパティには次のようなものがあります。

| 名前     | 型                  | 実体(確認中) |
|:--------|:--------------------|:-------------|
| status  | HttpStatusCode      | data class HttpStatusCode(val value: Int, val description: String) |
| version | HttpProtocolVersion | data class HttpProtocolVersion(val name: String, val major: Int, val minor: Int) |
| requestTime | Date | java.util.Date |
| responseTime | Date | java.util.Date |
| headers | HeadersBuilder | Map&lt;String, List&lt;String&gt;&gt; |

***

次は [POST](https://qiita.com/keijidosha/items/173cd002f3eed7e29374) を使ってみたいと思います。