Skip to content

Commit

Permalink
fix: remove content-type hack (#324)
Browse files Browse the repository at this point in the history
  • Loading branch information
typfel authored Mar 25, 2022
1 parent 8d95b8f commit e72c8eb
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,12 @@ actual fun defaultHttpEngine(): HttpClientEngine {
return OkHttp.create {
addInterceptor(object : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
return chain.call().request().handleProtoHeader()
.let { checkedRequest -> chain.proceed(checkedRequest) }.handleUnauthorizedResponse()
return chain.request().let { checkedRequest -> chain.proceed(checkedRequest) }.handleUnauthorizedResponse()
}
})
}
}

/**
* This is a hack
*
* As seen here: https://github.com/ktorio/ktor/issues/1127
* Ktor is pretty has `application/protobuf` but not `application/x-protobuf`
* and it doesn't support custom content types!
*/
private fun Request.handleProtoHeader(): Request =
when (headers["Content-Type"] == "application/octet-stream") {
true -> this.newBuilder().header("Content-Type", "application/x-protobuf").build()
false -> this
}

/**
* Ktor need "WWW-Authenticate" to be set by BE in-order for the tokens refresh to work
* see issue https://youtrack.jetbrains.com/issue/KTOR-2806
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ import com.wire.kalium.network.api.user.logout.LogoutApi
import com.wire.kalium.network.api.user.logout.LogoutImpl
import com.wire.kalium.network.api.user.self.SelfApi
import com.wire.kalium.network.api.user.self.SelfApiImpl
import com.wire.kalium.network.serialization.mls
import com.wire.kalium.network.serialization.xprotobuf
import com.wire.kalium.network.session.SessionManager
import com.wire.kalium.network.session.installAuth
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.plugins.ContentNegotiation

class AuthenticatedNetworkContainer(
private val sessionManager: SessionManager,
Expand Down Expand Up @@ -67,6 +70,10 @@ class AuthenticatedNetworkContainer(
internal val authenticatedHttpClient by lazy {
provideBaseHttpClient(engine, HttpClientOptions.DefaultHost(backendConfig)) {
installAuth(sessionManager)
install(ContentNegotiation) {
mls()
xprotobuf()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.wire.kalium.network.api.ErrorResponse
import com.wire.kalium.network.exceptions.KaliumException
import com.wire.kalium.network.exceptions.QualifiedSendMessageError
import com.wire.kalium.network.exceptions.SendMessageError
import com.wire.kalium.network.serialization.XProtoBuf
import com.wire.kalium.network.utils.NetworkResponse
import com.wire.kalium.network.utils.wrapKaliumResponse
import io.ktor.client.HttpClient
Expand Down Expand Up @@ -111,9 +112,7 @@ class MessageApiImp(
return try {
val response = httpClient.post("$PATH_CONVERSATIONS/${conversationId.domain}/${conversationId.value}$PATH_PROTEUS_MESSAGE") {
setBody(envelopeProtoMapper.encodeToProtobuf(parameters))
// This technically doesn't work, Ktor will replace with application/octet-stream anyway
// But if this ever gets improved, we're already on the right track
contentType(ContentType.Application.ProtoBuf)
contentType(ContentType.Application.XProtoBuf)
}
NetworkResponse.Success(httpResponse = response, value = response.body<QualifiedSendMessageResponse.MessageSent>())
} catch (e: ResponseException) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.wire.kalium.network.serialization

import io.ktor.http.ContentType
import io.ktor.http.content.ByteArrayContent
import io.ktor.http.content.OutgoingContent
import io.ktor.serialization.Configuration
import io.ktor.serialization.ContentConverter
import io.ktor.util.reflect.TypeInfo
import io.ktor.util.toByteArray
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.charsets.Charset

/**
* A ContentConverter which does nothing, it simply passes byte arrays through as they are. This is useful
* if you want to register your own custom binary content type with the ContentNegotiation plugin.
*/
class ByteArrayConverter: ContentConverter {

override suspend fun deserialize(charset: Charset, typeInfo: TypeInfo, content: ByteReadChannel): Any? {
return content.toByteArray()
}

override suspend fun serialize(contentType: ContentType, charset: Charset, typeInfo: TypeInfo, value: Any): OutgoingContent? {
return ByteArrayContent(value as ByteArray, contentType)
}

}

public val ContentType.Message.Mls: ContentType
get() = ContentType("message", "mls")

public val ContentType.Application.XProtoBuf: ContentType
get() = ContentType("application", "x-protobuf")

public fun Configuration.mls(contentType: ContentType = ContentType.Message.Mls) {
register(contentType, ByteArrayConverter())
}

public fun Configuration.xprotobuf(contentType: ContentType = ContentType.Application.XProtoBuf) {
register(contentType, ByteArrayConverter())
}

0 comments on commit e72c8eb

Please sign in to comment.