Fabiana Cecin 6837ae0c1f
feat: bump nim-libp2p to v2.0.0 (#3929)
* bump nim-libp2p pin to v2.0.0 tag
* bump json_rpc to v0.6.1, lsquic to v0.5.1, boringssl to v0.0.8 (latest tags)
* add libp2p_mix dep; repoint libp2p/protocols/mix -> libp2p_mix
* pin nimble.lock: websock / protobuf_serialization / npeg / jwt
* Makefile: add -d:libp2p_quic_support
* regenerate nix/deps.nix (adds libp2p_mix, refreshes pins)
* migrate rng ref HmacDrbgContext -> libp2p Rng across prod/channels/tests (interface-only; same DRBG)
* waku_switch: TransportConfig factory; unified 2.0.0 connection limits (withMaxInOut, withMaxConnections); local MaxConnections
* waku_relay/rendezvous/discv5/kademlia: v2.0.0 API (rng, config, ServiceDiscovery rename)
* call Service.setup() on post-build switch services (2.0.0 split setup/start)
* drop libp2p/utils/semaphore -> chronos AsyncSemaphore
* add logos_delivery/waku/compat/option_valueor shim (Option[T] valueOr/withValue, dropped upstream)
* add std/options where a transitive re-export was removed
* add newStandardSwitch shim (libp2p removed it in 2.0.0); mounts yamux+mplex to match prod muxer
* PeerId.random(rng); common.rng()/crypto.newRng(); hoist shared rng (instantiation cleanup)
* update expectations for 2.0.0 defaults: DEFAULT_PROTOCOLS += /ipfs/id/push/1.0.0; agent "nim-libp2p"
* drop relay reboot/reconnect test (asserted a Switch restart capability that is simply not supported)
* fix up a few tests that were flaking on MacOS (libp2p upgrade may have exposed these)
2026-06-15 09:56:15 -03:00

126 lines
4.1 KiB
Nim

import logos_delivery/waku/compat/option_valueor
{.push raises: [].}
import
std/[options, strutils, net],
regex,
results,
chronicles,
chronos,
chronos/apps/http/httpserver
type OriginHandlerMiddlewareRef* = ref object of HttpServerMiddlewareRef
allowedOriginMatcher: Option[Regex2]
everyOriginAllowed: bool
proc isEveryOriginAllowed(maybeAllowedOrigin: Option[string]): bool =
return maybeAllowedOrigin.isSome() and maybeAllowedOrigin.get() == "*"
proc compileOriginMatcher(maybeAllowedOrigin: Option[string]): Option[Regex2] =
if maybeAllowedOrigin.isNone():
return none(Regex2)
let allowedOrigin = maybeAllowedOrigin.get()
if (len(allowedOrigin) == 0):
return none(Regex2)
try:
var matchOrigin: string
if allowedOrigin == "*":
matchOrigin = r".*"
return some(re2(matchOrigin, {regexCaseless, regexExtended}))
let allowedOrigins = allowedOrigin.split(",")
var matchExpressions: seq[string] = @[]
var prefix: string
for allowedOrigin in allowedOrigins:
if allowedOrigin.startsWith("http://"):
prefix = r"http:\/\/"
matchOrigin = allowedOrigin.substr(7)
elif allowedOrigin.startsWith("https://"):
prefix = r"https:\/\/"
matchOrigin = allowedOrigin.substr(8)
else:
prefix = r"https?:\/\/"
matchOrigin = allowedOrigin
matchOrigin = matchOrigin.replace(".", r"\.")
matchOrigin = matchOrigin.replace("*", ".*")
matchOrigin = matchOrigin.replace("?", ".?")
matchExpressions.add("^" & prefix & matchOrigin & "$")
let finalExpression = matchExpressions.join("|")
return some(re2(finalExpression, {regexCaseless, regexExtended}))
except RegexError:
var msg = getCurrentExceptionMsg()
error "Failed to compile regex", source = allowedOrigin, err = msg
return none(Regex2)
proc originsMatch(
originHandler: OriginHandlerMiddlewareRef, requestOrigin: string
): bool =
if originHandler.allowedOriginMatcher.isNone():
return false
return requestOrigin.match(originHandler.allowedOriginMatcher.get())
proc originMiddlewareProc(
middleware: HttpServerMiddlewareRef,
reqfence: RequestFence,
nextHandler: HttpProcessCallback2,
): Future[HttpResponseRef] {.async: (raises: [CancelledError]).} =
let request = reqfence.valueOr:
# Ignore request errors that detected before our middleware.
# Let final handler deal with it.
return await nextHandler(reqfence)
let self = OriginHandlerMiddlewareRef(middleware)
var reqHeaders = request.headers
var response = request.getResponse()
if self.allowedOriginMatcher.isSome():
let origin = reqHeaders.getList("Origin")
try:
if origin.len == 1:
if self.everyOriginAllowed:
response.addHeader("Access-Control-Allow-Origin", "*")
response.addHeader("Access-Control-Allow-Headers", "Content-Type")
elif self.originsMatch(origin[0]):
# The Vary: Origin header to must be set to prevent
# potential cache poisoning attacks:
# https://textslashplain.com/2018/08/02/cors-and-vary/
response.addHeader("Vary", "Origin")
response.addHeader("Access-Control-Allow-Origin", origin[0])
response.addHeader("Access-Control-Allow-Headers", "Content-Type")
else:
return await request.respond(Http403, "Origin not allowed")
elif origin.len == 0:
discard
elif origin.len > 1:
return await request.respond(
Http400, "Only a single Origin header must be specified"
)
except HttpWriteError as exc:
# We use default error handler if we unable to send response.
return defaultResponse(exc)
# Calling next handler.
return await nextHandler(reqfence)
proc new*(
t: typedesc[OriginHandlerMiddlewareRef],
allowedOrigin: Option[string] = none(string),
): HttpServerMiddlewareRef =
let middleware = OriginHandlerMiddlewareRef(
allowedOriginMatcher: compileOriginMatcher(allowedOrigin),
everyOriginAllowed: isEveryOriginAllowed(allowedOrigin),
handler: originMiddlewareProc,
)
return HttpServerMiddlewareRef(middleware)