mirror of
https://github.com/status-im/nim-chronos.git
synced 2026-08-27 13:01:12 +00:00
* Docs: HTTP Server tutorial. * Add docstrings for Fence types. * Docs: Examples: Reorganize HTTP server examples to Nimble projects instead of standalone files. * Docs: Add shiftinclude an admonish preprocessors. * Docs: Add admonish css. * Add API docs building for apps inside chronos. * Docs: HTTP Server: Finalize chapter 1. * Docs: HTTP Server: Finalize intro. * Docs: summary: Fix path to async_procs.md. * Docs: HTTP Server: Finalize chapter 2. * Docs: HTTP Server: Finalize chapter 3. * CI: doc: Install mdbook-shiftinclude. * Docs: HTTP Server: Finalize chapter 4. * Docs: HTTP Server: Finalize chapter 5. * CI: Update required mdbook-open-on-gh version. * Docs: HTTP Server: Fix incorrect info about HttpProcessCallback2 in Chapter 4. * Docs: http server: chapter 3,4: Replace mutating var with in-place try-except. * Docs: http server: chapter 3,4: Remove explicit returns. * Docs: http server: Use valueOr instead of explicit isErr checks. * Docs: http server: Chapter 4: Add more info on middlewares. * Docs: http server: Add info about HTTP protocol. * Update docs/examples/http_server/chapter2/src/dashboard.nim Co-authored-by: Jacek Sieka <jacek@status.im> * Docs: HTTP Server: Remove redundant `return`s. * Add examples tech files to ignore. * Docs: Remove redundant css ref. * Docs: HTTP Server: Replace global threadvar with a TableRef passed to a closure. * Move examples out of docs. Fix #662. * Add example deps files to ignore. --------- Co-authored-by: Jacek Sieka <jacek@status.im>
127 lines
3.9 KiB
Nim
127 lines
3.9 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 handler(
|
|
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, handler, 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()
|