nim-json-rpc/json_rpc/servers/httpserver.nim

246 lines
8.7 KiB
Nim
Raw Normal View History

import
2021-11-22 10:09:13 -03:00
stew/byteutils,
std/[strutils],
chronicles, httputils, chronos,
chronos/apps/http/[httpserver, shttpserver],
".."/[errors, server]
export server, shttpserver
2018-07-14 10:51:54 +03:00
logScope:
topics = "JSONRPC-HTTP-SERVER"
2018-07-14 10:51:54 +03:00
type
ReqStatus = enum
Success, Error, ErrorFailure
# HttpAuthHook: handle CORS, JWT auth, etc. in HTTP header
# before actual request processed
# return value:
# - nil: auth success, continue execution
# - HttpResponse: could not authenticate, stop execution
# and return the response
HttpAuthHook* = proc(request: HttpRequestRef): Future[HttpResponseRef]
{.gcsafe, raises: [Defect, CatchableError].}
2018-07-14 10:51:54 +03:00
RpcHttpServer* = ref object of RpcServer
2021-11-22 10:09:13 -03:00
httpServers: seq[HttpServerRef]
authHooks: seq[HttpAuthHook]
2021-11-22 10:09:13 -03:00
proc processClientRpc(rpcServer: RpcHttpServer): HttpProcessCallback =
return proc (req: RequestFence): Future[HttpResponseRef] {.async.} =
if req.isOk():
let request = req.get()
# if hook result is not nil,
# it means we should return immediately
for hook in rpcServer.authHooks:
let res = await hook(request)
if not res.isNil:
return res
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")
2018-07-14 10:51:54 +03:00
else:
var data = future.read()
let res = await request.respond(Http200, data)
trace "JSON-RPC result has been sent"
return res
else:
return dumbResponse()
2019-05-14 18:42:51 +03:00
proc addHttpServer*(rpcServer: RpcHttpServer, address: TransportAddress) =
2021-11-22 10:09:13 -03:00
let initialServerCount = len(rpcServer.httpServers)
2018-07-14 10:51:54 +03:00
try:
info "Starting JSON-RPC HTTP server", url = "http://" & $address
2021-11-22 10:09:13 -03:00
var res = HttpServerRef.new(address, processClientRpc(rpcServer))
if res.isOk():
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())
2021-11-22 10:09:13 -03:00
else:
raise newException(RpcBindError, "Unable to create server!")
except CatchableError as exc:
2018-07-14 10:51:54 +03:00
error "Failed to create server", address = $address,
message = exc.msg
2018-07-14 10:51:54 +03:00
2021-11-22 10:09:13 -03:00
if len(rpcServer.httpServers) != initialServerCount + 1:
2018-07-14 10:51:54 +03:00
# Server was not bound, critical error.
raise newException(RpcBindError, "Unable to create server!")
2021-11-22 10:09:13 -03:00
proc addHttpServers*(server: RpcHttpServer,
addresses: openArray[TransportAddress]) =
2018-07-14 10:51:54 +03:00
for item in addresses:
2021-11-22 10:09:13 -03:00
server.addHttpServer(item)
2018-07-14 10:51:54 +03:00
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 =
2018-07-14 10:51:54 +03:00
var
tas4: seq[TransportAddress]
tas6: seq[TransportAddress]
added = 0
# Attempt to resolve `address` for IPv4 address space.
try:
2019-03-25 08:58:35 +02:00
tas4 = resolveTAddress(address, AddressFamily.IPv4)
except CatchableError:
2018-07-14 10:51:54 +03:00
discard
# Attempt to resolve `address` for IPv6 address space.
try:
2019-03-25 08:58:35 +02:00
tas6 = resolveTAddress(address, AddressFamily.IPv6)
except CatchableError:
2018-07-14 10:51:54 +03:00
discard
processResolvedAddresses()
2018-07-14 10:51:54 +03:00
iterator resolvedAddresses(address: string, port: Port): TransportAddress =
2018-07-14 10:51:54 +03:00
var
tas4: seq[TransportAddress]
tas6: seq[TransportAddress]
added = 0
# Attempt to resolve `address` for IPv4 address space.
try:
2019-03-25 08:58:35 +02:00
tas4 = resolveTAddress(address, port, AddressFamily.IPv4)
except CatchableError:
2018-07-14 10:51:54 +03:00
discard
# Attempt to resolve `address` for IPv6 address space.
try:
2019-03-25 08:58:35 +02:00
tas6 = resolveTAddress(address, port, AddressFamily.IPv6)
except CatchableError:
2018-07-14 10:51:54 +03:00
discard
processResolvedAddresses()
2018-07-14 10:51:54 +03:00
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)
2018-07-14 10:51:54 +03:00
2021-11-22 10:09:13 -03:00
if len(server.httpServers) == 0:
2018-07-14 10:51:54 +03:00
# 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, authHooks: seq[HttpAuthHook] = @[]): T =
T(router: RpcRouter.init(), httpServers: @[], authHooks: authHooks)
proc new*(T: type RpcHttpServer, router: RpcRouter, authHooks: seq[HttpAuthHook] = @[]): T =
T(router: router, httpServers: @[], authHooks: authHooks)
proc newRpcHttpServer*(authHooks: seq[HttpAuthHook] = @[]): RpcHttpServer =
RpcHttpServer.new(authHooks)
2018-07-14 10:51:54 +03:00
proc newRpcHttpServer*(router: RpcRouter, authHooks: seq[HttpAuthHook] = @[]): RpcHttpServer =
RpcHttpServer.new(router, authHooks)
proc newRpcHttpServer*(addresses: openArray[TransportAddress], authHooks: seq[HttpAuthHook] = @[]): RpcHttpServer =
2018-07-14 10:51:54 +03:00
## Create new server and assign it to addresses ``addresses``.
result = newRpcHttpServer(authHooks)
2021-11-22 10:09:13 -03:00
result.addHttpServers(addresses)
2018-07-14 10:51:54 +03:00
proc newRpcHttpServer*(addresses: openArray[string], authHooks: seq[HttpAuthHook] = @[]): RpcHttpServer =
2018-07-14 10:51:54 +03:00
## Create new server and assign it to addresses ``addresses``.
result = newRpcHttpServer(authHooks)
2021-11-22 10:09:13 -03:00
result.addHttpServers(addresses)
2018-07-14 10:51:54 +03:00
proc newRpcHttpServer*(addresses: openArray[string], router: RpcRouter, authHooks: seq[HttpAuthHook] = @[]): RpcHttpServer =
## Create new server and assign it to addresses ``addresses``.
result = newRpcHttpServer(router, authHooks)
2021-11-22 16:25:29 +02:00
result.addHttpServers(addresses)
proc newRpcHttpServer*(addresses: openArray[TransportAddress], router: RpcRouter, authHooks: seq[HttpAuthHook] = @[]): RpcHttpServer =
## Create new server and assign it to addresses ``addresses``.
result = newRpcHttpServer(router, authHooks)
2021-11-22 16:25:29 +02:00
result.addHttpServers(addresses)
2018-07-14 10:51:54 +03:00
proc start*(server: RpcHttpServer) =
## Start the RPC server.
2021-11-22 10:09:13 -03:00
for item in server.httpServers:
debug "HTTP RPC server started" # (todo: fix this), address = item
2018-07-14 10:51:54 +03:00
item.start()
2021-11-22 10:09:13 -03:00
proc stop*(server: RpcHttpServer) {.async.} =
2018-07-14 10:51:54 +03:00
## Stop the RPC server.
2021-11-22 10:09:13 -03:00
for item in server.httpServers:
debug "HTTP RPC server stopped" # (todo: fix this), address = item.local
await item.stop()
2018-09-13 18:06:33 +01:00
proc closeWait*(server: RpcHttpServer) {.async.} =
## Cleanup resources of RPC server.
2021-11-22 10:09:13 -03:00
for item in server.httpServers:
2018-09-13 18:06:33 +01:00
await item.closeWait()