[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[libeufin] branch master updated (35c2cb9 -> 4424785)
From: |
gnunet |
Subject: |
[libeufin] branch master updated (35c2cb9 -> 4424785) |
Date: |
Mon, 11 Oct 2021 09:51:00 +0200 |
This is an automated email from the git hooks/post-receive script.
ms pushed a change to branch master
in repository libeufin.
from 35c2cb9 syntax
new 569904a log conf
new b2423b3 Logging and sending HTTP chunks via library's handlers.
new 0a01af8 renaming
new afacf8c Ask env for base URL.
new 4424785 ignore trial-and-error test
The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../tech/libeufin/nexus/server/NexusServer.kt | 10 +-
nexus/src/main/resources/logback.xml | 1 +
.../src/main/kotlin/tech/libeufin/sandbox/Main.kt | 41 ++++----
util/src/main/kotlin/Config.kt | 6 +-
util/src/main/kotlin/UnixDomainSocket.kt | 110 ++++++++++++---------
util/src/test/kotlin/DomainSocketTest.kt | 14 ++-
.../main => util/src/test}/resources/logback.xml | 6 +-
7 files changed, 107 insertions(+), 81 deletions(-)
copy {sandbox/src/main => util/src/test}/resources/logback.xml (79%)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt
index 5fb37ea..a9a2e64 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt
@@ -54,6 +54,12 @@ import tech.libeufin.util.*
import java.net.BindException
import java.net.URLEncoder
import kotlin.system.exitProcess
+import java.net.URL
+
+private val baseUrl = URL(
+ getValueFromEnv("LIBEUFIN_NEXUS_BASE_URL") ?: throw Exception(
+ "env LIBEUFIN_NEXUS_BASE_URL is not defined")
+)
/**
* Return facade state depending on the type.
@@ -895,7 +901,7 @@ val nexusApp: Application.() -> Unit = {
type = f.type,
baseUrl = call.url {
parameters.clear()
- encodedPath = ""
+ encodedPath = baseUrl.path
pathComponents("facades", f.facadeName, f.type)
encodedPath += "/"
},
@@ -922,7 +928,7 @@ val nexusApp: Application.() -> Unit = {
type = it.type,
baseUrl = call.url {
parameters.clear()
- encodedPath = ""
+ encodedPath = baseUrl.path
pathComponents("facades", it.facadeName,
it.type)
encodedPath += "/"
},
diff --git a/nexus/src/main/resources/logback.xml
b/nexus/src/main/resources/logback.xml
index 54df1d0..d4e1605 100644
--- a/nexus/src/main/resources/logback.xml
+++ b/nexus/src/main/resources/logback.xml
@@ -14,6 +14,7 @@
<logger name="io.netty" level="WARN"/>
<logger name="ktor" level="WARN"/>
<logger name="Exposed" level="WARN"/>
+ <logger name="tech.libeufin.util" level="DEBUG"/>
<root level="WARN">
<appender-ref ref="STDERR"/>
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index 9411577..d0e8f7e 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -63,16 +63,12 @@ import com.github.ajalt.clikt.parameters.options.*
import com.github.ajalt.clikt.parameters.types.int
import execThrowableOrTerminate
import io.ktor.application.ApplicationCall
-import io.ktor.application.ApplicationCallPipeline
import io.ktor.application.call
import io.ktor.application.install
-import io.ktor.features.CallLogging
-import io.ktor.features.ContentNegotiation
import org.jetbrains.exposed.sql.statements.api.ExposedBlob
import com.fasterxml.jackson.core.util.DefaultIndenter
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter
import com.fasterxml.jackson.module.kotlin.KotlinModule
-import com.fasterxml.jackson.databind.SerializationFeature
import
org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
import io.ktor.features.StatusPages
import io.ktor.response.respond
@@ -84,19 +80,23 @@ import io.ktor.routing.*
import io.ktor.server.netty.*
import io.ktor.util.date.*
import io.ktor.application.*
+import io.ktor.util.*
import kotlinx.coroutines.newSingleThreadContext
import startServer
import tech.libeufin.util.*
import validatePlainAmount
import java.net.BindException
-import java.util.*
+import java.net.URL
import kotlin.system.exitProcess
private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.sandbox")
-private val hostName: String? = getValueFromEnv("LIBEUFIN_SANDBOX_HOSTNAME")
private val currencyEnv: String? = getValueFromEnv("LIBEUFIN_SANDBOX_CURRENCY")
private val envName: String? = getValueFromEnv("TALER_ENV_NAME")
const val SANDBOX_DB_ENV_VAR_NAME = "LIBEUFIN_SANDBOX_DB_CONNECTION"
+private val baseUrl = URL(
+ getValueFromEnv("LIBEUFIN_SANDBOX_BASE_URL") ?: throw Exception(
+ "env LIBEUFIN_SANDBOX_BASE_URL is not defined")
+)
data class SandboxError(
val statusCode: HttpStatusCode,
@@ -889,24 +889,20 @@ val sandboxApp: Application.() -> Unit = {
*/
get("/taler") {
requireSuperuser(call.request)
- SandboxAssert(
- hostName != null,
- "Own hostname not found. Logs should have warned"
- )
SandboxAssert(
currencyEnv != null,
"Currency not found. Logs should have warned"
)
// check that the three canonical accounts exist
val wo = transaction {
- val exchange = tech.libeufin.sandbox.BankAccountEntity.find {
- tech.libeufin.sandbox.BankAccountsTable.label eq
"sandbox-account-exchange"
+ val exchange = BankAccountEntity.find {
+ BankAccountsTable.label eq "sandbox-account-exchange"
}.firstOrNull()
- val customer = tech.libeufin.sandbox.BankAccountEntity.find {
- tech.libeufin.sandbox.BankAccountsTable.label eq
"sandbox-account-customer"
+ val customer = BankAccountEntity.find {
+ BankAccountsTable.label eq "sandbox-account-customer"
}.firstOrNull()
- val merchant = tech.libeufin.sandbox.BankAccountEntity.find {
- tech.libeufin.sandbox.BankAccountsTable.label eq
"sandbox-account-merchant"
+ val merchant = BankAccountEntity.find {
+ BankAccountsTable.label eq "sandbox-account-merchant"
}.firstOrNull()
SandboxAssert(exchange != null, "exchange has no bank account")
@@ -914,15 +910,22 @@ val sandboxApp: Application.() -> Unit = {
SandboxAssert(merchant != null, "merchant has no bank account")
// At this point, the three actors exist and a new withdraw
operation can be created.
- tech.libeufin.sandbox.TalerWithdrawalEntity.new {
+ TalerWithdrawalEntity.new {
// wopid is autogenerated, and momentarily the only column
-
}
}
/**
* Future versions will include the QR code in this response.
*/
- call.respondText("taler://withdraw/${hostName}/api/${wo.wopid}")
+ val ret = call.url {
+ protocol = URLProtocol(
+ "taler".plus(if (baseUrl.protocol.lowercase() == "http")
"+http" else ""),
+ -1
+ )
+ pathComponents(baseUrl.path, "api", wo.wopid.toString())
+ encodedPath += "/"
+ }
+ call.respondText(ret)
return@get
}
get("/api/config") {
diff --git a/util/src/main/kotlin/Config.kt b/util/src/main/kotlin/Config.kt
index bda57ea..8a9aa43 100644
--- a/util/src/main/kotlin/Config.kt
+++ b/util/src/main/kotlin/Config.kt
@@ -51,12 +51,12 @@ fun setLogLevel(logLevel: String?) {
}
fun getValueFromEnv(varName: String): String? {
- val hostName = System.getenv(varName)
- if (hostName.isNullOrBlank() or hostName.isNullOrEmpty()) {
+ val ret = System.getenv(varName)
+ if (ret.isNullOrBlank() or ret.isNullOrEmpty()) {
println("WARNING, $varName was not found in the environment. Will stay
unknown")
return null
}
- return hostName
+ return ret
}
fun getDbConnFromEnv(varName: String): String {
diff --git a/util/src/main/kotlin/UnixDomainSocket.kt
b/util/src/main/kotlin/UnixDomainSocket.kt
index b2713f2..448f1d5 100644
--- a/util/src/main/kotlin/UnixDomainSocket.kt
+++ b/util/src/main/kotlin/UnixDomainSocket.kt
@@ -1,75 +1,75 @@
import io.ktor.application.*
import io.ktor.client.statement.*
import io.ktor.http.*
+import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.server.engine.*
import io.ktor.server.testing.*
+import io.ktor.utils.io.pool.*
import io.netty.bootstrap.ServerBootstrap
+import io.netty.buffer.ByteBuf
import io.netty.buffer.ByteBufInputStream
import io.netty.buffer.Unpooled
+import io.netty.buffer.UnpooledDirectByteBuf
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.LengthFieldPrepender
import io.netty.handler.codec.http.*
import io.netty.handler.codec.http.DefaultHttpResponse
+import io.netty.handler.codec.http.HttpMessage
+import io.netty.handler.logging.LogLevel
+import io.netty.handler.logging.LoggingHandler
+import io.netty.handler.stream.ChunkedInput
+import io.netty.handler.stream.ChunkedStream
+import io.netty.handler.stream.ChunkedWriteHandler
import io.netty.util.AttributeKey
+import io.netty.util.ReferenceCountUtil
+import org.slf4j.LoggerFactory
+import java.io.ByteArrayInputStream
+import java.io.InputStream
+import java.nio.charset.Charset
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()
+ try {
+ val serverBootstrap = ServerBootstrap()
+ serverBootstrap.group(boss, worker).channel(
+ EpollServerDomainSocketChannel::class.java
+ ).childHandler(LibeufinHttpInit(app))
+ val socketPath = DomainSocketAddress(unixSocketPath)
+ logger.debug("Listening on $unixSocketPath ..")
+ serverBootstrap.bind(socketPath).sync().channel().closeFuture().sync()
+ } finally {
+ boss.shutdownGracefully()
+ worker.shutdownGracefully()
+ }
}
-private val ktorApplicationKey = AttributeKey.newInstance<Application.() ->
Unit>("KtorApplicationCall")
-
-class LibeufinHttpInit(private val app: Application.() -> Unit) :
ChannelInitializer<Channel>() {
+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)
+ ).addLast(LoggingHandler("tech.libeufin.util")
+ ).addLast(HttpServerCodec() // in- and out- bound
+ ).addLast(HttpObjectAggregator(Int.MAX_VALUE) // only in- bound
+ ).addLast(ChunkedWriteHandler()
+ ).addLast(LibeufinHttpHandler(app)) // in- bound, and triggers out-
bound.
}
}
-class LibeufinHttpHandler : SimpleChannelInboundHandler<FullHttpRequest>() {
-
+class LibeufinHttpHandler(
+ private val app: Application.() -> Unit
+) : 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.
- */
+ override fun channelRead0(ctx: ChannelHandlerContext, msg:
FullHttpRequest) {
withTestApplication(app) {
val httpVersion = msg.protocolVersion()
- // Proxying the request with Ktor API.
- val call = handleRequest(closeRequest = false) {
+ // Proxying the request to Ktor API.
+ val call = handleRequest {
msg.headers().forEach { addHeader(it.key, it.value) }
method = HttpMethod(msg.method().name())
uri = msg.uri()
@@ -81,17 +81,29 @@ class LibeufinHttpHandler :
SimpleChannelInboundHandler<FullHttpRequest>() {
"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(
+ // Responding to Netty API.
+ val response = DefaultHttpResponse(
httpVersion,
- HttpResponseStatus.valueOf(statusCode),
- Unpooled.wrappedBuffer(call.response.byteContent ?:
ByteArray(0))
+ HttpResponseStatus.valueOf(statusCode)
)
+ var chunked = false
call.response.headers.allValues().forEach { s, list ->
- response.headers().set(s, list.joinToString()) //
joinToString() separates with ", " by default.
+ if (s == HttpHeaders.TransferEncoding &&
list.contains("chunked"))
+ chunked = true
+ response.headers().set(s, list.joinToString())
+ }
+ ctx.writeAndFlush(response)
+ if (chunked) {
+ ctx.writeAndFlush(
+ HttpChunkedInput(
+ ChunkedStream(
+ ByteArrayInputStream(call.response.byteContent)
+ )
+ )
+ )
+ } else {
+
ctx.writeAndFlush(Unpooled.wrappedBuffer(call.response.byteContent))
}
- ctx.write(response)
- ctx.flush()
}
}
}
\ No newline at end of file
diff --git a/util/src/test/kotlin/DomainSocketTest.kt
b/util/src/test/kotlin/DomainSocketTest.kt
index d6e5c18..73e2cdd 100644
--- a/util/src/test/kotlin/DomainSocketTest.kt
+++ b/util/src/test/kotlin/DomainSocketTest.kt
@@ -1,3 +1,8 @@
+import com.fasterxml.jackson.core.util.DefaultIndenter
+import com.fasterxml.jackson.core.util.DefaultPrettyPrinter
+import com.fasterxml.jackson.databind.DeserializationFeature
+import com.fasterxml.jackson.databind.SerializationFeature
+import com.fasterxml.jackson.module.kotlin.KotlinModule
import io.ktor.application.*
import io.ktor.features.*
import io.ktor.http.*
@@ -6,22 +11,21 @@ import io.ktor.routing.*
import org.junit.Test
import io.ktor.jackson.jackson
import io.ktor.request.*
+import org.junit.Assert
+import org.junit.Ignore
class DomainSocketTest {
- // @Test
+ @Test @Ignore
fun bind() {
startServer("/tmp/java.sock") {
install(ContentNegotiation) { jackson() }
routing {
- // responds with a empty JSON object.
get("/") {
this.call.respond(object {})
return@get
}
- // echoes what it read in the request.
post("/") {
- val body = this.call.receiveText()
- this.call.respondText(body)
+ this.call.respond(object {})
return@post
}
}
diff --git a/sandbox/src/main/resources/logback.xml
b/util/src/test/resources/logback.xml
similarity index 79%
copy from sandbox/src/main/resources/logback.xml
copy to util/src/test/resources/logback.xml
index d761ec6..4e7740a 100644
--- a/sandbox/src/main/resources/logback.xml
+++ b/util/src/test/resources/logback.xml
@@ -1,12 +1,12 @@
<configuration>
<appender name="STDERR" class="ch.qos.logback.core.ConsoleAppender">
- <target>System.err</target>
+ <target>System.err</target>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -
%msg%n</pattern>
</encoder>
</appender>
- <logger name="tech.libeufin.sandbox" level="DEBUG" additivity="false">
+ <logger name="tech.libeufin.util" level="DEBUG" additivity="false">
<appender-ref ref="STDERR" />
</logger>
@@ -18,4 +18,4 @@
<appender-ref ref="STDERR" />
</root>
-</configuration>
+</configuration>
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [libeufin] branch master updated (35c2cb9 -> 4424785),
gnunet <=
- [libeufin] 01/05: log conf, gnunet, 2021/10/11
- [libeufin] 03/05: renaming, gnunet, 2021/10/11
- [libeufin] 05/05: ignore trial-and-error test, gnunet, 2021/10/11
- [libeufin] 02/05: Logging and sending HTTP chunks via library's handlers., gnunet, 2021/10/11
- [libeufin] 04/05: Ask env for base URL., gnunet, 2021/10/11