gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libeufin] 01/07: Unix domain socket server.


From: gnunet
Subject: [libeufin] 01/07: Unix domain socket server.
Date: Wed, 06 Oct 2021 16:17:52 +0200

This is an automated email from the git hooks/post-receive script.

ms pushed a commit to branch master
in repository libeufin.

commit ffef8c7dcf3af4d5c81cb907e7c0005eced0da87
Author: ms <ms@taler.net>
AuthorDate: Wed Oct 6 12:07:10 2021 +0200

    Unix domain socket server.
    
    This version takes HTTP requests from a Unix domain socket managed
    by Netty, and proxies the requests - via the Ktor TestEngine - to a
    Web application.
    
    The TestEngine was chosen since it bridges conveniently between
    raw HTTP requests and the Ktor ApplicationCall API.  Eventually,
    this implementation will disappear once Ktor will offer the API to
    bind to Unix domain sockets.
---
 sandbox/src/test/kotlin/CamtTest.kt      | 34 ------------
 util/src/main/kotlin/UnixDomainSocket.kt | 91 ++++++++++++++++++++++++++++++++
 2 files changed, 91 insertions(+), 34 deletions(-)

diff --git a/sandbox/src/test/kotlin/CamtTest.kt 
b/sandbox/src/test/kotlin/CamtTest.kt
deleted file mode 100644
index 56a53a4..0000000
--- a/sandbox/src/test/kotlin/CamtTest.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-import org.junit.Test
-import tech.libeufin.sandbox.buildCamtString
-import tech.libeufin.util.RawPayment
-import tech.libeufin.util.XMLUtil
-import kotlin.test.assertTrue
-
-class CamtTest {
-
-    @Test
-    fun validationTest() {
-        val payment = RawPayment(
-            creditorIban = "GB33BUKB20201222222222",
-            creditorName = "Oliver Smith",
-            creditorBic = "BUKBGB33",
-            debtorIban = "GB33BUKB20201333333333",
-            debtorName = "John Doe",
-            debtorBic = "BUKBGB33",
-            amount = "2",
-            currency = "EUR",
-            subject = "reimbursement",
-            date = "1000-02-02",
-            uid = "0",
-            direction = "DBIT"
-        )
-        val xml = buildCamtString(
-            53,
-            "GB33BUKB20201222222222",
-            mutableListOf(payment)
-        )
-        assertTrue {
-            XMLUtil.validateFromString(xml)
-        }
-    }
-}
\ No newline at end of file
diff --git a/util/src/main/kotlin/UnixDomainSocket.kt 
b/util/src/main/kotlin/UnixDomainSocket.kt
new file mode 100644
index 0000000..6742189
--- /dev/null
+++ b/util/src/main/kotlin/UnixDomainSocket.kt
@@ -0,0 +1,91 @@
+import io.ktor.application.*
+import io.ktor.client.statement.*
+import io.ktor.http.*
+import io.ktor.http.HttpMethod
+import io.ktor.server.engine.*
+import io.ktor.server.testing.*
+import io.netty.bootstrap.ServerBootstrap
+import io.netty.channel.*
+import io.netty.channel.epoll.EpollEventLoopGroup
+import io.netty.channel.epoll.EpollServerDomainSocketChannel
+import io.netty.channel.unix.DomainSocketAddress
+import io.netty.handler.codec.http.*
+import io.netty.handler.codec.http.DefaultHttpResponse
+import io.netty.util.AttributeKey
+
+fun startServer(unixSocketPath: String, app: Application.() -> Unit) {
+
+    val boss = EpollEventLoopGroup()
+    val worker = EpollEventLoopGroup()
+    val serverBootstrap = ServerBootstrap()
+    serverBootstrap.group(boss, worker).channel(
+        EpollServerDomainSocketChannel::class.java
+    ).childHandler(LibeufinHttpInit(app))
+
+    val socketPath = DomainSocketAddress(unixSocketPath)
+    serverBootstrap.bind(socketPath).sync().channel().closeFuture().sync()
+}
+
+private val ktorApplicationKey = AttributeKey.newInstance<Application.() -> 
Unit>("KtorApplicationCall")
+
+class LibeufinHttpInit(private val app: Application.() -> Unit) : 
ChannelInitializer<Channel>() {
+    override fun initChannel(ch: Channel) {
+        val libeufinHandler = LibeufinHttpHandler()
+        ch.pipeline(
+        ).addLast(
+            HttpServerCodec()
+        ).addLast(
+            HttpObjectAggregator(Int.MAX_VALUE)
+        ).addLast(
+            libeufinHandler
+        )
+        val libeufinCtx: ChannelHandlerContext = 
ch.pipeline().context(libeufinHandler)
+        libeufinCtx.attr(ktorApplicationKey).set(app)
+    }
+}
+
+class LibeufinHttpHandler : SimpleChannelInboundHandler<FullHttpRequest>() {
+
+    @OptIn(EngineAPI::class)
+    override fun channelRead0(ctx: ChannelHandlerContext?, msg: 
FullHttpRequest) {
+        val app = ctx?.attr(ktorApplicationKey)?.get()
+        if (app == null) throw UtilError(
+            HttpStatusCode.InternalServerError,
+            "custom libEufin Unix-domain-socket+HTTP handler lost its Web app",
+            null
+        )
+        /**
+         * Below is only a echo of what euFin gets from the network.  All
+         * the checks should then occur at the Web app + Ktor level.  Hence,
+         * a HTTP call of GET with a non-empty body is not to be blocked / 
warned
+         * at this level.
+         *
+         * The only exception is the HTTP version value in the response, as the
+         * response returned by the Web app does not set it.  Therefore, this
+         * proxy echoes back the HTTP version that was read in the request.
+         */
+        withTestApplication(app) {
+            val httpVersion = msg.protocolVersion()
+            // Proxying the request with Ktor API.
+            val call = handleRequest(closeRequest = false) {
+                msg.headers().forEach { addHeader(it.key, it.value) }
+                method = HttpMethod(msg.method().name())
+                uri = msg.uri()
+                version = httpVersion.text()
+                setBody(msg.content().array())
+            }
+            val statusCode: Int = call.response.status()?.value ?: throw 
UtilError(
+                HttpStatusCode.InternalServerError,
+                "app proxied via Unix domain socket did not include a response 
status code",
+                ec = null // FIXME: to be defined.
+            )
+            // Responding with Netty API.
+            val response = DefaultFullHttpResponse(httpVersion, 
HttpResponseStatus.valueOf(statusCode))
+            call.response.headers.allValues().forEach { s, list ->
+                response.headers().set(s, list.joinToString()) // 
joinToString() separates with ", " by default.
+            }
+            ctx.write(response)
+            ctx.flush()
+        }
+    }
+}
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]