mirror of
https://github.com/waku-org/nwaku.git
synced 2025-01-15 09:26:38 +00:00
d832f92a43
* Add allowOrigin configuration for wakunode and WakuRestServer Update nim-presto to the latest master that contains middleware support Rework Rest Server in waku to utilize chronos' and presto's new middleware design and added proper CORS handling. Added cors tests and fixes Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>
202 lines
6.9 KiB
Nim
202 lines
6.9 KiB
Nim
when (NimMajor, NimMinor) < (1, 4):
|
|
{.push raises: [Defect].}
|
|
else:
|
|
{.push raises: [].}
|
|
|
|
import
|
|
stew/results,
|
|
stew/shims/net,
|
|
chronicles,
|
|
chronos,
|
|
chronos/apps/http/httpserver,
|
|
presto,
|
|
presto/middleware,
|
|
presto/servercommon
|
|
|
|
import
|
|
./origin_handler
|
|
|
|
|
|
type
|
|
RestServerResult*[T] = Result[T, string]
|
|
|
|
WakuRestServer* = object of RootObj
|
|
router*: RestRouter
|
|
httpServer*: HttpServerRef
|
|
|
|
WakuRestServerRef* = ref WakuRestServer
|
|
|
|
### Configuration
|
|
|
|
type RestServerConf* = object
|
|
cacheSize*: Natural ## \
|
|
## The maximum number of recently accessed states that are kept in \
|
|
## memory. Speeds up requests obtaining information for consecutive
|
|
## slots or epochs.
|
|
|
|
cacheTtl*: Natural ## \
|
|
## The number of seconds to keep recently accessed states in memory
|
|
|
|
requestTimeout*: Natural ## \
|
|
## The number of seconds to wait until complete REST request will be received
|
|
|
|
maxRequestBodySize*: Natural ## \
|
|
## Maximum size of REST request body (kilobytes)
|
|
|
|
maxRequestHeadersSize*: Natural ## \
|
|
## Maximum size of REST request headers (kilobytes)
|
|
|
|
proc default*(T: type RestServerConf): T =
|
|
RestServerConf(
|
|
cacheSize: 3,
|
|
cacheTtl: 60,
|
|
requestTimeout: 0,
|
|
maxRequestBodySize: 16_384,
|
|
maxRequestHeadersSize: 64
|
|
)
|
|
|
|
|
|
### Initialization
|
|
|
|
proc new*(t: typedesc[WakuRestServerRef],
|
|
router: RestRouter,
|
|
address: TransportAddress,
|
|
serverIdent: string = PrestoIdent,
|
|
serverFlags = {HttpServerFlags.NotifyDisconnect},
|
|
socketFlags: set[ServerFlags] = {ReuseAddr},
|
|
serverUri = Uri(),
|
|
maxConnections: int = -1,
|
|
backlogSize: int = DefaultBacklogSize,
|
|
bufferSize: int = 4096,
|
|
httpHeadersTimeout = 10.seconds,
|
|
maxHeadersSize: int = 8192,
|
|
maxRequestBodySize: int = 1_048_576,
|
|
requestErrorHandler: RestRequestErrorHandler = nil,
|
|
dualstack = DualStackType.Auto,
|
|
allowedOrigin: Option[string] = none(string)
|
|
): RestServerResult[WakuRestServerRef] =
|
|
var server = WakuRestServerRef(router: router)
|
|
|
|
let restMiddleware = RestServerMiddlewareRef.new(router = server.router, errorHandler = requestErrorHandler)
|
|
let originHandlerMiddleware = OriginHandlerMiddlewareRef.new(allowedOrigin)
|
|
|
|
let middlewares = [originHandlerMiddleware,
|
|
restMiddleware]
|
|
|
|
## This must be empty and needed only to confirm original initialization requirements of
|
|
## the RestHttpServer now combining old and new middleware approach.
|
|
proc defaultProcessCallback(rf: RequestFence): Future[HttpResponseRef] {.
|
|
async: (raises: [CancelledError]).} =
|
|
discard
|
|
|
|
|
|
let sres = HttpServerRef.new(address
|
|
, defaultProcessCallback
|
|
, serverFlags
|
|
, socketFlags
|
|
, serverUri
|
|
, serverIdent
|
|
, maxConnections
|
|
, bufferSize
|
|
, backlogSize
|
|
, httpHeadersTimeout
|
|
, maxHeadersSize
|
|
, maxRequestBodySize
|
|
, dualstack = dualstack
|
|
, middlewares = middlewares)
|
|
if sres.isOk():
|
|
server.httpServer = sres.get()
|
|
ok(server)
|
|
else:
|
|
err(sres.error)
|
|
|
|
proc getRouter(): RestRouter =
|
|
# TODO: Review this `validate` method. Check in nim-presto what is this used for.
|
|
proc validate(pattern: string, value: string): int =
|
|
## This is rough validation procedure which should be simple and fast,
|
|
## because it will be used for query routing.
|
|
if pattern.startsWith("{") and pattern.endsWith("}"): 0
|
|
else: 1
|
|
|
|
# disable allowed origin handling by presto, we add our own handling as middleware
|
|
RestRouter.init(validate, allowedOrigin = none(string))
|
|
|
|
proc init*(T: type WakuRestServerRef,
|
|
ip: IpAddress, port: Port,
|
|
allowedOrigin=none(string),
|
|
conf=RestServerConf.default(),
|
|
requestErrorHandler: RestRequestErrorHandler = nil): RestServerResult[T] =
|
|
let address = initTAddress(ip, port)
|
|
let serverFlags = {
|
|
HttpServerFlags.QueryCommaSeparatedArray,
|
|
HttpServerFlags.NotifyDisconnect
|
|
}
|
|
|
|
let
|
|
headersTimeout = if conf.requestTimeout == 0: chronos.InfiniteDuration
|
|
else: seconds(int64(conf.requestTimeout))
|
|
maxHeadersSize = conf.maxRequestHeadersSize * 1024
|
|
maxRequestBodySize = conf.maxRequestBodySize * 1024
|
|
|
|
let router = getRouter()
|
|
|
|
try:
|
|
return WakuRestServerRef.new(
|
|
router,
|
|
address,
|
|
serverFlags = serverFlags,
|
|
httpHeadersTimeout = headersTimeout,
|
|
maxHeadersSize = maxHeadersSize,
|
|
maxRequestBodySize = maxRequestBodySize,
|
|
requestErrorHandler = requestErrorHandler,
|
|
allowedOrigin = allowedOrigin
|
|
)
|
|
except CatchableError:
|
|
return err(getCurrentExceptionMsg())
|
|
|
|
proc newRestHttpServer*(ip: IpAddress, port: Port,
|
|
allowedOrigin=none(string),
|
|
conf=RestServerConf.default(),
|
|
requestErrorHandler: RestRequestErrorHandler = nil):
|
|
RestServerResult[WakuRestServerRef] =
|
|
WakuRestServerRef.init(ip, port, allowedOrigin, conf, requestErrorHandler)
|
|
|
|
proc localAddress*(rs: WakuRestServerRef): TransportAddress =
|
|
## Returns `rs` bound local socket address.
|
|
rs.httpServer.instance.localAddress()
|
|
|
|
proc state*(rs: WakuRestServerRef): RestServerState =
|
|
## Returns current REST server's state.
|
|
case rs.httpServer.state
|
|
of HttpServerState.ServerClosed:
|
|
RestServerState.Closed
|
|
of HttpServerState.ServerStopped:
|
|
RestServerState.Stopped
|
|
of HttpServerState.ServerRunning:
|
|
RestServerState.Running
|
|
|
|
proc start*(rs: WakuRestServerRef) =
|
|
## Starts REST server.
|
|
rs.httpServer.start()
|
|
notice "REST service started", address = $rs.localAddress()
|
|
|
|
proc stop*(rs: WakuRestServerRef) {.async: (raises: []).} =
|
|
## Stop REST server from accepting new connections.
|
|
await rs.httpServer.stop()
|
|
notice "REST service stopped", address = $rs.localAddress()
|
|
|
|
proc drop*(rs: WakuRestServerRef): Future[void] {.
|
|
async: (raw: true, raises: []).} =
|
|
## Drop all pending connections.
|
|
rs.httpServer.drop()
|
|
|
|
proc closeWait*(rs: WakuRestServerRef) {.async: (raises: []).} =
|
|
## Stop REST server and drop all the pending connections.
|
|
await rs.httpServer.closeWait()
|
|
notice "REST service closed", address = $rs.localAddress()
|
|
|
|
proc join*(rs: WakuRestServerRef): Future[void] {.
|
|
async: (raw: true, raises: [CancelledError]).} =
|
|
## Wait until REST server will not be closed.
|
|
rs.httpServer.join()
|