nim-chronos/docs/examples/middleware.nim

131 lines
4.0 KiB
Nim

import chronos/apps/http/httpserver
{.push raises: [].}
proc firstMiddlewareHandler(
middleware: HttpServerMiddlewareRef,
reqfence: RequestFence,
nextHandler: HttpProcessCallback2
): Future[HttpResponseRef] {.async: (raises: [CancelledError]).} =
if reqfence.isErr():
# Ignore request errors
return await nextHandler(reqfence)
let request = reqfence.get()
var headers = request.headers
if request.uri.path.startsWith("/path/to/hidden/resources"):
headers.add("X-Filter", "drop")
elif request.uri.path.startsWith("/path/to/blocked/resources"):
headers.add("X-Filter", "block")
else:
headers.add("X-Filter", "pass")
# Updating request by adding new HTTP header `X-Filter`.
let res = request.updateRequest(headers)
if res.isErr():
# We use default error handler in case of error which will respond with
# proper HTTP status code error.
return defaultResponse(res.error)
# Calling next handler.
await nextHandler(reqfence)
proc secondMiddlewareHandler(
middleware: HttpServerMiddlewareRef,
reqfence: RequestFence,
nextHandler: HttpProcessCallback2
): Future[HttpResponseRef] {.async: (raises: [CancelledError]).} =
if reqfence.isErr():
# Ignore request errors
return await nextHandler(reqfence)
let
request = reqfence.get()
filtered = request.headers.getString("X-Filter", "pass")
if filtered == "drop":
# Force HTTP server to drop connection with remote peer.
dropResponse()
elif filtered == "block":
# Force HTTP server to respond with HTTP `404 Not Found` error code.
codeResponse(Http404)
else:
# Calling next handler.
await nextHandler(reqfence)
proc thirdMiddlewareHandler(
middleware: HttpServerMiddlewareRef,
reqfence: RequestFence,
nextHandler: HttpProcessCallback2
): Future[HttpResponseRef] {.async: (raises: [CancelledError]).} =
if reqfence.isErr():
# Ignore request errors
return await nextHandler(reqfence)
let request = reqfence.get()
echo "QUERY = [", request.rawPath, "]"
echo request.headers
try:
if request.uri.path == "/path/to/plugin/resources/page1":
await request.respond(Http200, "PLUGIN PAGE1")
elif request.uri.path == "/path/to/plugin/resources/page2":
await request.respond(Http200, "PLUGIN PAGE2")
else:
# Calling next handler.
await nextHandler(reqfence)
except HttpWriteError as exc:
# We use default error handler if we unable to send response.
defaultResponse(exc)
proc mainHandler(
reqfence: RequestFence
): Future[HttpResponseRef] {.async: (raises: [CancelledError]).} =
if reqfence.isErr():
return defaultResponse()
let request = reqfence.get()
try:
if request.uri.path == "/path/to/original/page1":
await request.respond(Http200, "ORIGINAL PAGE1")
elif request.uri.path == "/path/to/original/page2":
await request.respond(Http200, "ORIGINAL PAGE2")
else:
# Force HTTP server to respond with `404 Not Found` status code.
codeResponse(Http404)
except HttpWriteError as exc:
defaultResponse(exc)
proc middlewareExample() {.async: (raises: []).} =
let
middlewares = [
HttpServerMiddlewareRef(handler: firstMiddlewareHandler),
HttpServerMiddlewareRef(handler: secondMiddlewareHandler),
HttpServerMiddlewareRef(handler: thirdMiddlewareHandler)
]
socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr}
boundAddress =
if isAvailable(AddressFamily.IPv6):
AnyAddress6
else:
AnyAddress
res = HttpServerRef.new(boundAddress, mainHandler,
socketFlags = socketFlags,
middlewares = middlewares)
doAssert(res.isOk(), "Unable to start HTTP server")
let server = res.get()
server.start()
let address = server.instance.localAddress()
echo "HTTP server running on ", address
try:
await server.join()
except CancelledError:
discard
finally:
await server.stop()
await server.closeWait()
when isMainModule:
waitFor(middlewareExample())