Skip to content

Commit

Permalink
mime/auth type improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
olme04 committed Mar 10, 2022
1 parent 4a7bedb commit b7c7db9
Show file tree
Hide file tree
Showing 30 changed files with 365 additions and 419 deletions.
129 changes: 129 additions & 0 deletions rsocket-core/src/commonMain/kotlin/io/rsocket/kotlin/CompactType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package io.rsocket.kotlin

import io.ktor.utils.io.core.*
import io.rsocket.kotlin.frame.io.*
import kotlin.experimental.*

//used for MimeType, AuthType and later in LeaseStrategy
public interface CompactType {
public interface WithId : CompactType {
public val identifier: Byte
}

public interface WithName : CompactType {
public val text: String
}

public interface WellKnown : CompactType, WithId, WithName
}

@Suppress("UNCHECKED_CAST")
//TODO: type relations
public abstract class CompactTypeFactory<
Type : CompactType,
WithId : CompactType.WithId,
WithName : CompactType.WithName,
WellKnown,
> internal constructor(
private val withIdConstructor: (identifier: Byte) -> WithId,
private val withNameConstructor: (text: String) -> WithName,
wellKnownValues: Array<WellKnown>,
) where WellKnown : CompactType.WellKnown, WellKnown : Enum<WellKnown> {
private val wellKnownByIdentifier: Array<Any?> = arrayOfNulls<Any?>(128)
private val wellKnownByName: MutableMap<String, WellKnown> = HashMap(128)

init {
wellKnownValues.forEach {
wellKnownByIdentifier[it.identifier.toInt()] = it
wellKnownByName[it.text] = it
}
}

//TODO is it needed?
public fun WellKnown(text: String): WellKnown? = wellKnownByName[text]
public fun WellKnown(identifier: Byte): WellKnown? = wellKnownByIdentifier[identifier.toInt()] as WellKnown?
public fun WellKnown(identifier: Int): WellKnown? = wellKnownByIdentifier[identifier] as WellKnown?

public fun WithName(text: String): WithName = (WellKnown(text) ?: withNameConstructor(text)) as WithName
public fun WithId(identifier: Byte): WithId = (WellKnown(identifier) ?: withIdConstructor(identifier)) as WithId
public fun WithId(identifier: Int): WithId = WithId(identifier.toByte())

public operator fun invoke(text: String): WithName = WithName(text)
public operator fun invoke(identifier: Byte): WithId = WithId(identifier)
public operator fun invoke(identifier: Int): WithId = WithId(identifier)
}

internal fun CompactType.WellKnown.toString(typeName: String): String = "$typeName(id=$identifier, text=$text)"
internal fun CompactType.WithId.toString(typeName: String): String = "$typeName(id=$identifier)"
internal fun CompactType.WithName.toString(typeName: String): String = "$typeName(text=$text)"

internal abstract class AbstractCompactTypeWithId(final override val identifier: Byte) : CompactType.WithId {
init {
require(identifier > 0) { "Mime-type identifier must be positive but was '${identifier}'" }
}

final override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false

other as AbstractCompactTypeWithId

if (identifier != other.identifier) return false

return true
}

final override fun hashCode(): Int {
return identifier.hashCode()
}

abstract override fun toString(): String
}

internal abstract class AbstractCompactTypeWithName(final override val text: String) : CompactType.WithName {
init {
require(text.all { it.code <= 0x7f }) { "String should be an ASCII encodded string" }
require(text.length in 1..128) { "Mime-type text length must be in range 1..128 but was '${text.length}'" }
}

final override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false

other as AbstractCompactTypeWithName

if (text != other.text) return false

return true
}

final override fun hashCode(): Int {
return text.hashCode()
}

abstract override fun toString(): String
}

private const val KnownTypeFlag: Byte = Byte.MIN_VALUE

internal fun BytePacketBuilder.writeCompactType(type: CompactType) {
when (type) {
is CompactType.WithId -> writeByte(type.identifier or KnownTypeFlag)
is CompactType.WithName -> {
val typeBytes = type.text.encodeToByteArray()
writeByte(typeBytes.size.toByte()) //write length
writeFully(typeBytes) //write type
}
}
}

internal fun <T : CompactType> ByteReadPacket.readCompactType(factory: CompactTypeFactory<T, *, *, *>): T {
val byte = readByte()
return if (byte check KnownTypeFlag) {
val identifier = byte xor KnownTypeFlag
factory(identifier)
} else {
val stringType = readTextExactBytes(byte.toInt())
factory(stringType)
} as T
}
89 changes: 89 additions & 0 deletions rsocket-core/src/commonMain/kotlin/io/rsocket/kotlin/MimeType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright 2015-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.rsocket.kotlin

public sealed interface MimeType : CompactType {
public companion object : CompactTypeFactory<MimeType, WithId, WithName, WellKnown>(::WithIdImpl, ::WithNameImpl, enumValues())

public sealed interface WithId : MimeType, CompactType.WithId
public sealed interface WithName : MimeType, CompactType.WithName
public enum class WellKnown(
public override val text: String,
public override val identifier: Byte,
) : MimeType, CompactType.WellKnown, WithId, WithName {
ApplicationAvro("application/avro", 0x00),
ApplicationCbor("application/cbor", 0x01),
ApplicationGraphql("application/graphql", 0x02),
ApplicationGzip("application/gzip", 0x03),
ApplicationJavascript("application/javascript", 0x04),
ApplicationJson("application/json", 0x05),
ApplicationOctetStream("application/octet-stream", 0x06),
ApplicationPdf("application/pdf", 0x07),
ApplicationThrift("application/vnd.apache.thrift.binary", 0x08),
ApplicationProtoBuf("application/vnd.google.protobuf", 0x09),
ApplicationXml("application/xml", 0x0A),
ApplicationZip("application/zip", 0x0B),
AudioAac("audio/aac", 0x0C),
AudioMp3("audio/mp3", 0x0D),
AudioMp4("audio/mp4", 0x0E),
AudioMpeg3("audio/mpeg3", 0x0F),
AudioMpeg("audio/mpeg", 0x10),
AudioOgg("audio/ogg", 0x11),
AudioOpus("audio/opus", 0x12),
AudioVorbis("audio/vorbis", 0x13),
ImageBmp("image/bmp", 0x14),
ImageGif("image/gif", 0x15),
ImageHeicSequence("image/heic-sequence", 0x16),
ImageHeic("image/heic", 0x17),
ImageHeifSequence("image/heif-sequence", 0x18),
ImageHeif("image/heif", 0x19),
ImageJpeg("image/jpeg", 0x1A),
ImagePng("image/png", 0x1B),
ImageTiff("image/tiff", 0x1C),
MultipartMixed("multipart/mixed", 0x1D),
TextCss("text/css", 0x1E),
TextCsv("text/csv", 0x1F),
TextHtml("text/html", 0x20),
TextPlain("text/plain", 0x21),
TextXml("text/xml", 0x22),
VideoH264("video/H264", 0x23),
VideoH265("video/H265", 0x24),
VideoVp8("video/VP8", 0x25),
ApplicationHessian("application/x-hessian", 0x26),
ApplicationJavaObject("application/x-java-object", 0x27),
ApplicationCloudeventsJson("application/cloudevents+json", 0x28),
ApplicationCapnProto("application/x-capnp", 0x29),
ApplicationFlatBuffers("application/x-flatbuffers", 0x2A),

MessageRSocketMimeType("message/x.rsocket.mime-type.v0", 0x7A),
MessageRSocketAcceptMimeTypes("message/x.rsocket.accept-mime-types.v0", 0x7b),
MessageRSocketAuthentication("message/x.rsocket.authentication.v0", 0x7C),
MessageRSocketTracingZipkin("message/x.rsocket.tracing-zipkin.v0", 0x7D),
MessageRSocketRouting("message/x.rsocket.routing.v0", 0x7E),
MessageRSocketCompositeMetadata("message/x.rsocket.composite-metadata.v0", 0x7F);

override fun toString(): String = toString("MimeType")
}

private class WithIdImpl(identifier: Byte) : WithId, AbstractCompactTypeWithId(identifier) {
override fun toString(): String = toString("MimeType")
}

private class WithNameImpl(text: String) : WithName, AbstractCompactTypeWithName(text) {
override fun toString(): String = toString("MimeType")
}
}
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
package io.rsocket.kotlin.configuration

import io.rsocket.kotlin.core.*
import io.rsocket.kotlin.*

public sealed interface MimeTypeConfiguration {
public val metadata: MimeTypeWithName
public val data: MimeTypeWithName
public val metadata: MimeType.WithName
public val data: MimeType.WithName
}

public sealed interface MimeTypeConnectConfiguration : MimeTypeConfiguration, ConnectConfiguration

public sealed interface MimeTypeClientConnectConfiguration : MimeTypeConnectConfiguration {
public fun metadata(mimeType: MimeTypeWithName)
public fun data(mimeType: MimeTypeWithName)
public fun metadata(mimeType: MimeType.WithName)
public fun data(mimeType: MimeType.WithName)
}

public sealed interface MimeTypeServerConnectConfiguration : MimeTypeConnectConfiguration

internal class MimeTypeClientConnectConfigurationImpl(
private val configurationState: ConfigurationState,
) : MimeTypeClientConnectConfiguration {
override var metadata: MimeTypeWithName = WellKnownMimeType.ApplicationOctetStream
override var metadata: MimeType.WithName = MimeType.WellKnown.ApplicationOctetStream
private set
override var data: MimeTypeWithName = WellKnownMimeType.ApplicationOctetStream
override var data: MimeType.WithName = MimeType.WellKnown.ApplicationOctetStream
private set

override fun metadata(mimeType: MimeTypeWithName) {
override fun metadata(mimeType: MimeType.WithName) {
configurationState.checkNotConfigured()
metadata = mimeType
}

override fun data(mimeType: MimeTypeWithName) {
override fun data(mimeType: MimeType.WithName) {
configurationState.checkNotConfigured()
data = mimeType
}
}

internal class MimeTypeServerConnectConfigurationImpl(
override val metadata: MimeTypeWithName,
override val data: MimeTypeWithName,
override val metadata: MimeType.WithName,
override val data: MimeType.WithName,
) : MimeTypeServerConnectConfiguration
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.rsocket.kotlin.configuration

import io.rsocket.kotlin.core.*
import io.rsocket.kotlin.*

public sealed interface PayloadConfiguration {
//TODO: Long?
Expand Down Expand Up @@ -55,8 +55,8 @@ internal class PayloadClientConnectConfigurationImpl(

internal class PayloadServerConnectConfigurationImpl(
configurationState: ConfigurationState,
metadataMimeType: MimeTypeWithName,
dataMimeType: MimeTypeWithName,
metadataMimeType: MimeType.WithName,
dataMimeType: MimeType.WithName,
) : PayloadServerConnectConfiguration, PayloadConnectConfigurationImpl(configurationState) {
override val mimeType: MimeTypeServerConnectConfigurationImpl =
MimeTypeServerConnectConfigurationImpl(metadataMimeType, dataMimeType)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.rsocket.kotlin.configuration

import io.ktor.utils.io.core.*
import io.rsocket.kotlin.core.*
import io.rsocket.kotlin.*
import io.rsocket.kotlin.payload.*
import kotlin.time.*

Expand Down Expand Up @@ -52,8 +52,8 @@ internal class RSocketServerConnectConfigurationImpl(
configurationState: ConfigurationState,
keepAliveInterval: Duration,
keepAliveMaxLifetime: Duration,
metadataMimeType: MimeTypeWithName,
dataMimeType: MimeTypeWithName,
metadataMimeType: MimeType.WithName,
dataMimeType: MimeType.WithName,
setupPayload: Payload,
) : RSocketServerConnectConfiguration, RSocketConnectConfigurationImpl() {
override val setup: SetupServerConnectConfigurationImpl =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package io.rsocket.kotlin.connect

import io.rsocket.kotlin.*
import io.rsocket.kotlin.configuration.*
import io.rsocket.kotlin.core.*
import io.rsocket.kotlin.frame.*
import io.rsocket.kotlin.frame.io.*
import io.rsocket.kotlin.internal.*
Expand Down Expand Up @@ -214,8 +213,8 @@ internal class RSocketServerConnectContextImpl(
deferredRequester: Deferred<RSocket>,
keepAliveInterval: Duration,
keepAliveMaxLifetime: Duration,
metadataMimeType: MimeTypeWithName,
dataMimeType: MimeTypeWithName,
metadataMimeType: MimeType.WithName,
dataMimeType: MimeType.WithName,
setupPayload: Payload,
) : RSocketServerConnectContext, RSocketConnectContextImpl(session, deferredRequester) {
override val isServer: Boolean get() = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
package io.rsocket.kotlin.connect

import io.rsocket.kotlin.*
import io.rsocket.kotlin.core.*
import io.rsocket.kotlin.frame.*
import io.rsocket.kotlin.frame.io.*
import io.rsocket.kotlin.logging.*
Expand Down Expand Up @@ -131,10 +130,8 @@ private class RSocketServerImpl(
deferred,
keepAliveInterval = setupFrame.keepAliveIntervalMillis.milliseconds,
keepAliveMaxLifetime = setupFrame.keepAliveMaxLifetimeMillis.milliseconds,
metadataMimeType = WellKnownMimeType(setupFrame.metadataMimeTypeText)
?: CustomMimeType(setupFrame.metadataMimeTypeText),
dataMimeType = WellKnownMimeType(setupFrame.dataMimeTypeText)
?: CustomMimeType(setupFrame.dataMimeTypeText),
metadataMimeType = MimeType(setupFrame.metadataMimeTypeText),
dataMimeType = MimeType(setupFrame.dataMimeTypeText),
setupPayload = setupFrame.payload,
)
connectContext.configure(beforeConfigurators, afterConfigurators, peerConfigurator)
Expand Down

This file was deleted.

Loading

0 comments on commit b7c7db9

Please sign in to comment.