Add KMP networking backends

This commit is contained in:
sergeych committed 2026-04-02 19:23:46 +03:00
1 parent d409a4bb8b
commit 5346d15a9f
25 files changed
+1744 -75

No files matched your search

@@ -0,0 +1,117 @@
package net.sergeych.lyng.io.net
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.promise
import net.sergeych.lyng.Compiler
import net.sergeych.lyng.ExecutionError
import net.sergeych.lyng.Script
import net.sergeych.lyngio.fs.security.AccessContext
import net.sergeych.lyngio.fs.security.AccessDecision
import net.sergeych.lyngio.fs.security.Decision
import net.sergeych.lyngio.net.getSystemNetEngine
import net.sergeych.lyngio.net.security.NetAccessOp
import net.sergeych.lyngio.net.security.NetAccessPolicy
import net.sergeych.lyngio.net.security.PermitAllNetAccessPolicy
import kotlin.test.Test
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
@OptIn(DelicateCoroutinesApi::class)
class LyngNetModuleJsNodeTest {
@Test
fun testResolveAndCapabilities() = GlobalScope.promise {
val scope = Script.newScope()
createNetModule(PermitAllNetAccessPolicy, scope)
val code = """
import lyng.io.net
val a: SocketAddress = Net.resolve("127.0.0.1", 4040)[0]
[Net.isSupported(), Net.isTcpAvailable(), Net.isTcpServerAvailable(), Net.isUdpAvailable(), a.toString(), a.resolved, a.ipVersion == IpVersion.IPV4]
""".trimIndent()
val result = Compiler.compile(code).execute(scope).inspect(scope)
assertTrue(result.contains("true,true,true,true"), result)
assertTrue(result.contains("127.0.0.1:4040"), result)
}
@Test
fun testTcpConnectConvenience() = GlobalScope.promise {
val engine = getSystemNetEngine()
val server = engine.tcpListen(host = "127.0.0.1", port = 0, backlog = 8, reuseAddress = true)
val serverPort = server.localAddress().port
val worker = async {
val client = server.accept()
val line = client.read(4)?.decodeToString()
client.writeUtf8("reply:$line")
client.flush()
client.close()
}
val scope = Script.newScope()
createNetModule(PermitAllNetAccessPolicy, scope)
val code = """
import lyng.buffer
import lyng.io.net
val socket = Net.tcpConnect("127.0.0.1", $serverPort)
socket.writeUtf8("ping")
socket.flush()
val reply = (socket.read(16) as Buffer).decodeUtf8()
val localPort = socket.localAddress().port
val remotePort = socket.remoteAddress().port
socket.close()
[reply, localPort > 0, remotePort == $serverPort]
""".trimIndent()
val result = Compiler.compile(code).execute(scope).inspect(scope)
worker.await()
server.close()
assertTrue(result.contains("reply:ping"), result)
assertTrue(result.contains("true,true"), result)
}
@Test
fun testUdpLoopback() = GlobalScope.promise {
val scope = Script.newScope()
createNetModule(PermitAllNetAccessPolicy, scope)
val code = """
import lyng.buffer
import lyng.io.net
val server = Net.udpBind(0, "127.0.0.1")
val client = Net.udpBind(0, "127.0.0.1")
client.send(Buffer("ping"), "127.0.0.1", server.localAddress().port)
val d = server.receive()
client.close()
server.close()
[d.data.decodeUtf8(), d.address.port > 0]
""".trimIndent()
val result = Compiler.compile(code).execute(scope).inspect(scope)
assertTrue(result.contains("[ping,true]"), result)
}
@Test
fun testPolicyDenialSurfacesAsLyngError() = GlobalScope.promise {
val scope = Script.newScope()
val denyAll = object : NetAccessPolicy {
override suspend fun check(op: NetAccessOp, ctx: AccessContext): AccessDecision =
AccessDecision(Decision.Deny, "blocked by test policy")
}
createNetModule(denyAll, scope)
val code = """
import lyng.io.net
Net.tcpConnect("127.0.0.1", 1)
""".trimIndent()
val error = assertFailsWith<ExecutionError> {
Compiler.compile(code).execute(scope)
}
assertTrue(error.errorMessage.isNotBlank())
}
}
@@ -0,0 +1,70 @@
package net.sergeych.lyngio
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.promise
import net.sergeych.lyngio.net.LyngIpVersion
import net.sergeych.lyngio.net.getSystemNetEngine
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
@OptIn(DelicateCoroutinesApi::class)
class NetJsNodeTest {
@Test
fun testNodeNetCapabilitiesAndResolve() = GlobalScope.promise {
val engine = getSystemNetEngine()
assertTrue(engine.isSupported)
assertTrue(engine.isTcpAvailable)
assertTrue(engine.isTcpServerAvailable)
assertTrue(engine.isUdpAvailable)
val resolved = engine.resolve("127.0.0.1", 4040)
assertTrue(resolved.isNotEmpty())
assertEquals(4040, resolved.first().port)
assertEquals(LyngIpVersion.IPV4, resolved.first().ipVersion)
}
@Test
fun testNodeTcpLoopback() = GlobalScope.promise {
val engine = getSystemNetEngine()
val server = engine.tcpListen(host = "127.0.0.1", port = 0, backlog = 8, reuseAddress = true)
val accepted = async {
val socket = server.accept()
val line = socket.readLine()
socket.writeUtf8("echo:$line\n")
socket.flush()
socket.close()
line
}
val client = engine.tcpConnect("127.0.0.1", server.localAddress().port, timeoutMillis = null, noDelay = true)
client.writeUtf8("ping\n")
client.flush()
val reply = client.readLine()
client.close()
server.close()
assertEquals("ping", accepted.await())
assertEquals("echo:ping", reply)
}
@Test
fun testNodeUdpLoopback() = GlobalScope.promise {
val engine = getSystemNetEngine()
val server = engine.udpBind(host = "127.0.0.1", port = 0, reuseAddress = true)
val client = engine.udpBind(host = "127.0.0.1", port = 0, reuseAddress = true)
client.send("ping".encodeToByteArray(), "127.0.0.1", server.localAddress().port)
val received = server.receive(1024)
client.close()
server.close()
assertNotNull(received)
assertEquals("ping", received.data.decodeToString())
assertTrue(received.address.port > 0)
}
}
@@ -0,0 +1,14 @@
package net.sergeych.lyngio
import net.sergeych.lyngio.http.getSystemHttpEngine
import net.sergeych.lyngio.ws.getSystemWsEngine
import kotlin.test.Test
import kotlin.test.assertTrue
class PlatformCapabilityJsTest {
@Test
fun testJsHttpAndWsCapabilitiesReportSupported() {
assertTrue(getSystemHttpEngine().isSupported, "JS HTTP engine should be available")
assertTrue(getSystemWsEngine().isSupported, "JS websocket engine should be available")
}
}