diff --git a/changelog.markdown b/changelog.markdown index b5a4db0..57553f6 100644 --- a/changelog.markdown +++ b/changelog.markdown @@ -4,6 +4,8 @@ - **Breaking change:** By default `redirect` now skips future handlers, including when used in a `before` route. To retain the old behavior, set the parameter `halt=false` (e.g. `redirect("/somewhere", halt=false)`) +- Fix for [#211](https://github.com/dom96/jester/issues/211) - custom routers now have the same error handling as normal routes. + ## 0.4.3 - 12/08/2019 Minor release correcting a few packaging issues and includes some other diff --git a/jester.nim b/jester.nim index 46b32ce..b9d509a 100644 --- a/jester.nim +++ b/jester.nim @@ -42,6 +42,14 @@ type request: Request, error: RouteError ): Future[ResponseData] {.gcsafe, closure.} + MatchPair* = tuple + matcher: MatchProc + errorHandler: ErrorProc + + MatchPairSync* = tuple + matcher: MatchProcSync + errorHandler: ErrorProc + Jester* = object when not useHttpBeast: httpServer*: AsyncHttpServer @@ -449,18 +457,20 @@ proc initJester*( result.errorHandlers = @[] proc initJester*( - matcher: MatchProc, + pair: MatchPair, settings: Settings = newSettings() ): Jester = result = initJester(settings) - result.register(matcher) + result.register(pair.matcher) + result.register(pair.errorHandler) proc initJester*( - matcher: MatchProcSync, # TODO: Annoying nim bug: `MatchProc | MatchProcSync` doesn't work. + pair: MatchPairSync, # TODO: Annoying nim bug: `MatchPair | MatchPairSync` doesn't work. settings: Settings = newSettings() ): Jester = result = initJester(settings) - result.register(matcher) + result.register(pair.matcher) + result.register(pair.errorHandler) proc serve*( self: var Jester @@ -1288,7 +1298,7 @@ proc routesEx(name: string, body: NimNode): NimNode = `afterRoutes` ) - let matchIdent = newIdentNode(name) + let matchIdent = newIdentNode(name & "Matcher") let reqIdent = newIdentNode("request") let needsAsync = needsAsync(body) case needsAsync @@ -1348,6 +1358,26 @@ proc routesEx(name: string, body: NimNode): NimNode = errorHandlerProc[6][0][1][^1][2][1][0] = stmts result.add(errorHandlerProc) + # Pair the matcher and error matcher + let pairIdent = newIdentNode(name) + let matchProcVarIdent = newIdentNode(name & "MatchProc") + let errorProcVarIdent = newIdentNode(name & "ErrorProc") + if needsAsync in {ImplicitTrue, ExplicitTrue}: + # TODO: I don't understand why I have to assign these procs to intermediate + # variables in order to get them into the tuple. It would be nice if it could + # just be: + # let `pairIdent`: MatchPair = (`matchIdent`, `errorHandlerIdent`) + result.add quote do: + let `matchProcVarIdent`: MatchProc = `matchIdent` + let `errorProcVarIdent`: ErrorProc = `errorHandlerIdent` + let `pairIdent`: MatchPair = (`matchProcVarIdent`, `errorProcVarIdent`) + else: + result.add quote do: + let `matchProcVarIdent`: MatchProcSync = `matchIdent` + let `errorProcVarIdent`: ErrorProc = `errorHandlerIdent` + let `pairIdent`: MatchPairSync = (`matchProcVarIdent`, `errorProcVarIdent`) + + # TODO: Replace `body`, `headers`, `code` in routes with `result[i]` to # get these shortcuts back without sacrificing usability. # TODO2: Make sure you replace what `guessAction` used to do for this. @@ -1358,13 +1388,11 @@ proc routesEx(name: string, body: NimNode): NimNode = macro routes*(body: untyped) = result = routesEx("match", body) let jesIdent = genSym(nskVar, "jes") - let matchIdent = newIdentNode("match") - let errorHandlerIdent = newIdentNode("matchErrorHandler") + let pairIdent = newIdentNode("match") let settingsIdent = newIdentNode("settings") result.add( quote do: - var `jesIdent` = initJester(`matchIdent`, `settingsIdent`) - `jesIdent`.register(`errorHandlerIdent`) + var `jesIdent` = initJester(`pairIdent`, `settingsIdent`) ) result.add( quote do: diff --git a/tests/customRouter.nim b/tests/customRouter.nim new file mode 100644 index 0000000..5f4f141 --- /dev/null +++ b/tests/customRouter.nim @@ -0,0 +1,20 @@ +import jester + +router myrouter: + get "/": + resp "Hello world" + + get "/raise": + raise newException(Exception, "Foobar") + + error Exception: + resp Http500, "Something bad happened: " & exception.msg + +when isMainModule: + let s = newSettings( + Port(5454), + bindAddr="127.0.0.1", + ) + var jest = initJester(myrouter, s) + # jest.register(myrouterErrorHandler) + jest.serve() diff --git a/tests/tester.nim b/tests/tester.nim index 03c5c98..f1a0be4 100644 --- a/tests/tester.nim +++ b/tests/tester.nim @@ -235,12 +235,26 @@ proc issue150(useStdLib: bool) = check resp.code == Http500 check (waitFor resp.body).startsWith("Something bad happened") +proc customRouterTest(useStdLib: bool) = + waitFor startServer("customRouter.nim", useStdLib) + var client = newAsyncHttpClient(maxRedirects = 0) + + suite "customRouter useStdLib=" & $useStdLib: + test "error handler": + let resp = waitFor client.get(address & "/raise") + check resp.code == Http500 + let body = (waitFor resp.body) + checkpoint body + check body.startsWith("Something bad happened: Foobar") + when isMainModule: try: allTest(useStdLib=false) # Test HttpBeast. allTest(useStdLib=true) # Test asynchttpserver. issue150(useStdLib=false) issue150(useStdLib=true) + customRouterTest(useStdLib=false) + customRouterTest(useStdLib=true) # Verify that Nim in Action Tweeter still compiles. test "Nim in Action - Tweeter":