From aae54f0cecf6570eab067d63c8cd61b43c4014cf Mon Sep 17 00:00:00 2001 From: bhartnett <51288821+bhartnett@users.noreply.github.com> Date: Thu, 3 Oct 2024 16:00:29 +0800 Subject: [PATCH 1/3] Implement isConnected method. --- json_rpc/client.nim | 3 ++ json_rpc/clients/httpclient.nim | 4 ++ json_rpc/clients/socketclient.nim | 3 ++ json_rpc/clients/websocketclientimpl.nim | 3 ++ tests/testserverclient.nim | 56 +++++++++++++++++++++++- 5 files changed, 67 insertions(+), 2 deletions(-) diff --git a/json_rpc/client.nim b/json_rpc/client.nim index 114a899..ffac138 100644 --- a/json_rpc/client.nim +++ b/json_rpc/client.nim @@ -137,6 +137,9 @@ method call*(client: RpcClient, name: string, await client.call(name, params.paramsTx) +method isConnected*(client: RpcClient): bool {.base, gcsafe.} = + doAssert(false, "`RpcClient.isConnected` not implemented") + method close*(client: RpcClient): Future[void] {.base, gcsafe, async.} = doAssert(false, "`RpcClient.close` not implemented") diff --git a/json_rpc/clients/httpclient.nim b/json_rpc/clients/httpclient.nim index d15a5e5..2cb89a2 100644 --- a/json_rpc/clients/httpclient.nim +++ b/json_rpc/clients/httpclient.nim @@ -220,8 +220,12 @@ proc connect*(client: RpcHttpClient, address: string, port: Port, secure: bool) else: raise newException(RpcAddressUnresolvableError, res.error) +method isConnected*(client: RpcHttpClient): bool = + (not client.httpSession.isNil()) and client.httpAddress.isOk() + method close*(client: RpcHttpClient) {.async.} = if not client.httpSession.isNil: await client.httpSession.closeWait() + client.httpSession = nil {.pop.} diff --git a/json_rpc/clients/socketclient.nim b/json_rpc/clients/socketclient.nim index 67f41ab..fbc6340 100644 --- a/json_rpc/clients/socketclient.nim +++ b/json_rpc/clients/socketclient.nim @@ -130,6 +130,9 @@ proc connect*(client: RpcSocketClient, address: TransportAddress) {.async.} = client.address = address client.loop = processData(client) +method isConnected*(client: RpcSocketClient): bool = + not client.transport.isNil() + method close*(client: RpcSocketClient) {.async.} = await client.loop.cancelAndWait() if not client.transport.isNil: diff --git a/json_rpc/clients/websocketclientimpl.nim b/json_rpc/clients/websocketclientimpl.nim index 59fb07e..e712c7d 100644 --- a/json_rpc/clients/websocketclientimpl.nim +++ b/json_rpc/clients/websocketclientimpl.nim @@ -148,6 +148,9 @@ proc connect*( client.uri = uri client.loop = processData(client) +method isConnected*(client: RpcWebSocketClient): bool = + not client.transport.isNil() + method close*(client: RpcWebSocketClient) {.async.} = await client.loop.cancelAndWait() if not client.transport.isNil: diff --git a/tests/testserverclient.nim b/tests/testserverclient.nim index 052887b..83f0e0b 100644 --- a/tests/testserverclient.nim +++ b/tests/testserverclient.nim @@ -49,9 +49,51 @@ suite "Socket Server/Client RPC": except CatchableError as e: check e.msg == """{"code":-32001,"message":"Unknown payload"}""" + test "Client close and isConnected": + check client.isConnected() == true + # Is socket server close broken? + # waitFor client.close() + # check client.isConnected() == false + srv.stop() waitFor srv.closeWait() +suite "HTTP Server/Client RPC": + var srv = newRpcHttpServer([initTAddress("127.0.0.1", Port(0))]) + var client = newRpcHttpClient() + + echo "address: ", $srv.localAddress() + srv.setupServer() + srv.start() + waitFor client.connect("http://" & $(srv.localAddress()[0])) + + test "Successful RPC call": + let r = waitFor client.call("myProc", %[%"abc", %[1, 2, 3, 4]]) + check r.string == "\"Hello abc data: [1, 2, 3, 4]\"" + + test "Missing params": + expect(CatchableError): + discard waitFor client.call("myProc", %[%"abc"]) + + test "Error RPC call": + expect(CatchableError): # The error type wont be translated + discard waitFor client.call("myError", %[%"abc", %[1, 2, 3, 4]]) + + test "Invalid request exception": + try: + discard waitFor client.call("invalidRequest", %[]) + check false + except CatchableError as e: + check e.msg == """{"code":-32001,"message":"Unknown payload"}""" + + test "Client close and isConnected": + check client.isConnected() == true + waitFor client.close() + check client.isConnected() == false + + waitFor srv.stop() + waitFor srv.closeWait() + suite "Websocket Server/Client RPC": var srv = newRpcWebSocketServer("127.0.0.1", Port(0)) var client = newRpcWebSocketClient() @@ -79,6 +121,11 @@ suite "Websocket Server/Client RPC": except CatchableError as e: check e.msg == """{"code":-32001,"message":"Unknown payload"}""" + test "Client close and isConnected": + check client.isConnected() == true + waitFor client.close() + check client.isConnected() == false + srv.stop() waitFor srv.closeWait() @@ -111,16 +158,21 @@ suite "Websocket Server/Client RPC with Compression": except CatchableError as e: check e.msg == """{"code":-32001,"message":"Unknown payload"}""" + test "Client close and isConnected": + check client.isConnected() == true + waitFor client.close() + check client.isConnected() == false + srv.stop() waitFor srv.closeWait() suite "Custom processClient": test "Should be able to use custom processClient": var wasCalled: bool = false - + proc processClientHook(server: StreamServer, transport: StreamTransport) {.async: (raises: []), gcsafe.} = wasCalled = true - + var srv = newRpcSocketServer(processClientHook) srv.addStreamServer("localhost", Port(8888)) var client = newRpcSocketClient() From 53e9d98dcf05e666514ecf5d292db99f3b605cd3 Mon Sep 17 00:00:00 2001 From: bhartnett <51288821+bhartnett@users.noreply.github.com> Date: Thu, 3 Oct 2024 23:29:40 +0800 Subject: [PATCH 2/3] Fix broken tests. --- tests/testhttp.nim | 2 +- tests/testhttps.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/testhttp.nim b/tests/testhttp.nim index 2bd0c82..183adf1 100644 --- a/tests/testhttp.nim +++ b/tests/testhttp.nim @@ -28,7 +28,7 @@ proc continuousTest(address: string): Future[int] {.async.} = var r = await client.call("myProc", %[%"abc", %[1, 2, 3, i]]) if r.string == "\"Hello abc data: [1, 2, 3, " & $i & "]\"": result += 1 - await client.close() + await client.close() proc invalidTest(address: string): Future[bool] {.async.} = var client = newRpcHttpClient() diff --git a/tests/testhttps.nim b/tests/testhttps.nim index 037050b..93a99c9 100644 --- a/tests/testhttps.nim +++ b/tests/testhttps.nim @@ -88,7 +88,7 @@ proc continuousTest(address: string): Future[int] {.async.} = var r = await client.call("myProc", %[%"abc", %[1, 2, 3, i]]) if r.string == "\"Hello abc data: [1, 2, 3, " & $i & "]\"": result += 1 - await client.close() + await client.close() proc invalidTest(address: string): Future[bool] {.async.} = var client = newRpcHttpClient(secure=true) From d4d970581f2cf69c435d04b2550adb38c7fb3905 Mon Sep 17 00:00:00 2001 From: bhartnett <51288821+bhartnett@users.noreply.github.com> Date: Fri, 4 Oct 2024 16:39:58 +0800 Subject: [PATCH 3/3] Fix socket server close. --- json_rpc/clients/socketclient.nim | 7 ++++++- tests/testserverclient.nim | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/json_rpc/clients/socketclient.nim b/json_rpc/clients/socketclient.nim index fbc6340..71722d0 100644 --- a/json_rpc/clients/socketclient.nim +++ b/json_rpc/clients/socketclient.nim @@ -80,6 +80,7 @@ method callBatch*(client: RpcSocketClient, proc processData(client: RpcSocketClient) {.async: (raises: []).} = while true: var localException: ref JsonRpcError + var stopping = false while true: try: var value = await client.transport.readLine(defaultMaxRequestLength) @@ -99,6 +100,7 @@ proc processData(client: RpcSocketClient) {.async: (raises: []).} = break except CancelledError as exc: localException = newException(JsonRpcError, exc.msg) + stopping = true await client.transport.closeWait() break @@ -108,6 +110,9 @@ proc processData(client: RpcSocketClient) {.async: (raises: []).} = if client.batchFut.isNil.not and not client.batchFut.completed(): client.batchFut.fail(localException) + if stopping: + break + # async loop reconnection and waiting try: info "Reconnect to server", address=`$`(client.address) @@ -136,5 +141,5 @@ method isConnected*(client: RpcSocketClient): bool = method close*(client: RpcSocketClient) {.async.} = await client.loop.cancelAndWait() if not client.transport.isNil: - client.transport.close() + await client.transport.closeWait() client.transport = nil diff --git a/tests/testserverclient.nim b/tests/testserverclient.nim index 83f0e0b..a36cfab 100644 --- a/tests/testserverclient.nim +++ b/tests/testserverclient.nim @@ -52,8 +52,8 @@ suite "Socket Server/Client RPC": test "Client close and isConnected": check client.isConnected() == true # Is socket server close broken? - # waitFor client.close() - # check client.isConnected() == false + waitFor client.close() + check client.isConnected() == false srv.stop() waitFor srv.closeWait()