mirror of
https://github.com/logos-storage/nim-json-rpc.git
synced 2026-01-03 14:13:08 +00:00
[FEATURE] Add http json rpc proxy (#105)
This commit is contained in:
parent
809172abe4
commit
147ef3f562
@ -36,7 +36,8 @@ proc newRpcRouter*: RpcRouter {.deprecated.} =
|
||||
proc register*(router: var RpcRouter, path: string, call: RpcProc) =
|
||||
router.procs.add(path, call)
|
||||
|
||||
proc clear*(router: var RpcRouter) = router.procs.clear
|
||||
proc clear*(router: var RpcRouter) =
|
||||
router.procs.clear
|
||||
|
||||
proc hasMethod*(router: RpcRouter, methodName: string): bool = router.procs.hasKey(methodName)
|
||||
|
||||
@ -71,17 +72,19 @@ proc route*(router: RpcRouter, node: JsonNode): Future[StringOfJson] {.async, gc
|
||||
return wrapError(INVALID_REQUEST, "'method' missing or invalid")
|
||||
|
||||
let rpcProc = router.procs.getOrDefault(methodName)
|
||||
let params = node.getOrDefault("params")
|
||||
|
||||
if rpcProc == nil:
|
||||
return wrapError(METHOD_NOT_FOUND, "'" & methodName & "' is not a registered RPC method", id)
|
||||
else:
|
||||
try:
|
||||
let res = await rpcProc(if params == nil: newJArray() else: params)
|
||||
return wrapReply(id, res)
|
||||
|
||||
let params = node.getOrDefault("params")
|
||||
try:
|
||||
let res = await rpcProc(if params == nil: newJArray() else: params)
|
||||
return wrapReply(id, res)
|
||||
except CatchableError as err:
|
||||
debug "Error occurred within RPC", methodName = methodName, err = err.msg
|
||||
return wrapError(
|
||||
SERVER_ERROR, methodName & " raised an exception", id, newJString(err.msg))
|
||||
except CatchableError as err:
|
||||
debug "Error occurred within RPC", methodName = methodName, err = err.msg
|
||||
return wrapError(
|
||||
SERVER_ERROR, methodName & " raised an exception", id, newJString(err.msg))
|
||||
|
||||
proc route*(router: RpcRouter, data: string): Future[string] {.async, gcsafe.} =
|
||||
## Route to RPC from string data. Data is expected to be able to be converted to Json.
|
||||
|
||||
46
json_rpc/rpcproxy.nim
Normal file
46
json_rpc/rpcproxy.nim
Normal file
@ -0,0 +1,46 @@
|
||||
{.push raises: [Defect].}
|
||||
|
||||
import
|
||||
./servers/[httpserver],
|
||||
./clients/[httpclient]
|
||||
|
||||
type RpcHttpProxy* = ref object of RootRef
|
||||
rpcHttpClient*: RpcHttpClient
|
||||
rpcHttpServer*: RpcHttpServer
|
||||
|
||||
proc proxyCall(client: RpcHttpClient, name: string): RpcProc =
|
||||
return proc (params: JsonNode): Future[StringOfJson] {.async.} =
|
||||
let res = await client.call(name, params)
|
||||
return StringOfJson($res)
|
||||
|
||||
proc new*(T: type RpcHttpProxy, listenAddresses: openArray[string]): T {.raises: [Defect, CatchableError].}=
|
||||
let client = newRpcHttpClient()
|
||||
let router = RpcRouter.init()
|
||||
T(rpcHttpClient: client, rpcHttpServer: newRpcHttpServer(listenAddresses, router))
|
||||
|
||||
proc newRpcHttpProxy*(listenAddresses: openArray[string]): RpcHttpProxy {.raises: [Defect, CatchableError].} =
|
||||
RpcHttpProxy.new(listenAddresses)
|
||||
|
||||
proc start*(proxy:RpcHttpProxy, proxyServerUrl: string) {.async.} =
|
||||
proxy.rpcHttpServer.start()
|
||||
await proxy.rpcHttpClient.connect(proxyServerUrl)
|
||||
|
||||
proc start*(proxy:RpcHttpProxy, proxyServerAddress: string, proxyServerPort: Port) {.async.} =
|
||||
proxy.rpcHttpServer.start()
|
||||
await proxy.rpcHttpClient.connect(proxyServerAddress, proxyServerPort)
|
||||
|
||||
template rpc*(server: RpcHttpProxy, path: string, body: untyped): untyped =
|
||||
server.rpcHttpServer.rpc(path, body)
|
||||
|
||||
proc registerProxyMethod*(proxy: var RpcHttpProxy, methodName: string) =
|
||||
try:
|
||||
proxy.rpcHttpServer.register(methodName, proxyCall(proxy.rpcHttpClient, methodName))
|
||||
except CatchableError as err:
|
||||
# Adding proc type to table gives invalid exception tracking, see Nim bug: https://github.com/nim-lang/Nim/issues/18376
|
||||
raiseAssert err.msg
|
||||
|
||||
proc stop*(rpcHttpProxy: RpcHttpProxy) {.raises: [Defect, CatchableError].} =
|
||||
rpcHttpProxy.rpcHttpServer.stop()
|
||||
|
||||
proc closeWait*(rpcHttpProxy: RpcHttpProxy) {.async.} =
|
||||
await rpcHttpProxy.rpcHttpServer.closeWait()
|
||||
@ -299,9 +299,15 @@ proc addStreamServer*(server: RpcHttpServer, address: string, port: Port) =
|
||||
proc new*(T: type RpcHttpServer): T =
|
||||
T(router: RpcRouter.init(), servers: @[])
|
||||
|
||||
proc new*(T: type RpcHttpServer, router: RpcRouter): T =
|
||||
T(router: router, servers: @[])
|
||||
|
||||
proc newRpcHttpServer*(): RpcHttpServer =
|
||||
RpcHttpServer.new()
|
||||
|
||||
proc newRpcHttpServer*(router: RpcRouter): RpcHttpServer =
|
||||
RpcHttpServer.new(router)
|
||||
|
||||
proc newRpcHttpServer*(addresses: openArray[TransportAddress]): RpcHttpServer =
|
||||
## Create new server and assign it to addresses ``addresses``.
|
||||
result = newRpcHttpServer()
|
||||
@ -312,6 +318,11 @@ proc newRpcHttpServer*(addresses: openArray[string]): RpcHttpServer =
|
||||
result = newRpcHttpServer()
|
||||
result.addStreamServers(addresses)
|
||||
|
||||
proc newRpcHttpServer*(addresses: openArray[string], router: RpcRouter): RpcHttpServer =
|
||||
## Create new server and assign it to addresses ``addresses``.
|
||||
result = newRpcHttpServer(router)
|
||||
result.addStreamServers(addresses)
|
||||
|
||||
proc start*(server: RpcHttpServer) =
|
||||
## Start the RPC server.
|
||||
for item in server.servers:
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{. warning[UnusedImport]:off .}
|
||||
|
||||
import
|
||||
testrpcmacro, testserverclient, testethcalls, testhttp
|
||||
testrpcmacro, testserverclient, testethcalls, testhttp, testproxy
|
||||
|
||||
47
tests/testproxy.nim
Normal file
47
tests/testproxy.nim
Normal file
@ -0,0 +1,47 @@
|
||||
import
|
||||
unittest, json, chronicles,
|
||||
../json_rpc/[rpcclient, rpcserver, rpcproxy]
|
||||
|
||||
let srvAddress = "localhost:8545"
|
||||
var srv = newRpcHttpServer([srvAddress])
|
||||
|
||||
let proxySrvAddress = "localhost:8546"
|
||||
var proxy = newRpcHttpProxy([proxySrvAddress])
|
||||
|
||||
var client = newRpcHttpClient()
|
||||
let duplicatedProcedureName = "duplicated"
|
||||
|
||||
# Create RPC on server
|
||||
srv.rpc("myProc") do(input: string, data: array[0..3, int]):
|
||||
return %("Hello " & input & " data: " & $data)
|
||||
|
||||
# Create RPC on proxy server
|
||||
proxy.registerProxyMethod("myProc")
|
||||
|
||||
# Create standard handler on server
|
||||
proxy.rpc("myProc1") do(input: string, data: array[0..3, int]):
|
||||
return %("Hello " & input & " data: " & $data)
|
||||
|
||||
srv.start()
|
||||
waitFor proxy.start("localhost", Port(8545))
|
||||
waitFor client.connect("localhost", Port(8546))
|
||||
|
||||
suite "Proxy RPC":
|
||||
test "Successful RPC call thorugh proxy":
|
||||
let r = waitFor client.call("myProc", %[%"abc", %[1, 2, 3, 4]])
|
||||
check r.getStr == "Hello abc data: [1, 2, 3, 4]"
|
||||
test "Successful RPC call no proxy":
|
||||
let r = waitFor client.call("myProc1", %[%"abc", %[1, 2, 3, 4]])
|
||||
check r.getStr == "Hello abc data: [1, 2, 3, 4]"
|
||||
test "Missing params":
|
||||
expect(CatchableError):
|
||||
discard waitFor client.call("myProc", %[%"abc"])
|
||||
test "Method missing on server and proxy server":
|
||||
expect(CatchableError):
|
||||
discard waitFor client.call("missingMethod", %[%"abc"])
|
||||
|
||||
|
||||
srv.stop()
|
||||
waitFor srv.closeWait()
|
||||
proxy.stop()
|
||||
waitFor proxy.closeWait()
|
||||
Loading…
x
Reference in New Issue
Block a user