nimbus-eth1/nimbus/rpc/cors.nim

89 lines
2.9 KiB
Nim

# Nimbus
# Copyright (c) 2022 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at
# https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at
# https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except
# according to those terms.
import
std/[uri],
chronos,
chronos/apps/http/[httptable, httpserver],
json_rpc/rpcserver,
httputils,
websock/websock as ws,
../config
proc sameOrigin(a, b: Uri): bool =
a.hostname == b.hostname and
a.scheme == b.scheme and
a.port == b.port
proc containsOrigin(list: seq[Uri], origin: Uri): bool =
for x in list:
if x.sameOrigin(origin): return true
const
HookOK = HttpResponseRef(nil)
proc httpCors*(allowedOrigins: seq[Uri]): HttpAuthHook =
proc handler(req: HttpRequestRef): Future[HttpResponseRef] {.async.} =
let origins = req.headers.getList("Origin")
let everyOriginAllowed = allowedOrigins.len == 0
if origins.len > 1:
return await req.respond(Http400,
"Only a single Origin header must be specified")
if origins.len == 0:
# maybe not a CORS request
return HookOK
# this section shared by all http method
let origin = parseUri(origins[0])
let resp = req.getResponse()
if not allowedOrigins.containsOrigin(origin):
return await req.respond(Http403, "Origin not allowed")
if everyOriginAllowed:
resp.addHeader("Access-Control-Allow-Origin", "*")
else:
# The Vary: Origin header to must be set to prevent
# potential cache poisoning attacks:
# https://textslashplain.com/2018/08/02/cors-and-vary/
resp.addHeader("Vary", "Origin")
resp.addHeader("Access-Control-Allow-Origin", origins[0])
if req.meth == MethodOptions:
# Preflight request
let meth = resp.getHeader("Access-Control-Request-Method", "?")
if meth != "?":
# TODO: get actual methods supported by respective server
# e.g. JSON-RPC, GRAPHQL, ENGINE-API
resp.addHeader("Access-Control-Allow-Methods", "GET, POST")
resp.addHeader("Vary", "Access-Control-Request-Method")
let heads = resp.getHeader("Access-Control-Request-Headers", "?")
if heads != "?":
# TODO: get actual headers supported by each server?
resp.addHeader("Access-Control-Allow-Headers", heads)
resp.addHeader("Vary", "Access-Control-Request-Headers")
return await req.respond(Http400)
# other method such as POST or GET will fill
# the rest of response in server
return HookOK
result = HttpAuthHook(handler)
proc wsCors*(allowedOrigins: seq[Uri]): WsAuthHook =
proc handler(req: ws.HttpRequest): Future[bool] {.async.} =
# TODO: implement websock equivalent of
# request.getResponse
return true
result = WsAuthHook(handler)