Skip to content

Commit

Permalink
SwiftNIO: Add LineBasedFrameDecoder
Browse files Browse the repository at this point in the history
  • Loading branch information
yuroyami committed Apr 5, 2024
1 parent 16a9920 commit 3922bf6
Show file tree
Hide file tree
Showing 13 changed files with 115 additions and 57 deletions.
17 changes: 17 additions & 0 deletions iosApp/iosApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
04BF80122B9704FA002E2EDE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04BF80112B9704FA002E2EDE /* Foundation.framework */; };
04BF80242B97056B002E2EDE /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04BF80232B97056B002E2EDE /* UIKit.framework */; };
04E674932AB8AF3D00404C63 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 04E674922AB8AF3D00404C63 /* Launch Screen.storyboard */; };
04EE38EC2BC00C5100E830A3 /* NIOExtras in Frameworks */ = {isa = PBXBuildFile; productRef = 04EE38EB2BC00C5100E830A3 /* NIOExtras */; };
04FEEA3A2BBD821400E9AC8F /* NIOTransportServices in Frameworks */ = {isa = PBXBuildFile; productRef = 04FEEA392BBD821400E9AC8F /* NIOTransportServices */; };
058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; };
058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; };
Expand Down Expand Up @@ -76,6 +77,7 @@
0424B733C25F1A6509DE1F07 /* Pods_iosApp.framework in Frameworks */,
0404ED342BB9375000FCDAB5 /* _NIOFileSystemFoundationCompat in Frameworks */,
0404ED2A2BB9375000FCDAB5 /* NIOPosix in Frameworks */,
04EE38EC2BC00C5100E830A3 /* NIOExtras in Frameworks */,
04BF80022B9704B9002E2EDE /* AudioToolbox.framework in Frameworks */,
0404ED262BB9374F00FCDAB5 /* NIOEmbedded in Frameworks */,
0404ED202BB9374F00FCDAB5 /* NIO in Frameworks */,
Expand Down Expand Up @@ -202,6 +204,7 @@
0404ED332BB9375000FCDAB5 /* _NIOFileSystemFoundationCompat */,
0404ED382BB96D4C00FCDAB5 /* NIOSSL */,
04FEEA392BBD821400E9AC8F /* NIOTransportServices */,
04EE38EB2BC00C5100E830A3 /* NIOExtras */,
);
productName = iosApp;
productReference = 7555FF7B242A565900829871 /* iosApp.app */;
Expand Down Expand Up @@ -235,6 +238,7 @@
0404ED1E2BB9374F00FCDAB5 /* XCRemoteSwiftPackageReference "swift-nio" */,
0404ED372BB96D4C00FCDAB5 /* XCRemoteSwiftPackageReference "swift-nio-ssl" */,
04FEEA382BBD821400E9AC8F /* XCRemoteSwiftPackageReference "swift-nio-transport-services" */,
04EE38EA2BC00C5100E830A3 /* XCRemoteSwiftPackageReference "swift-nio-extras" */,
);
productRefGroup = 7555FF7C242A565900829871 /* Products */;
projectDirPath = "";
Expand Down Expand Up @@ -541,6 +545,14 @@
minimumVersion = 2.26.0;
};
};
04EE38EA2BC00C5100E830A3 /* XCRemoteSwiftPackageReference "swift-nio-extras" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/apple/swift-nio-extras.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.22.0;
};
};
04FEEA382BBD821400E9AC8F /* XCRemoteSwiftPackageReference "swift-nio-transport-services" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/apple/swift-nio-transport-services";
Expand Down Expand Up @@ -612,6 +624,11 @@
package = 0404ED372BB96D4C00FCDAB5 /* XCRemoteSwiftPackageReference "swift-nio-ssl" */;
productName = NIOSSL;
};
04EE38EB2BC00C5100E830A3 /* NIOExtras */ = {
isa = XCSwiftPackageProductDependency;
package = 04EE38EA2BC00C5100E830A3 /* XCRemoteSwiftPackageReference "swift-nio-extras" */;
productName = NIOExtras;
};
04FEEA392BBD821400E9AC8F /* NIOTransportServices */ = {
isa = XCSwiftPackageProductDependency;
package = 04FEEA382BBD821400E9AC8F /* XCRemoteSwiftPackageReference "swift-nio-transport-services" */;
Expand Down
27 changes: 27 additions & 0 deletions iosApp/iosApp.xcworkspace/xcshareddata/swiftpm/Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@
"version" : "1.1.0"
}
},
{
"identity" : "swift-http-types",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-http-types",
"state" : {
"revision" : "12358d55a3824bd5fed310b999ea8cf83a9a1a65",
"version" : "1.0.3"
}
},
{
"identity" : "swift-nio",
"kind" : "remoteSourceControl",
Expand All @@ -27,6 +36,24 @@
"version" : "2.64.0"
}
},
{
"identity" : "swift-nio-extras",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-extras.git",
"state" : {
"revision" : "a3b640d7dc567225db7c94386a6e71aded1bfa63",
"version" : "1.22.0"
}
},
{
"identity" : "swift-nio-http2",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-http2.git",
"state" : {
"revision" : "0904bf0feb5122b7e5c3f15db7df0eabe623dd87",
"version" : "1.30.0"
}
},
{
"identity" : "swift-nio-ssl",
"kind" : "remoteSourceControl",
Expand Down
25 changes: 13 additions & 12 deletions iosApp/iosApp/SpProtocolIApple.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Foundation
import NIO
import NIOFoundationCompat
import NIOTransportServices
import NIOExtras
import shared

@objc class SpProtocolApple: SyncplayProtocol, ChannelInboundHandler {
Expand All @@ -10,21 +11,22 @@ import shared
private var channel: Channel?
private var eventLoopGroup: EventLoopGroup?

//override let engine = SyncplayProtocol.NetworkEngine.swiftnio

@objc override func connectSocket() {
let group = NIOTSEventLoopGroup()
eventLoopGroup = group

print("Bootstrapping...")

let host = session.serverHost
let port = Int(session.serverPort)

let result = NIOTSConnectionBootstrap(group: group)
let result: EventLoopFuture<Channel> = NIOTSConnectionBootstrap(group: group)
.connectTimeout(TimeAmount.seconds(10))
.channelInitializer { channel in
channel.pipeline.addHandler(self)
}
.connect(host: host, port: port)
channel.pipeline.addHandler(ByteToMessageHandler(LineBasedFrameDecoder())).flatMap {
channel.pipeline.addHandler(self)
}
}.connect(host: host, port: port)

result.whenSuccess { channel in
self.channel = channel
Expand Down Expand Up @@ -53,8 +55,8 @@ import shared
}

@objc override func endConnection(terminating: Bool) {
//try? channel?.close().wait()
//try? eventLoopGroup?.syncShutdownGracefully()
try? channel?.close().wait()
try? eventLoopGroup?.syncShutdownGracefully()

if terminating {
terminateScope()
Expand Down Expand Up @@ -89,16 +91,15 @@ import shared
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
var buffer = self.unwrapInboundIn(data)
let readableBytes = buffer.readableBytes
var data = buffer.readData(length: readableBytes)!
let data = buffer.readData(length: readableBytes)!

// Decode the data buffer into a string using ASCII encoding
if let received = String(data: data, encoding: .utf8) {
self.jsonHandler.parse(protocol: self, json: received, retry: true)
self.jsonHandler.parse(protocol: self, json: received)
}
}

func channelReadComplete(context: ChannelHandlerContext) {
//context.flush()
context.flush()
}

func errorCaught(context: ChannelHandlerContext, error: Error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ import io.netty.handler.codec.Delimiters
import io.netty.handler.codec.string.StringDecoder
import io.netty.handler.codec.string.StringEncoder
import io.netty.handler.ssl.SslContextBuilder
import kotlinx.coroutines.launch

class SpProtocolAndroid : SyncplayProtocol() {

override val engine = NetworkEngine.NETTY

/** Netty stuff */
var channel: Channel? = null
lateinit var pipeline: ChannelPipeline
Expand All @@ -33,7 +34,8 @@ class SpProtocolAndroid : SyncplayProtocol() {
.handler(object : ChannelInitializer<SocketChannel>() {
override fun initChannel(ch: SocketChannel) {
pipeline = ch.pipeline()
pipeline.addLast("framer",
pipeline.addLast(
"framer",
DelimiterBasedFrameDecoder(8192, *Delimiters.lineDelimiter())
)
pipeline.addLast(StringDecoder())
Expand Down Expand Up @@ -90,11 +92,10 @@ class SpProtocolAndroid : SyncplayProtocol() {

loggy("Channel event: ${evt.toString()}", 0)
}

override fun channelRead0(ctx: ChannelHandlerContext?, msg: String?) {
if (msg != null) {
protoScope.launch {
JsonHandler.parse(this@SpProtocolAndroid, msg)
}
JsonHandler.parse(this@SpProtocolAndroid, msg)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ val EnStrings = object : Strings {
override val settingNetworkEngineTitle = "Network Engine"
override val settingNetworkEngineSummary = "The network engine serves as the backbone of connectivity. Experiment with each option and choose the one that offers the most stability for your needs."
override val settingNetworkEngineNetty = "Netty (Recommended, TLS)"
override val settingNetworkEngineSwiftNIO = "SwiftNIO (Experimental)"
override val settingNetworkEngineSwiftNIO = "SwiftNIO (Recommended)"
override val settingNetworkEngineKtor = "Ktor (Experimental)"

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package com.yuroyami.syncplay.models

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.font.FontWeight
Expand Down Expand Up @@ -35,10 +31,7 @@ data class Message(
/** Returns an AnnotatedString to use with Compose Text
* @param msgPalette A [MessagePalette] that contains colors and properties
**/
@Composable
fun factorize(msgPalette: MessagePalette): AnnotatedString {
val isActuallyError by remember { mutableStateOf(isError) }

/* An AnnotatedString builder that will append child AnnotatedStings together */
val builder = AnnotatedString.Builder()

Expand Down Expand Up @@ -74,7 +67,7 @@ data class Message(
} else {
AnnotatedString(
text = content,
spanStyle = if (isActuallyError) SpanStyle(msgPalette.errormsgColor) else
spanStyle = if (isError) SpanStyle(msgPalette.errormsgColor) else
SpanStyle(msgPalette.systemmsgColor)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ abstract class BasePlayer {
@Composable
abstract fun VideoPlayer(modifier: Modifier)

fun onPlaybackEnded() {
//TODO
}

fun collectInfoURL(media: MediaFile) {
with (media) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,29 @@ import kotlinx.serialization.json.jsonPrimitive

object JsonHandler {

fun parse(protocol: SyncplayProtocol, json: String, retry: Boolean = true) {
loggy("Server: $json")

/* Second, we check what kind of JSON message we received from the first Json Object */
try {
val element = Json.parseToJsonElement(json).jsonObject
with(element.keys) {
when {
contains("Hello") -> handleHello(element["Hello"]!!.jsonObject, protocol)
contains("Set") -> handleSet(element["Set"]!!.jsonObject, protocol)
contains("List") -> handleList(element["List"]!!.jsonObject, protocol)
contains("State") -> handleState(element["State"]!!.jsonObject, protocol, json)
contains("Chat") -> handleChat(element["Chat"]!!.jsonObject, protocol)
contains("Error") -> handleError(element["Error"]!!.jsonObject, protocol)
contains("TLS") -> handleTLS(element["TLS"]!!.jsonObject, protocol)
else -> dropError("unknown-command-server-error")
fun parse(protocol: SyncplayProtocol, json: String) {
protocol.protoScope.launch {
loggy("Server: $json")

/* Second, we check what kind of JSON message we received from the first Json Object */
try {
val parsed = Json.parseToJsonElement(json)
val element = parsed.jsonObject
with(element.keys) {
when {
contains("Hello") -> handleHello(element["Hello"]!!.jsonObject, protocol)
contains("Set") -> handleSet(element["Set"]!!.jsonObject, protocol)
contains("List") -> handleList(element["List"]!!.jsonObject, protocol)
contains("State") -> handleState(element["State"]!!.jsonObject, protocol, json)
contains("Chat") -> handleChat(element["Chat"]!!.jsonObject, protocol)
contains("Error") -> handleError(element["Error"]!!.jsonObject, protocol)
contains("TLS") -> handleTLS(element["TLS"]!!.jsonObject, protocol)
else -> dropError("unknown-command-server-error")
}
}
} catch (e: Exception) {
loggy(e.stackTraceToString(), 1)
}
} catch (e: Exception) {
if (retry) parse(protocol, json, false);
loggy(e.stackTraceToString(), 1)
loggy("Problematic string: $json")
}
}

Expand All @@ -53,7 +54,8 @@ object JsonHandler {
p.sendPacket(JsonSender.sendJoined(p.session.currentRoom))
p.sendPacket(JsonSender.sendEmptyList())
//p.sendPacket(JsonSender.sendReadiness(p.ready, false))
p.syncplayCallback?.onConnected()
p.syncplayCallback!!.onConnected()
//p.sendPacket(JsonSender.sendEmptyList())
}

private fun handleSet(set: JsonObject, p: SyncplayProtocol) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

class SpProtocolKtor : SyncplayProtocol() {
override val engine = NetworkEngine.KTOR

private var socket: Socket? = null
private var connection: Connection? = null
Expand All @@ -41,9 +42,7 @@ class SpProtocolKtor : SyncplayProtocol() {
while (true) {
connection?.input?.awaitContent()
input?.readUTF8Line()?.let {
launch {
jsonHandler.parse(this@SpProtocolKtor, it)
}
jsonHandler.parse(this@SpProtocolKtor, it)
}

delay(100)
Expand Down Expand Up @@ -77,7 +76,7 @@ class SpProtocolKtor : SyncplayProtocol() {
try {
connection?.output?.writeStringUtf8(s)
connection?.output?.flush()
} catch(e: Exception) {
} catch (e: Exception) {
syncplayCallback?.onDisconnected()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ abstract class SyncplayProtocol {
val finalOut = json + "\r\n"
//loggy("Client: $finalOut", 206)
writeActualString(finalOut)

} else {
/** Queuing any pending outgoing messages */
if (json != sendHello(
Expand Down Expand Up @@ -163,6 +164,8 @@ abstract class SyncplayProtocol {
/** Attempts to upgrade the plain TCP socket to a TLS secure socket */
abstract fun upgradeTls()

open val engine: NetworkEngine = NetworkEngine.SWIFTNIO

enum class NetworkEngine {
KTOR,
NETTY,
Expand All @@ -171,7 +174,7 @@ abstract class SyncplayProtocol {

companion object {
fun getPreferredEngine(): NetworkEngine {
val defaultEngine = if (getPlatform() == PLATFORM.Android) NetworkEngine.NETTY else NetworkEngine.KTOR
val defaultEngine = if (getPlatform() == PLATFORM.Android) NetworkEngine.NETTY else NetworkEngine.SWIFTNIO
val engineName = valueBlockingly(DataStoreKeys.PREF_NETWORK_ENGINE, defaultEngine.name.lowercase())
return NetworkEngine.valueOf(engineName.uppercase())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ private val settingsGLOBAL: List<Pair<Setting<out Any>, String>>
key = PREF_NETWORK_ENGINE,
title = lyricist.strings.settingNetworkEngineTitle,
summary = lyricist.strings.settingNetworkEngineSummary,
defaultValue = if (getPlatform() == PLATFORM.Android) "netty" else "ktor",
defaultValue = if (getPlatform() == PLATFORM.Android) "netty" else "swiftnio",
icon = Icons.Filled.Lan,
styling = settingGLOBALstyle,
entryKeys =
Expand Down
Loading

0 comments on commit 3922bf6

Please sign in to comment.