From c455198d4fd3f56cb064b15f80c3a036907fde37 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 22 Nov 2021 20:24:11 +0200 Subject: [PATCH] Eliminate code duplication in the HTTP/S servers --- json_rpc/clients/httpclient.nim | 1 - json_rpc/servers/httpserver.nim | 143 ++++++++++++++-------- json_rpc/servers/shttpserver.nim | 198 ------------------------------- 3 files changed, 95 insertions(+), 247 deletions(-) delete mode 100644 json_rpc/servers/shttpserver.nim diff --git a/json_rpc/clients/httpclient.nim b/json_rpc/clients/httpclient.nim index e3d8f7e..9ec76b0 100644 --- a/json_rpc/clients/httpclient.nim +++ b/json_rpc/clients/httpclient.nim @@ -117,4 +117,3 @@ proc connect*(client: RpcHttpClient, address: string, port: Port, secure: bool) client.httpAddress = res else: raise newException(RpcAddressUnresolvableError, res.error) - diff --git a/json_rpc/servers/httpserver.nim b/json_rpc/servers/httpserver.nim index 25c2a49..8cbf295 100644 --- a/json_rpc/servers/httpserver.nim +++ b/json_rpc/servers/httpserver.nim @@ -2,7 +2,7 @@ import stew/byteutils, std/[strutils], chronicles, httputils, chronos, - chronos/apps/http/httpserver, + chronos/apps/http/[httpserver, shttpserver], ".."/[errors, server] export server @@ -17,33 +17,58 @@ type RpcHttpServer* = ref object of RpcServer httpServers: seq[HttpServerRef] -proc addHttpServer*(rpcServer: RpcHttpServer, address: TransportAddress) = - proc processClientRpc(rpcServer: RpcHttpServer): HttpProcessCallback {.closure.} = - return proc (req: RequestFence): Future[HttpResponseRef] {.async.} = - if req.isOk(): - let request = req.get() - let body = await request.getBody() +proc processClientRpc(rpcServer: RpcServer): HttpProcessCallback = + return proc (req: RequestFence): Future[HttpResponseRef] {.async.} = + if req.isOk(): + let request = req.get() + let body = await request.getBody() - let future = rpcServer.route(string.fromBytes(body)) - yield future - if future.failed: - debug "Internal error while processing JSON-RPC call" - return await request.respond(Http503, "Internal error while processing JSON-RPC call") - else: - var data = future.read() - let res = await request.respond(Http200, data) - trace "JSON-RPC result has been sent" - return res + let future = rpcServer.route(string.fromBytes(body)) + yield future + if future.failed: + debug "Internal error while processing JSON-RPC call" + return await request.respond(Http503, "Internal error while processing JSON-RPC call") else: - return dumbResponse() + var data = future.read() + let res = await request.respond(Http200, data) + trace "JSON-RPC result has been sent" + return res + else: + return dumbResponse() +proc addHttpServer*(rpcServer: RpcHttpServer, address: TransportAddress) = let initialServerCount = len(rpcServer.httpServers) try: info "Starting JSON-RPC HTTP server", url = "http://" & $address var res = HttpServerRef.new(address, processClientRpc(rpcServer)) if res.isOk(): - let httpServer = res.get() - rpcServer.httpServers.add(httpServer) + rpcServer.httpServers.add(res.get()) + else: + raise newException(RpcBindError, "Unable to create server!") + + except CatchableError as exc: + error "Failed to create server", address = $address, + message = exc.msg + + if len(rpcServer.httpServers) != initialServerCount + 1: + # Server was not bound, critical error. + raise newException(RpcBindError, "Unable to create server!") + +proc addSecureHttpServer*(rpcServer: RpcHttpServer, + address: TransportAddress, + tlsPrivateKey: TLSPrivateKey, + tlsCertificate: TLSCertificate) = + let initialServerCount = len(rpcServer.httpServers) + try: + info "Starting JSON-RPC HTTPS server", url = "https://" & $address + var res = SecureHttpServerRef.new(address, + processClientRpc(rpcServer), + tlsPrivateKey, + tlsCertificate, + serverFlags = {HttpServerFlags.Secure}, + socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}) + if res.isOk(): + rpcServer.httpServers.add(res.get()) else: raise newException(RpcBindError, "Unable to create server!") @@ -60,8 +85,26 @@ proc addHttpServers*(server: RpcHttpServer, for item in addresses: server.addHttpServer(item) -proc addHttpServer*(server: RpcHttpServer, address: string) = - ## Create new server and assign it to addresses ``addresses``. +proc addSecureHttpServers*(server: RpcHttpServer, + addresses: openArray[TransportAddress], + tlsPrivateKey: TLSPrivateKey, + tlsCertificate: TLSCertificate) = + for item in addresses: + server.addSecureHttpServer(item, tlsPrivateKey, tlsCertificate) + +template processResolvedAddresses = + if tas4.len + tas6.len == 0: + # Addresses could not be resolved, critical error. + raise newException(RpcAddressUnresolvableError, "Unable to get address!") + + for r in tas4: + yield r + + if tas4.len == 0: # avoid ipv4 + ipv6 running together + for r in tas6: + yield r + +iterator resolvedAddresses(address: string): TransportAddress = var tas4: seq[TransportAddress] tas6: seq[TransportAddress] @@ -79,23 +122,9 @@ proc addHttpServer*(server: RpcHttpServer, address: string) = except CatchableError: discard - for r in tas4: - server.addHttpServer(r) - added.inc - if added == 0: # avoid ipv4 + ipv6 running together - for r in tas6: - server.addHttpServer(r) - added.inc + processResolvedAddresses() - if added == 0: - # Addresses could not be resolved, critical error. - raise newException(RpcAddressUnresolvableError, "Unable to get address!") - -proc addHttpServers*(server: RpcHttpServer, addresses: openArray[string]) = - for address in addresses: - server.addHttpServer(address) - -proc addHttpServer*(server: RpcHttpServer, address: string, port: Port) = +iterator resolvedAddresses(address: string, port: Port): TransportAddress = var tas4: seq[TransportAddress] tas6: seq[TransportAddress] @@ -113,23 +142,41 @@ proc addHttpServer*(server: RpcHttpServer, address: string, port: Port) = except CatchableError: discard - if len(tas4) == 0 and len(tas6) == 0: - # Address was not resolved, critical error. - raise newException(RpcAddressUnresolvableError, - "Address " & address & " could not be resolved!") + processResolvedAddresses() - for r in tas4: - server.addHttpServer(r) - added.inc - for r in tas6: - server.addHttpServer(r) - added.inc +proc addHttpServer*(server: RpcHttpServer, address: string) = + ## Create new server and assign it to addresses ``addresses``. + for a in resolvedAddresses(address): + server.addHttpServer(a) + +proc addSecureHttpServer*(server: RpcHttpServer, + address: string, + tlsPrivateKey: TLSPrivateKey, + tlsCertificate: TLSCertificate) = + for a in resolvedAddresses(address): + server.addSecureHttpServer(a, tlsPrivateKey, tlsCertificate) + +proc addHttpServers*(server: RpcHttpServer, addresses: openArray[string]) = + for address in addresses: + server.addHttpServer(address) + +proc addHttpServer*(server: RpcHttpServer, address: string, port: Port) = + for a in resolvedAddresses(address, port): + server.addHttpServer(a) if len(server.httpServers) == 0: # Server was not bound, critical error. raise newException(RpcBindError, "Could not setup server on " & address & ":" & $int(port)) +proc addSecureHttpServer*(server: RpcHttpServer, + address: string, + port: Port, + tlsPrivateKey: TLSPrivateKey, + tlsCertificate: TLSCertificate) = + for a in resolvedAddresses(address, port): + server.addSecureHttpServer(a, tlsPrivateKey, tlsCertificate) + proc new*(T: type RpcHttpServer): T = T(router: RpcRouter.init(), httpServers: @[]) diff --git a/json_rpc/servers/shttpserver.nim b/json_rpc/servers/shttpserver.nim deleted file mode 100644 index ab3e7f8..0000000 --- a/json_rpc/servers/shttpserver.nim +++ /dev/null @@ -1,198 +0,0 @@ -import - stew/byteutils, - std/[strutils], - chronicles, httputils, chronos, - chronos/apps/http/shttpserver, - ".."/[errors, server] - -export server - -logScope: - topics = "JSONRPC-HTTPS-SERVER" - -type - ReqStatus = enum - Success, Error, ErrorFailure - - RpcSecureHttpServer* = ref object of RpcServer - secureHttpServers: seq[SecureHttpServerRef] - -proc addSecureHttpServer*(rpcServer: RpcSecureHttpServer, - address: TransportAddress, - tlsPrivateKey: TLSPrivateKey, - tlsCertificate: TLSCertificate - ) = - proc processClientRpc(rpcServer: RpcSecureHttpServer): HttpProcessCallback {.closure.} = - return proc (req: RequestFence): Future[HttpResponseRef] {.async.} = - if req.isOk(): - let request = req.get() - let body = await request.getBody() - - let future = rpcServer.route(string.fromBytes(body)) - yield future - if future.failed: - debug "Internal error while processing JSON-RPC call" - return await request.respond(Http503, "Internal error while processing JSON-RPC call") - else: - var data = future.read() - let res = await request.respond(Http200, data) - trace "JSON-RPC result has been sent" - return res - else: - if req.error.code == Http408: - debug "Timeout error while processing JSON-RPC call" - return dumbResponse() - - let initialServerCount = len(rpcServer.secureHttpServers) - try: - info "Starting JSON-RPC HTTPS server", url = "https://" & $address - var res = SecureHttpServerRef.new(address, - processClientRpc(rpcServer), - tlsPrivateKey, - tlsCertificate, - serverFlags = {Secure}, - socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr} - ) - if res.isOk(): - let secureHttpServer = res.get() - rpcServer.secureHttpServers.add(secureHttpServer) - else: - raise newException(RpcBindError, "Unable to create server!") - - except CatchableError as exc: - error "Failed to create server", address = $address, - message = exc.msg - - if len(rpcServer.secureHttpServers) != initialServerCount + 1: - # Server was not bound, critical error. - raise newException(RpcBindError, "Unable to create server!") - -proc addSecureHttpServers*(server: RpcSecureHttpServer, - addresses: openArray[TransportAddress], - tlsPrivateKey: TLSPrivateKey, - tlsCertificate: TLSCertificate - ) = - for item in addresses: - server.addSecureHttpServer(item, tlsPrivateKey, tlsCertificate) - -proc addSecureHttpServer*(server: RpcSecureHttpServer, - address: string, - tlsPrivateKey: TLSPrivateKey, - tlsCertificate: TLSCertificate - ) = - ## Create new server and assign it to addresses ``addresses``. - var - tas4: seq[TransportAddress] - tas6: seq[TransportAddress] - added = 0 - - # Attempt to resolve `address` for IPv4 address space. - try: - tas4 = resolveTAddress(address, AddressFamily.IPv4) - except CatchableError: - discard - - # Attempt to resolve `address` for IPv6 address space. - try: - tas6 = resolveTAddress(address, AddressFamily.IPv6) - except CatchableError: - discard - - for r in tas4: - server.addSecureHttpServer(r, tlsPrivateKey, tlsCertificate) - added.inc - if added == 0: # avoid ipv4 + ipv6 running together - for r in tas6: - server.addSecureHttpServer(r, tlsPrivateKey, tlsCertificate) - added.inc - - if added == 0: - # Addresses could not be resolved, critical error. - raise newException(RpcAddressUnresolvableError, "Unable to get address!") - -proc addSecureHttpServers*(server: RpcSecureHttpServer, - addresses: openArray[string], - tlsPrivateKey: TLSPrivateKey, - tlsCertificate: TLSCertificate - ) = - for address in addresses: - server.addSecureHttpServer(address, tlsPrivateKey, tlsCertificate) - -proc addSecureHttpServer*(server: RpcSecureHttpServer, - address: string, - port: Port, - tlsPrivateKey: TLSPrivateKey, - tlsCertificate: TLSCertificate - ) = - var - tas4: seq[TransportAddress] - tas6: seq[TransportAddress] - added = 0 - - # Attempt to resolve `address` for IPv4 address space. - try: - tas4 = resolveTAddress(address, port, AddressFamily.IPv4) - except CatchableError: - discard - - # Attempt to resolve `address` for IPv6 address space. - try: - tas6 = resolveTAddress(address, port, AddressFamily.IPv6) - except CatchableError: - discard - - if len(tas4) == 0 and len(tas6) == 0: - # Address was not resolved, critical error. - raise newException(RpcAddressUnresolvableError, - "Address " & address & " could not be resolved!") - - for r in tas4: - server.addSecureHttpServer(r, tlsPrivateKey, tlsCertificate) - added.inc - for r in tas6: - server.addSecureHttpServer(r, tlsPrivateKey, tlsCertificate) - added.inc - - if len(server.secureHttpServers) == 0: - # Server was not bound, critical error. - raise newException(RpcBindError, - "Could not setup server on " & address & ":" & $int(port)) - -proc new*(T: type RpcSecureHttpServer): T = - T(router: RpcRouter.init(), secureHttpServers: @[]) - -proc newRpcSecureHttpServer*(): RpcSecureHttpServer = - RpcSecureHttpServer.new() - -proc newRpcSecureHttpServer*(addresses: openArray[TransportAddress], - tlsPrivateKey: TLSPrivateKey, - tlsCertificate: TLSCertificate - ): RpcSecureHttpServer = - ## Create new server and assign it to addresses ``addresses``. - result = newRpcSecureHttpServer() - result.addSecureHttpServers(addresses, tlsPrivateKey, tlsCertificate) - -proc newRpcSecureHttpServer*(addresses: openArray[string], - tlsPrivateKey: TLSPrivateKey, - tlsCertificate: TLSCertificate - ): RpcSecureHttpServer = - ## Create new server and assign it to addresses ``addresses``. - result = newRpcSecureHttpServer() - result.addSecureHttpServers(addresses, tlsPrivateKey, tlsCertificate) - -proc start*(server: RpcSecureHttpServer) = - ## Start the RPC server. - for item in server.secureHttpServers: - debug "HTTPS RPC server started" # (todo: fix this), address = item - item.start() - -proc stop*(server: RpcSecureHttpServer) {.async.} = - ## Stop the RPC server. - for item in server.secureHttpServers: - debug "HTTPS RPC server stopped" # (todo: fix this), address = item.local - await item.stop() - -proc closeWait*(server: RpcSecureHttpServer) {.async.} = - ## Cleanup resources of RPC server. - for item in server.secureHttpServers: - await item.closeWait() \ No newline at end of file