This is an automated email from the ASF dual-hosted git repository. toulmean pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-tuweni.git
commit da996b8d129f8e667e8d859b681bd3eb5ba94e5a Author: Antoine Toulme <[email protected]> AuthorDate: Mon Jun 10 16:35:46 2019 -0700 Add relayer --- dist/build.gradle | 35 +++++- dist/docker/relayer.Dockerfile | 19 +++ settings.gradle => hobbits-relayer/build.gradle | 58 ++++----- .../kotlin/org/apache/tuweni/relayer/RelayerApp.kt | 46 ++++++++ .../org/apache/tuweni/relayer/RelayerAppTest.kt | 57 +++++++++ .../kotlin/org/apache/tuweni/hobbits/Message.kt | 5 + .../kotlin/org/apache/tuweni/hobbits/Relayer.kt | 81 +++++++++++++ .../org/apache/tuweni/hobbits/InteractionTest.kt | 24 ++++ .../org/apache/tuweni/hobbits/RelayerTest.kt | 131 +++++++++++++++++++++ settings.gradle | 1 + 10 files changed, 420 insertions(+), 37 deletions(-) diff --git a/dist/build.gradle b/dist/build.gradle index 62bd44a..cbb8ca9 100644 --- a/dist/build.gradle +++ b/dist/build.gradle @@ -97,6 +97,22 @@ distributions { } } } + relayer { + baseName = 'tuweni-relayer' + contents { + mandatoryFiles(it) + into('bin') { + from { project(':hobbits-relayer').startScripts.outputs.files } + fileMode = 0755 + } + into('lib') { + def libs = [] + libs << project(':hobbits-relayer').configurations.runtime + from libs + from project(':hobbits-relayer').jar + } + } + } } import org.gradle.crypto.checksum.Checksum @@ -107,6 +123,8 @@ sourcesDistTar{ compression = Compression.GZIP } gossipDistTar{ compression = Compression.GZIP } +relayerDistTar{ compression = Compression.GZIP } + if (System.getenv('ENABLE_SIGNING') == 'true') { signing { useGpgCmd() @@ -116,6 +134,8 @@ if (System.getenv('ENABLE_SIGNING') == 'true') { sign sourcesDistTar sign gossipDistZip sign gossipDistTar + sign relayerDistZip + sign relayerDistTar } } @@ -125,10 +145,12 @@ task createChecksums(type: Checksum, dependsOn: [ 'sourcesDistZip', 'sourcesDistTar', 'gossipDistZip', - 'gossipDistTar' + 'gossipDistTar', + 'relayerDistZip', + 'relayerDistTar' ]) { files = distZip.outputs.files + distTar.outputs.files + sourcesDistZip.outputs.files + sourcesDistTar.outputs.files \ - + gossipDistZip.outputs.files + gossipDistTar.outputs.files + + gossipDistZip.outputs.files + gossipDistTar.outputs.files + relayerDistZip.outputs.files + relayerDistTar.outputs.files outputDir = new File(project.buildDir, "distributions") algorithm = Checksum.Algorithm.SHA512 } @@ -137,9 +159,16 @@ build.dependsOn('createChecksums') import com.bmuschko.gradle.docker.tasks.image.DockerBuildImage -task buildImage(type: DockerBuildImage) { +task buildGossipImage(type: DockerBuildImage) { dependsOn gossipDistTar inputDir = projectDir dockerFile = file("docker/gossip.Dockerfile") tag = "apache-tuweni/gossip:$project.version" } + +task buildRelayerImage(type: DockerBuildImage) { + dependsOn relayerDistTar + inputDir = projectDir + dockerFile = file("docker/relayer.Dockerfile") + tag = "apache-tuweni/relayer:$project.version" +} diff --git a/dist/docker/relayer.Dockerfile b/dist/docker/relayer.Dockerfile new file mode 100644 index 0000000..b471169 --- /dev/null +++ b/dist/docker/relayer.Dockerfile @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE +# file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file +# to You 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. + +FROM openjdk:11.0.3-jre-stretch + +COPY build/distributions/tuweni-relayer-*.tgz /usr/relayer.tgz +RUN cd /usr \ + && tar xzf relayer.tgz \ + && mv tuweni-relayer-* relayer + +ENTRYPOINT ["/usr/relayer/bin/relayer"] \ No newline at end of file diff --git a/settings.gradle b/hobbits-relayer/build.gradle similarity index 54% copy from settings.gradle copy to hobbits-relayer/build.gradle index b198613..b27e0e6 100644 --- a/settings.gradle +++ b/hobbits-relayer/build.gradle @@ -1,4 +1,4 @@ -/** +/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the @@ -10,36 +10,26 @@ * 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. */ -rootProject.name='tuweni' -include 'bytes' -include 'concurrent' -include 'concurrent-coroutines' -include 'config' -include 'crypto' -include 'devp2p' -include 'dist' -include 'dns-discovery' -include 'eth' -include 'eth-reference-tests' -include 'eth-repository' -include 'gossip' -include 'hobbits' -include 'io' -include 'junit' -include 'kademlia' -include 'kv' -include 'les' -include 'merkle-trie' -include 'net' -include 'net-coroutines' -include 'plumtree' -include 'progpow' -include 'rlp' -include 'rlpx' -include 'scuttlebutt' -include 'scuttlebutt-discovery' -include 'scuttlebutt-handshake' -include 'scuttlebutt-rpc' -include 'ssz' -include 'toml' -include 'units' +plugins { id 'application' } + +description = 'Hobbits relayer application.' + +dependencies { + compile project(':hobbits') + + compile 'info.picocli:picocli' + compile 'io.vertx:vertx-core' + compile 'org.bouncycastle:bcprov-jdk15on' + compile 'org.logl:logl-api' + compile 'org.logl:logl-logl' + + testCompile project(':junit') + testCompile 'org.bouncycastle:bcprov-jdk15on' + testCompile 'org.junit.jupiter:junit-jupiter-api' + testCompile 'org.junit.jupiter:junit-jupiter-params' + + testRuntime 'org.junit.jupiter:junit-jupiter-engine' +} + + +application { mainClassName = 'org.apache.tuweni.relayer.RelayerApp' } diff --git a/hobbits-relayer/src/main/kotlin/org/apache/tuweni/relayer/RelayerApp.kt b/hobbits-relayer/src/main/kotlin/org/apache/tuweni/relayer/RelayerApp.kt new file mode 100644 index 0000000..201eb12 --- /dev/null +++ b/hobbits-relayer/src/main/kotlin/org/apache/tuweni/relayer/RelayerApp.kt @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.tuweni.relayer + +import io.vertx.core.Vertx +import kotlinx.coroutines.runBlocking +import org.apache.tuweni.hobbits.Relayer +import org.bouncycastle.jce.provider.BouncyCastleProvider +import picocli.CommandLine +import java.security.Security + +internal class RelayerAppCommandlineArguments() { + @CommandLine.Option(names = ["-b", "--bind"], description = ["Endpoint to bind to"]) var bind: String = "" + @CommandLine.Option(names = ["-t", "--to"], description = ["Endpoint to relay to"]) var to: String = "" +} + +/** + * Runs a relayer between two hobbits endpoints. + */ +fun main(args: Array<String>) { + Security.addProvider(BouncyCastleProvider()) + val opts = CommandLine.populateCommand<RelayerAppCommandlineArguments>(RelayerAppCommandlineArguments(), *args) + val vertx = Vertx.vertx() + val relayer = Relayer(vertx, opts.bind, opts.to, { + System.out.println(it) + }) + Runtime.getRuntime().addShutdownHook(Thread { relayer.stop() + vertx.close() }) + runBlocking { + relayer.start() + } +} diff --git a/hobbits-relayer/src/test/kotlin/org/apache/tuweni/relayer/RelayerAppTest.kt b/hobbits-relayer/src/test/kotlin/org/apache/tuweni/relayer/RelayerAppTest.kt new file mode 100644 index 0000000..d7d38a1 --- /dev/null +++ b/hobbits-relayer/src/test/kotlin/org/apache/tuweni/relayer/RelayerAppTest.kt @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.tuweni.relayer + +import io.vertx.core.Vertx +import kotlinx.coroutines.runBlocking +import org.apache.tuweni.bytes.Bytes +import org.apache.tuweni.hobbits.HobbitsTransport +import org.apache.tuweni.hobbits.Message +import org.apache.tuweni.hobbits.Transport +import org.apache.tuweni.junit.VertxExtension +import org.apache.tuweni.junit.VertxInstance +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import java.util.concurrent.atomic.AtomicReference + +@ExtendWith(VertxExtension::class) +class RelayerAppTest { + + @Test + fun testRelayerApp(@VertxInstance vertx: Vertx) { + val ref = AtomicReference<Message>() + val client1 = HobbitsTransport(vertx) + val client2 = HobbitsTransport(vertx) + main(arrayOf("-b", "tcp://localhost:12000", "-t", "tcp://0.0.0.0:10000")) + runBlocking { + client1.createTCPEndpoint("foo", port = 10000, handler = ref::set) + client1.start() + client2.start() + client2.sendMessage( + Message(command = "WHO", body = Bytes.fromHexString("deadbeef"), headers = Bytes.random(16)), + Transport.TCP, + "localhost", + 12000 + ) + } + Thread.sleep(1000) + Assertions.assertEquals(Bytes.fromHexString("deadbeef"), ref.get().body) + client1.stop() + client2.stop() + } +} diff --git a/hobbits/src/main/kotlin/org/apache/tuweni/hobbits/Message.kt b/hobbits/src/main/kotlin/org/apache/tuweni/hobbits/Message.kt index 259da84..eba9eeb 100644 --- a/hobbits/src/main/kotlin/org/apache/tuweni/hobbits/Message.kt +++ b/hobbits/src/main/kotlin/org/apache/tuweni/hobbits/Message.kt @@ -100,4 +100,9 @@ class Message( return protocol.length + 5 + version.length + command.length + headers.size().toString().length + body.size().toString().length + headers.size() + body.size() } + + override fun toString(): String { + val requestLine = "$protocol $version $command ${headers.size()} ${body.size()}\n" + return requestLine + headers.toHexString() + "\n" + body.toHexString() + } } diff --git a/hobbits/src/main/kotlin/org/apache/tuweni/hobbits/Relayer.kt b/hobbits/src/main/kotlin/org/apache/tuweni/hobbits/Relayer.kt new file mode 100644 index 0000000..cd97281 --- /dev/null +++ b/hobbits/src/main/kotlin/org/apache/tuweni/hobbits/Relayer.kt @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.tuweni.hobbits + +import io.vertx.core.Vertx +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import java.net.URI +import kotlin.coroutines.CoroutineContext + +class Relayer( + private val vertx: Vertx, + bind: String, + to: String, + interceptor: (Message) -> Unit, + override val coroutineContext: CoroutineContext = Dispatchers.Default +) : CoroutineScope { + + private val transport = HobbitsTransport(vertx, coroutineContext) + init { + val toURI = URI.create(to) + val uri = URI.create(bind) + when (uri.scheme) { + "http" -> { + transport.createHTTPEndpoint(networkInterface = uri.host, port = uri.port, handler = { + async { + interceptor(it) + transport.sendMessage(it, Transport.HTTP, toURI.host, toURI.port, toURI.path) + } + }) + } + "tcp" -> { + transport.createTCPEndpoint(networkInterface = uri.host, port = uri.port, handler = { + async { + interceptor(it) + transport.sendMessage(it, Transport.TCP, toURI.host, toURI.port, toURI.path) + } + }) + } + "udp" -> { + transport.createUDPEndpoint(networkInterface = uri.host, port = uri.port, handler = { + async { + interceptor(it) + transport.sendMessage(it, Transport.UDP, toURI.host, toURI.port, toURI.path) + } + }) + } + "ws" -> { + transport.createWSEndpoint(networkInterface = uri.host, port = uri.port, handler = { + async { + interceptor(it) + transport.sendMessage(it, Transport.WS, toURI.host, toURI.port, toURI.path) + } + }) + } + } + } + + suspend fun start() { + transport.start() + } + + fun stop() { + transport.stop() + } +} diff --git a/hobbits/src/test/kotlin/org/apache/tuweni/hobbits/InteractionTest.kt b/hobbits/src/test/kotlin/org/apache/tuweni/hobbits/InteractionTest.kt index 49eeb09..5ee5825 100644 --- a/hobbits/src/test/kotlin/org/apache/tuweni/hobbits/InteractionTest.kt +++ b/hobbits/src/test/kotlin/org/apache/tuweni/hobbits/InteractionTest.kt @@ -22,6 +22,7 @@ import org.apache.tuweni.bytes.Bytes import org.apache.tuweni.junit.VertxExtension import org.apache.tuweni.junit.VertxInstance import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import java.util.concurrent.atomic.AtomicReference @@ -51,6 +52,29 @@ class TCPPersistentTest { client2.stop() } + @Disabled + @Test + fun testTwoTCPConnectionsWithTLS(@VertxInstance vertx: Vertx) { + val ref = AtomicReference<Message>() + val client1 = HobbitsTransport(vertx) + val client2 = HobbitsTransport(vertx) + runBlocking { + client1.createTCPEndpoint("foo", port = 10000, handler = ref::set, tls = true) + client1.start() + client2.start() + client2.sendMessage( + Message(command = "WHO", body = Bytes.fromHexString("deadbeef"), headers = Bytes.random(16)), + Transport.TCP, + "0.0.0.0", + 10000 + ) + } + Thread.sleep(200) + assertEquals(Bytes.fromHexString("deadbeef"), ref.get().body) + client1.stop() + client2.stop() + } + @Test fun testTwoEndpoints(@VertxInstance vertx: Vertx) { val ref = AtomicReference<Message>() diff --git a/hobbits/src/test/kotlin/org/apache/tuweni/hobbits/RelayerTest.kt b/hobbits/src/test/kotlin/org/apache/tuweni/hobbits/RelayerTest.kt new file mode 100644 index 0000000..99192e7 --- /dev/null +++ b/hobbits/src/test/kotlin/org/apache/tuweni/hobbits/RelayerTest.kt @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.tuweni.hobbits + +import io.vertx.core.Vertx +import kotlinx.coroutines.runBlocking +import org.apache.tuweni.bytes.Bytes +import org.apache.tuweni.junit.VertxExtension +import org.apache.tuweni.junit.VertxInstance +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import java.util.concurrent.atomic.AtomicReference + +@ExtendWith(VertxExtension::class) +class RelayerTest { + + @Test + fun testTCPRelay(@VertxInstance vertx: Vertx) { + val ref = AtomicReference<Message>() + val client1 = HobbitsTransport(vertx) + val client2 = HobbitsTransport(vertx) + val relayer = Relayer(vertx, "tcp://localhost:22000", "tcp://0.0.0.0:20000", { }) + runBlocking { + client1.createTCPEndpoint("foo", port = 20000, handler = ref::set) + client1.start() + client2.start() + relayer.start() + client2.sendMessage( + Message(command = "WHO", body = Bytes.fromHexString("deadbeef"), headers = Bytes.random(16)), + Transport.TCP, + "localhost", + 22000 + ) + } + Thread.sleep(1000) + Assertions.assertEquals(Bytes.fromHexString("deadbeef"), ref.get().body) + client1.stop() + client2.stop() + relayer.stop() + } + + @Test + fun testHTTPRelay(@VertxInstance vertx: Vertx) { + val ref = AtomicReference<Message>() + val client1 = HobbitsTransport(vertx) + val client2 = HobbitsTransport(vertx) + val relayer = Relayer(vertx, "http://localhost:13000", "http://0.0.0.0:11000", { }) + runBlocking { + client1.createHTTPEndpoint("foo", port = 11000, handler = ref::set) + client1.start() + client2.start() + relayer.start() + client2.sendMessage( + Message(command = "WHO", body = Bytes.fromHexString("deadbeef"), headers = Bytes.random(16)), + Transport.HTTP, + "localhost", + 13000 + ) + } + Thread.sleep(1000) + Assertions.assertEquals(Bytes.fromHexString("deadbeef"), ref.get().body) + client1.stop() + client2.stop() + relayer.stop() + } + + @Test + fun testUDPRelay(@VertxInstance vertx: Vertx) { + val ref = AtomicReference<Message>() + val client1 = HobbitsTransport(vertx) + val client2 = HobbitsTransport(vertx) + val relayer = Relayer(vertx, "udp://localhost:12000", "udp://0.0.0.0:10000", { }) + runBlocking { + client1.createUDPEndpoint("foo", port = 10000, handler = ref::set) + client1.start() + client2.start() + relayer.start() + client2.sendMessage( + Message(command = "WHO", body = Bytes.fromHexString("deadbeef"), headers = Bytes.random(16)), + Transport.UDP, + "localhost", + 12000 + ) + } + Thread.sleep(1000) + Assertions.assertEquals(Bytes.fromHexString("deadbeef"), ref.get().body) + client1.stop() + client2.stop() + relayer.stop() + } + + @Test + fun testWSRelay(@VertxInstance vertx: Vertx) { + val ref = AtomicReference<Message>() + val client1 = HobbitsTransport(vertx) + val client2 = HobbitsTransport(vertx) + val relayer = Relayer(vertx, "ws://localhost:32000", "ws://0.0.0.0:30000", { }) + runBlocking { + client1.createWSEndpoint("foo", port = 30000, handler = ref::set) + client1.start() + client2.start() + relayer.start() + client2.sendMessage( + Message(command = "WHO", body = Bytes.fromHexString("deadbeef"), headers = Bytes.random(16)), + Transport.WS, + "localhost", + 32000 + ) + } + Thread.sleep(1000) + Assertions.assertEquals(Bytes.fromHexString("deadbeef"), ref.get().body) + client1.stop() + client2.stop() + relayer.stop() + } +} diff --git a/settings.gradle b/settings.gradle index b198613..d458825 100644 --- a/settings.gradle +++ b/settings.gradle @@ -24,6 +24,7 @@ include 'eth-reference-tests' include 'eth-repository' include 'gossip' include 'hobbits' +include 'hobbits-relayer' include 'io' include 'junit' include 'kademlia' --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
