From 9a34452e235806f97387dd0c5711b82a24d47ba0 Mon Sep 17 00:00:00 2001 From: jangko Date: Thu, 25 Jan 2024 19:41:56 +0700 Subject: [PATCH] Refactoring: extract rpc handler from HTTP and WebSocket server --- json_rpc/servers/httpserver.nim | 96 ++++++++++++++++------------ json_rpc/servers/websocketserver.nim | 57 ++++++++++------- 2 files changed, 87 insertions(+), 66 deletions(-) diff --git a/json_rpc/servers/httpserver.nim b/json_rpc/servers/httpserver.nim index 114fa71..5e9ff25 100644 --- a/json_rpc/servers/httpserver.nim +++ b/json_rpc/servers/httpserver.nim @@ -34,13 +34,63 @@ type HttpAuthHook* = proc(request: HttpRequestRef): Future[HttpResponseRef] {.gcsafe, raises: [Defect, CatchableError].} - RpcHttpServer* = ref object of RpcServer + # This inheritance arrangement is useful for + # e.g. combo HTTP server + RpcHttpHandler* = ref object of RpcServer + maxChunkSize*: int + + RpcHttpServer* = ref object of RpcHttpHandler httpServers: seq[HttpServerRef] authHooks: seq[HttpAuthHook] - maxChunkSize: int + +proc serveHTTP*(rpcServer: RpcHttpHandler, request: HttpRequestRef): + Future[HttpResponseRef] {.async: (raises: [CancelledError]).} = + let + headers = HttpTable.init([("Content-Type", + "application/json; charset=utf-8")]) + chunkSize = rpcServer.maxChunkSize + + try: + let + body = await request.getBody() + data = await rpcServer.route(string.fromBytes(body)) + + if data.len <= chunkSize: + let res = await request.respond(Http200, data, headers) + trace "JSON-RPC result has been sent" + return res + + let response = request.getResponse() + response.status = Http200 + response.addHeader("Content-Type", "application/json; charset=utf-8") + + await response.prepare() + let maxLen = data.len + + var len = data.len + while len > chunkSize: + await response.sendChunk(data[maxLen - len].unsafeAddr, chunkSize) + len -= chunkSize + + if len > 0: + await response.sendChunk(data[maxLen - len].unsafeAddr, len) + + await response.finish() + except CancelledError as exc: + raise exc + except CatchableError as exc: + debug "Internal error while processing JSON-RPC call" + try: + return await request.respond( + Http503, + "Internal error while processing JSON-RPC call: " & exc.msg) + except HttpWriteError as exc: + error "Something error", msg=exc.msg + return defaultResponse() proc processClientRpc(rpcServer: RpcHttpServer): HttpProcessCallback2 = - return proc (req: RequestFence): Future[HttpResponseRef] {.async: (raises: [CancelledError]).} = + return proc (req: RequestFence): Future[HttpResponseRef] + {.async: (raises: [CancelledError]).} = if not req.isOk(): return defaultResponse() @@ -62,45 +112,7 @@ proc processClientRpc(rpcServer: RpcHttpServer): HttpProcessCallback2 = error "Something error", msg=exc.msg return defaultResponse() - let - headers = HttpTable.init([("Content-Type", - "application/json; charset=utf-8")]) - chunkSize = rpcServer.maxChunkSize - - try: - let - body = await request.getBody() - data = await rpcServer.route(string.fromBytes(body)) - - if data.len <= chunkSize: - let res = await request.respond(Http200, data, headers) - trace "JSON-RPC result has been sent" - return res - - let response = request.getResponse() - response.status = Http200 - response.addHeader("Content-Type", "application/json; charset=utf-8") - await response.prepare() - let maxLen = data.len - var len = data.len - while len > chunkSize: - await response.sendChunk(data[maxLen - len].unsafeAddr, chunkSize) - len -= chunkSize - if len > 0: - await response.sendChunk(data[maxLen - len].unsafeAddr, len) - await response.finish() - - except CancelledError as exc: - raise exc - except CatchableError as exc: - debug "Internal error while processing JSON-RPC call" - try: - return await request.respond( - Http503, - "Internal error while processing JSON-RPC call: " & exc.msg) - except HttpWriteError as exc: - error "Something error", msg=exc.msg - return defaultResponse() + return await rpcServer.serveHTTP(request) proc addHttpServer*( rpcServer: RpcHttpServer, diff --git a/json_rpc/servers/websocketserver.nim b/json_rpc/servers/websocketserver.nim index e61e3a9..7005cc9 100644 --- a/json_rpc/servers/websocketserver.nim +++ b/json_rpc/servers/websocketserver.nim @@ -28,33 +28,17 @@ type WsAuthHook* = proc(request: HttpRequest): Future[bool] {.gcsafe, raises: [Defect, CatchableError].} - RpcWebSocketServer* = ref object of RpcServer + # This inheritance arrangement is useful for + # e.g. combo HTTP server + RpcWebSocketHandler* = ref object of RpcServer + wsserver*: WSServer + + RpcWebSocketServer* = ref object of RpcWebSocketHandler server: StreamServer - wsserver: WSServer authHooks: seq[WsAuthHook] -proc handleRequest(rpc: RpcWebSocketServer, request: HttpRequest) {.async: (raises: [CancelledError]).} = - trace "Handling request:", uri = request.uri.path - trace "Initiating web socket connection." - - # if hook result is false, - # it means we should return immediately - try: - for hook in rpc.authHooks: - let res = await hook(request) - if not res: - return - except CatchableError as exc: - error "Internal error while processing JSON-RPC hook", msg=exc.msg - try: - await request.sendResponse(Http503, - data = "", - content = "Internal error, processing JSON-RPC hook: " & exc.msg) - return - except CatchableError as exc: - error "Something error", msg=exc.msg - return - +proc serveHTTP*(rpc: RpcWebSocketHandler, request: HttpRequest) + {.async: (raises: [CancelledError]).} = try: let server = rpc.wsserver let ws = await server.handleRequest(request) @@ -99,6 +83,31 @@ proc handleRequest(rpc: RpcWebSocketServer, request: HttpRequest) {.async: (rais except CatchableError as exc: error "Something error", msg=exc.msg +proc handleRequest(rpc: RpcWebSocketServer, request: HttpRequest) + {.async: (raises: [CancelledError]).} = + trace "Handling request:", uri = request.uri.path + trace "Initiating web socket connection." + + # if hook result is false, + # it means we should return immediately + try: + for hook in rpc.authHooks: + let res = await hook(request) + if not res: + return + except CatchableError as exc: + error "Internal error while processing JSON-RPC hook", msg=exc.msg + try: + await request.sendResponse(Http503, + data = "", + content = "Internal error, processing JSON-RPC hook: " & exc.msg) + return + except CatchableError as exc: + error "Something error", msg=exc.msg + return + + await rpc.serveHTTP(request) + proc initWebsocket(rpc: RpcWebSocketServer, compression: bool, authHooks: seq[WsAuthHook], rng: ref HmacDrbgContext) =