M src/silk.nim => src/silk.nim +3 -1
@@ 10,6 10,7 @@ import ./silk/headers
import ./silk/context
import ./silk/router
import ./silk/middleware
+import ./silk/sugar
export tables.`[]`, tables.`[]=`
export nativesockets.Port
@@ 21,6 22,7 @@ export context
export router
export serverconfig
export middleware
+export sugar
type Server* = ref object
config*: ServerConfig
@@ 79,7 81,7 @@ proc dispatchClient(s: Server, client: AsyncSocket) {.async.} =
# Dispatch context to router to obtain a relevant `Response`.
if not skip:
- await s.router.dispatchRoute(req.path, ctx)
+ await s.router.dispatchRoute(s.config, req.path, ctx)
# Send response through middleware pipeline.
for mw in s.middleware:
M => +2 -1
@@ 22,7 22,8 @@ proc `$`*(h: Response): string =
for k, v in pairs(h.headerFields):
responseHeader.add(k & ": " & v & "\r\n")
responseHeader.add("\r\n" & h.content)
if h.content != "":
responseHeader.add("\r\n" & h.content)
return responseHeader
M src/silk/router.nim => src/silk/router.nim +12 -8
@@ 6,17 6,14 @@ from std/strutils import split, contains
import ./context
import ./headers
import ./middleware
+import ./status
+import ./serverconfig
+import ./sugar
type PathParams = TableRef[string, string]
type RouteHandler* = proc(ctx: Context) {.async.}
type RouterEntryHandle = tuple[handler: RouteHandler, middleware: seq[Middleware]]
-template handler*(code: untyped): untyped =
- proc(ctx{.inject.}: Context) {.async.} = code
-
-template handler*(name: untyped, code: untyped): untyped =
- proc name(ctx{.inject.}: Context) {.async.} = code
-
type Node = ref object
part: string
handle: RouterEntryHandle
@@ 127,12 124,19 @@ proc DELETE*(r: Router, path: string, handler: RouteHandler, middleware: seq[Mid
var newPath = Path(path)
r.registerHandlerRoute("DELETE", newPath, handler, middleware)
+proc defaultHandler(ctx: Context) {.async.} =
+ ctx.noContent(STATUS_NOT_FOUND)
+
type RoutingError = object of CatchableError
-proc dispatchRoute*(r: Router, path: Path, ctx: Context) {.async.} =
+proc dispatchRoute*(r: Router, cfg: ServerConfig, path: Path, ctx: Context) {.async.} =
let searchResults = r.routeTrees[ctx.req.action].search(path, ctx.params)
if searchResults.node == nil or not searchResults.matched:
- raise newException(RoutingError, "Could not match route")
+ if cfg.defaultHandler != nil:
+ await cfg.defaultHandler(ctx)
+ else:
+ raise newException(RoutingError, "Could not match route")
+ return
var skip = false
M src/silk/serverconfig.nim => src/silk/serverconfig.nim +9 -0
@@ 6,6 6,13 @@
from std/math import `^`
from std/nativesockets import Port
+import ./context
+import ./status
+import ./sugar
+
+handler defaultHandler:
+ ctx.noContent(STATUS_NOT_FOUND)
+
type ServerConfig* = object
host*: string
port*: Port
@@ 18,3 25,5 @@ type ServerConfig* = object
# When `true`, keep program alive, and log errors to loggers. If `false`,
# let the error kill the program.
keepAlive* = true
+
+ defaultHandler* = defaultHandler
A src/silk/sugar.nim => src/silk/sugar.nim +11 -0
@@ 0,0 1,11 @@
+import std/asyncdispatch
+
+import ./context
+
+export asyncdispatch
+
+template handler*(code: untyped): untyped =
+ proc(ctx{.inject.}: Context) {.async.} = code
+
+template handler*(name: untyped, code: untyped): untyped =
+ proc name(ctx{.inject.}: Context) {.async.} = code