problem: stuck if upstream returned null
This commit is contained in:
@@ -24,6 +24,7 @@ import io.infinitape.etherjar.rpc.*
|
|||||||
import io.infinitape.etherjar.rpc.json.ResponseJson
|
import io.infinitape.etherjar.rpc.json.ResponseJson
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
|
import reactor.core.publisher.switchIfEmpty
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
|
||||||
open class DirectEthereumApi(
|
open class DirectEthereumApi(
|
||||||
@@ -41,6 +42,10 @@ open class DirectEthereumApi(
|
|||||||
targets.isAllowed(method) -> callUpstream(method, params)
|
targets.isAllowed(method) -> callUpstream(method, params)
|
||||||
else -> Mono.error(RpcException(-32601, "Method not allowed or not found"))
|
else -> Mono.error(RpcException(-32601, "Method not allowed or not found"))
|
||||||
}
|
}
|
||||||
|
return processResult(id, method, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun processResult(id: Int, method: String, result: Mono<out Any>): Mono<ByteArray> {
|
||||||
return result
|
return result
|
||||||
.doOnError { t ->
|
.doOnError { t ->
|
||||||
log.warn("Upstream error: [${t.message}] for $method")
|
log.warn("Upstream error: [${t.message}] for $method")
|
||||||
@@ -49,7 +54,18 @@ open class DirectEthereumApi(
|
|||||||
val resp = ResponseJson<Any, Int>()
|
val resp = ResponseJson<Any, Int>()
|
||||||
resp.id = id
|
resp.id = id
|
||||||
resp.result = it
|
resp.result = it
|
||||||
objectMapper.writer().writeValueAsBytes(resp)
|
resp
|
||||||
|
}
|
||||||
|
.switchIfEmpty(
|
||||||
|
Mono.fromCallable {
|
||||||
|
val resp = ResponseJson<Any, Int>()
|
||||||
|
resp.id = id
|
||||||
|
resp.result = null
|
||||||
|
resp
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.map {
|
||||||
|
objectMapper.writer().writeValueAsBytes(it)
|
||||||
}
|
}
|
||||||
.onErrorResume(StatusRuntimeException::class.java) { t ->
|
.onErrorResume(StatusRuntimeException::class.java) { t ->
|
||||||
if (t.status.code == Status.Code.CANCELLED) {
|
if (t.status.code == Status.Code.CANCELLED) {
|
||||||
|
|||||||
@@ -60,6 +60,14 @@ class TestingCommons {
|
|||||||
return new EthereumUpstreamMock(Chain.ETHEREUM, api)
|
return new EthereumUpstreamMock(Chain.ETHEREUM, api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static EthereumUpstreamMock upstream(DirectEthereumApi api, String method) {
|
||||||
|
return upstream(api, [method])
|
||||||
|
}
|
||||||
|
|
||||||
|
static EthereumUpstreamMock upstream(DirectEthereumApi api, List<String> methods) {
|
||||||
|
return new EthereumUpstreamMock(Chain.ETHEREUM, api, new DirectCallMethods(methods))
|
||||||
|
}
|
||||||
|
|
||||||
static AggregatedUpstream aggregatedUpstream(DirectEthereumApi api) {
|
static AggregatedUpstream aggregatedUpstream(DirectEthereumApi api) {
|
||||||
return aggregatedUpstream(upstream(api))
|
return aggregatedUpstream(upstream(api))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2019 ETCDEV GmbH
|
||||||
|
*
|
||||||
|
* 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 io.emeraldpay.dshackle.upstream.ethereum
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.test.TestingCommons
|
||||||
|
import io.emeraldpay.dshackle.upstream.DirectCallMethods
|
||||||
|
import io.infinitape.etherjar.rpc.ReactorRpcClient
|
||||||
|
import io.infinitape.etherjar.rpc.RpcException
|
||||||
|
import io.infinitape.etherjar.rpc.RpcResponseError
|
||||||
|
import reactor.core.publisher.Mono
|
||||||
|
import reactor.test.StepVerifier
|
||||||
|
import spock.lang.Specification
|
||||||
|
|
||||||
|
import java.time.Duration
|
||||||
|
|
||||||
|
class DirectEthereumApiSpec extends Specification {
|
||||||
|
|
||||||
|
DirectEthereumApi api = new DirectEthereumApi(Stub(ReactorRpcClient), TestingCommons.objectMapper(), new DirectCallMethods())
|
||||||
|
|
||||||
|
def "Process successful result"() {
|
||||||
|
setup:
|
||||||
|
def result = Mono.just("hello")
|
||||||
|
when:
|
||||||
|
def act = api.processResult(1, "eth_test", result)
|
||||||
|
.map { new String(it) }
|
||||||
|
|
||||||
|
then:
|
||||||
|
StepVerifier.create(act)
|
||||||
|
.expectNext('{"jsonrpc":"2.0","id":1,"result":"hello"}')
|
||||||
|
.expectComplete()
|
||||||
|
.verify(Duration.ofSeconds(1))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def "Process empty result"() {
|
||||||
|
setup:
|
||||||
|
def result = Mono.empty()
|
||||||
|
when:
|
||||||
|
def act = api.processResult(1, "eth_test", result)
|
||||||
|
.map { new String(it) }
|
||||||
|
|
||||||
|
then:
|
||||||
|
StepVerifier.create(act)
|
||||||
|
.expectNext('{"jsonrpc":"2.0","id":1,"result":null}')
|
||||||
|
.expectComplete()
|
||||||
|
.verify(Duration.ofSeconds(1))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def "Process standard RPC error"() {
|
||||||
|
setup:
|
||||||
|
def result = Mono.error(new RpcException(RpcResponseError.CODE_METHOD_NOT_EXIST, "Test Error", Map.of("foo", "bar")))
|
||||||
|
when:
|
||||||
|
def act = api.processResult(1, "eth_test", result)
|
||||||
|
.map { new String(it) }
|
||||||
|
|
||||||
|
then:
|
||||||
|
StepVerifier.create(act)
|
||||||
|
.expectNext('{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"Test Error","data":{"foo":"bar"}}}')
|
||||||
|
.expectComplete()
|
||||||
|
.verify(Duration.ofSeconds(1))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def "Process internal exception"() {
|
||||||
|
setup:
|
||||||
|
def result = Mono.error(new InterruptedException("test"))
|
||||||
|
when:
|
||||||
|
def act = api.processResult(1, "eth_test", result)
|
||||||
|
.map { new String(it) }
|
||||||
|
|
||||||
|
then:
|
||||||
|
StepVerifier.create(act)
|
||||||
|
.expectNext('{"jsonrpc":"2.0","id":1,"error":{"code":-32020,"message":"Error reading from upstream"}}')
|
||||||
|
.expectComplete()
|
||||||
|
.verify(Duration.ofSeconds(1))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user