Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Upgrade to Ktor 1.6.1 and Kotlin 1.5 #143

Merged
merged 9 commits into from
Jul 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
buildscript {
ext.kotlinVersion = '1.5.20'
ext.kotlinCoroutinesVersion = '1.5.0'
ext.ktorVersion = '1.4.1'
ext.ktorVersion = '1.6.1'
ext.okhttpVersion = '4.9.1'
}

Expand Down Expand Up @@ -62,7 +62,6 @@ dependencies {
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0")
testImplementation("org.mockito:mockito-inline:3.11.2")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutinesVersion")
}

java {
Expand Down
39 changes: 21 additions & 18 deletions src/main/kotlin/tech/relaycorp/poweb/PoWebClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package tech.relaycorp.poweb
import io.ktor.client.HttpClient
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.engine.okhttp.OkHttp
import io.ktor.client.features.ClientRequestException
import io.ktor.client.features.RedirectResponseException
import io.ktor.client.features.ServerResponseException
import io.ktor.client.features.websocket.DefaultClientWebSocketSession
import io.ktor.client.features.websocket.WebSockets
import io.ktor.client.features.websocket.webSocket
Expand All @@ -19,7 +22,6 @@ import io.ktor.http.content.ByteArrayContent
import io.ktor.http.content.OutgoingContent
import io.ktor.http.content.TextContent
import io.ktor.http.contentType
import io.ktor.util.KtorExperimentalAPI
import io.ktor.util.toByteArray
import kotlinx.coroutines.channels.ClosedReceiveChannelException
import kotlinx.coroutines.flow.Flow
Expand Down Expand Up @@ -62,7 +64,6 @@ import java.time.Duration
*
* The underlying connection is created lazily.
*/
@OptIn(KtorExperimentalAPI::class)
public class PoWebClient internal constructor(
internal val hostName: String,
internal val port: Int,
Expand All @@ -81,7 +82,8 @@ public class PoWebClient internal constructor(
private val urlScheme = if (useTls) "https" else "http"
private val wsScheme = if (useTls) "wss" else "ws"

internal val baseURL: String = "$urlScheme://$hostName:$port/v1"
internal val baseHttpUrl: String = "$urlScheme://$hostName:$port/v1"
internal val baseWsUrl: String = "$wsScheme://$hostName:$port/v1"

/**
* Close the underlying connection to the server (if any).
Expand Down Expand Up @@ -250,29 +252,30 @@ public class PoWebClient internal constructor(
requestBody: OutgoingContent,
authorizationHeader: String? = null
): HttpResponse {
val url = "$baseURL$path"
val response: HttpResponse = try {
val url = "$baseHttpUrl$path"

return try {
ktorClient.post(url) {
if (authorizationHeader != null) {
header("Authorization", authorizationHeader)
}
body = requestBody
}
} catch (exc: UnknownHostException) {
throw ServerConnectionException("Failed to resolve DNS for $baseURL", exc)
throw ServerConnectionException("Failed to resolve DNS for $baseHttpUrl", exc)
} catch (exc: IOException) {
throw ServerConnectionException("Failed to connect to $url", exc)
}

if (response.status.value in 200..299) {
return response
}
throw when (response.status.value) {
in 400..499 -> PoWebClientException(response.status)
in 500..599 -> ServerConnectionException(
"The server was unable to fulfil the request (${response.status})"
} catch (exc: RedirectResponseException) {
// HTTP 3XX response
throw ServerBindingException("Unexpected redirect (${exc.response.status})")
} catch (exc: ClientRequestException) {
// HTTP 4XX response
throw PoWebClientException(exc.response.status)
} catch (exc: ServerResponseException) {
// HTTP 5XX response
throw ServerConnectionException(
"The server was unable to fulfil the request (${exc.response.status})"
)
else -> ServerBindingException("Received unexpected status (${response.status})")
}
}

Expand All @@ -293,7 +296,7 @@ public class PoWebClient internal constructor(
block: suspend DefaultClientWebSocketSession.() -> Unit
) = try {
ktorClient.webSocket(
"$wsScheme://$hostName:$port$path",
"$baseWsUrl$path",
{ headers?.forEach { header(it.first, it.second) } },
block
)
Expand All @@ -304,7 +307,7 @@ public class PoWebClient internal constructor(
}

public companion object {
internal const val PARCEL_COLLECTION_ENDPOINT_PATH = "/v1/parcel-collection"
internal const val PARCEL_COLLECTION_ENDPOINT_PATH = "/parcel-collection"

private const val DEFAULT_LOCAL_PORT = 276
private const val DEFAULT_REMOTE_PORT = 443
Expand Down
20 changes: 9 additions & 11 deletions src/test/kotlin/tech/relaycorp/poweb/ParcelCollectionTest.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package tech.relaycorp.poweb

import io.ktor.http.cio.websocket.CloseReason
import io.ktor.util.KtorExperimentalAPI
import kotlinx.coroutines.channels.ClosedReceiveChannelException
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.first
Expand All @@ -15,7 +14,6 @@ import org.junit.jupiter.api.assertThrows
import tech.relaycorp.poweb.websocket.ActionSequence
import tech.relaycorp.poweb.websocket.ChallengeAction
import tech.relaycorp.poweb.websocket.CloseConnectionAction
import tech.relaycorp.poweb.websocket.MockKtorClientManager
import tech.relaycorp.poweb.websocket.ParcelDeliveryAction
import tech.relaycorp.poweb.websocket.SendTextMessageAction
import tech.relaycorp.poweb.websocket.WebSocketTestCase
Expand All @@ -36,7 +34,6 @@ import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

@KtorExperimentalAPI
class ParcelCollectionTest : WebSocketTestCase() {
private val nonce = "nonce".toByteArray()

Expand All @@ -53,18 +50,19 @@ class ParcelCollectionTest : WebSocketTestCase() {
fun closeClient() = client.ktorClient.close()

@Test
fun `Request should be made to the parcel collection endpoint`() = runBlocking {
val mockClient = PoWebClient.initLocal()
val ktorClientManager = MockKtorClientManager()
mockClient.ktorClient = ktorClientManager.ktorClient
fun `Request should be made to the parcel collection endpoint`() {
val client = PoWebClient.initLocal(mockWebServer.port)
client.ktorClient = ktorWSClient
setListenerActions(CloseConnectionAction())

ktorClientManager.useClient {
mockClient.collectParcels(arrayOf(signer)).toList()
assertThrows<ServerConnectionException> {
runBlocking { client.collectParcels(arrayOf(signer)).toList() }
}

val request = mockWebServer.takeRequest()
assertEquals(
PoWebClient.PARCEL_COLLECTION_ENDPOINT_PATH,
ktorClientManager.request.url.encodedPath
"/v1${PoWebClient.PARCEL_COLLECTION_ENDPOINT_PATH}",
request.path
)
}

Expand Down
24 changes: 10 additions & 14 deletions src/test/kotlin/tech/relaycorp/poweb/ParcelDeliveryTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import io.ktor.client.request.HttpRequestData
import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
import io.ktor.http.content.OutgoingContent
import io.ktor.util.KtorExperimentalAPI
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.runBlocking
import org.bouncycastle.util.encoders.Base64
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
Expand All @@ -24,15 +22,13 @@ import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue

@ExperimentalCoroutinesApi
@KtorExperimentalAPI
class ParcelDeliveryTest {
private val parcelSerialized = "Let's say I'm the serialization of a parcel".toByteArray()
private val signer =
Signer(PDACertPath.PRIVATE_ENDPOINT, KeyPairSet.PRIVATE_ENDPOINT.private)

@Test
fun `Request should be made with HTTP POST`() = runBlockingTest {
fun `Request should be made with HTTP POST`() = runBlocking {
var method: HttpMethod? = null
val client = makeTestClient { request: HttpRequestData ->
method = request.method
Expand All @@ -45,7 +41,7 @@ class ParcelDeliveryTest {
}

@Test
fun `Endpoint should be the one for parcels`() = runBlockingTest {
fun `Endpoint should be the one for parcels`() = runBlocking {
var endpointURL: String? = null
val client = makeTestClient { request: HttpRequestData ->
endpointURL = request.url.toString()
Expand All @@ -54,11 +50,11 @@ class ParcelDeliveryTest {

client.use { client.deliverParcel(parcelSerialized, signer) }

assertEquals("${client.baseURL}/parcels", endpointURL)
assertEquals("${client.baseHttpUrl}/parcels", endpointURL)
}

@Test
fun `Request content type should be the appropriate value`() = runBlockingTest {
fun `Request content type should be the appropriate value`() = runBlocking {
var contentType: String? = null
val client = makeTestClient { request: HttpRequestData ->
contentType = request.body.contentType.toString()
Expand All @@ -71,7 +67,7 @@ class ParcelDeliveryTest {
}

@Test
fun `Request body should be the parcel serialized`() = runBlockingTest {
fun `Request body should be the parcel serialized`() = runBlocking {
var requestBody: ByteArray? = null
val client = makeTestClient { request: HttpRequestData ->
assertTrue(request.body is OutgoingContent.ByteArrayContent)
Expand All @@ -85,7 +81,7 @@ class ParcelDeliveryTest {
}

@Test
fun `Delivery signature should be in the request headers`() = runBlockingTest {
fun `Delivery signature should be in the request headers`(): Unit = runBlocking {
var authorizationHeader: String? = null
val client = makeTestClient { request: HttpRequestData ->
authorizationHeader = request.headers["Authorization"]
Expand All @@ -106,7 +102,7 @@ class ParcelDeliveryTest {
}

@Test
fun `HTTP 20X should be regarded a successful delivery`() = runBlockingTest {
fun `HTTP 20X should be regarded a successful delivery`() = runBlocking {
val client = makeTestClient { respond("", HttpStatusCode.Accepted) }

client.use { client.deliverParcel(parcelSerialized, signer) }
Expand All @@ -118,7 +114,7 @@ class ParcelDeliveryTest {

client.use {
val exception = assertThrows<RejectedParcelException> {
runBlockingTest { client.deliverParcel(parcelSerialized, signer) }
runBlocking { client.deliverParcel(parcelSerialized, signer) }
}

assertEquals("The server rejected the parcel", exception.message)
Expand All @@ -132,7 +128,7 @@ class ParcelDeliveryTest {

client.use {
val exception = assertThrows<ClientBindingException> {
runBlockingTest { client.deliverParcel(parcelSerialized, signer) }
runBlocking { client.deliverParcel(parcelSerialized, signer) }
}

assertEquals("The server returned a $status response", exception.message)
Expand Down
Loading