diff --git a/examples/tcpserver.lyng b/examples/tcpserver.lyng new file mode 100644 index 0000000..db72fb4 --- /dev/null +++ b/examples/tcpserver.lyng @@ -0,0 +1,21 @@ +import lyng.buffer +import lyng.io.net + +val server = Net.tcpListen(0, "127.0.0.1") +val port = server.localAddress().port +val accepted = launch { + val client = server.accept() + val line = (client.read(4) as Buffer).decodeUtf8() + client.writeUtf8("echo:" + line) + client.flush() + client.close() + server.close() + line +} + +val socket = Net.tcpConnect("127.0.0.1", port) +socket.writeUtf8("ping") +socket.flush() +val reply = (socket.read(16) as Buffer).decodeUtf8() +socket.close() +println("${accepted.await()}: $reply") diff --git a/lyng/src/commonMain/kotlin/Common.kt b/lyng/src/commonMain/kotlin/Common.kt index 9fbfb93..a2c94a9 100644 --- a/lyng/src/commonMain/kotlin/Common.kt +++ b/lyng/src/commonMain/kotlin/Common.kt @@ -27,7 +27,7 @@ import com.github.ajalt.clikt.parameters.arguments.optional import com.github.ajalt.clikt.parameters.options.flag import com.github.ajalt.clikt.parameters.options.option import kotlinx.coroutines.runBlocking -import net.sergeych.lyng.Compiler +import net.sergeych.lyng.EvalSession import net.sergeych.lyng.LyngVersion import net.sergeych.lyng.Script import net.sergeych.lyng.ScriptError @@ -216,12 +216,7 @@ private class Lyng(val launcher: (suspend () -> Unit) -> Unit) : CliktCommand() launcher { // there is no script name, it is a first argument instead: processErrors { - val script = Compiler.compileWithResolution( - Source("", execute!!), - baseScope.currentImportProvider, - seedScope = baseScope - ) - script.execute(baseScope) + executeSource(Source("", execute!!)) } } } @@ -247,6 +242,15 @@ fun executeFileWithArgs(fileName: String, args: List) { } } +suspend fun executeSource(source: Source) { + val session = EvalSession(baseScopeDefer.await()) + try { + session.eval(source) + } finally { + session.cancelAndJoin() + } +} + suspend fun executeFile(fileName: String) { var text = FileSystem.SYSTEM.source(fileName.toPath()).use { fileSource -> fileSource.buffer().use { bs -> @@ -259,13 +263,7 @@ suspend fun executeFile(fileName: String) { text = text.substring(pos + 1) } processErrors { - val scope = baseScopeDefer.await() - val script = Compiler.compileWithResolution( - Source(fileName, text), - scope.currentImportProvider, - seedScope = scope - ) - script.execute(scope) + executeSource(Source(fileName, text)) } } diff --git a/lyngio/src/commonTest/kotlin/net/sergeych/lyng/io/net/LyngNetTcpServerExampleTest.kt b/lyngio/src/commonTest/kotlin/net/sergeych/lyng/io/net/LyngNetTcpServerExampleTest.kt new file mode 100644 index 0000000..a2fd20e --- /dev/null +++ b/lyngio/src/commonTest/kotlin/net/sergeych/lyng/io/net/LyngNetTcpServerExampleTest.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com + * + * 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 net.sergeych.lyng.io.net + +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withTimeout +import net.sergeych.lyng.Compiler +import net.sergeych.lyng.Script +import net.sergeych.lyngio.net.getSystemNetEngine +import net.sergeych.lyngio.net.security.PermitAllNetAccessPolicy +import kotlin.test.Test +import kotlin.test.assertEquals + +class LyngNetTcpServerExampleTest { + + @Test + fun tcpServerExampleRoundTripsOverLoopback() = runBlocking { + val engine = getSystemNetEngine() + if (!engine.isSupported || !engine.isTcpAvailable || !engine.isTcpServerAvailable) return@runBlocking + + val scope = Script.newScope() + createNetModule(PermitAllNetAccessPolicy, scope) + + val code = """ + import lyng.buffer + import lyng.io.net + + val server = Net.tcpListen(0, "127.0.0.1") + val port = server.localAddress().port + val accepted = launch { + val client = server.accept() + val line = (client.read(4) as Buffer).decodeUtf8() + client.writeUtf8("echo:" + line) + client.flush() + client.close() + server.close() + line + } + + val socket = Net.tcpConnect("127.0.0.1", port) + socket.writeUtf8("ping") + socket.flush() + val reply = (socket.read(16) as Buffer).decodeUtf8() + socket.close() + "${'$'}{accepted.await()}: ${'$'}reply" + """.trimIndent() + + val result = withTimeout(5_000) { + Compiler.compile(code).execute(scope).inspect(scope) + } + + assertEquals("\"ping: echo:ping\"", result) + } +}