package router import ( "github.com/kataras/iris/v12/context" ) // ExecutionRules gives control to the execution of the route handlers outside of the handlers themselves. // Usage: // Party#SetExecutionRules(ExecutionRules { // Done: ExecutionOptions{Force: true}, // }) // // See `Party#SetExecutionRules` for more. type ExecutionRules struct { // Begin applies from `Party#Use`/`APIBUilder#UseGlobal` to the first...!last `Party#Handle`'s IF main handlers > 1. Begin ExecutionOptions // Done applies to the latest `Party#Handle`'s (even if one) and all done handlers. Done ExecutionOptions // Main applies to the `Party#Handle`'s all handlers, plays nice with the `Done` rule // when more than one handler was registered in `Party#Handle` without `ctx.Next()` (for Force: true). Main ExecutionOptions } func applyExecutionRules(rules ExecutionRules, begin, done, main *context.Handlers) { if !rules.Begin.Force && !rules.Done.Force && !rules.Main.Force { return // do not proceed and spend build-time here if nothing changed. } beginOK := rules.Begin.apply(begin) mainOK := rules.Main.apply(main) doneOK := rules.Done.apply(done) if !mainOK { mainCp := (*main)[0:] lastIdx := len(mainCp) - 1 if beginOK { if len(mainCp) > 1 { mainCpFirstButNotLast := make(context.Handlers, lastIdx) copy(mainCpFirstButNotLast, mainCp[:lastIdx]) for i, h := range mainCpFirstButNotLast { (*main)[i] = rules.Begin.buildHandler(h) } } } if doneOK { latestMainHandler := mainCp[lastIdx] (*main)[lastIdx] = rules.Done.buildHandler(latestMainHandler) } } } // ExecutionOptions is a set of default behaviors that can be changed in order to customize the execution flow of the routes' handlers with ease. // // See `ExecutionRules` and `Party#SetExecutionRules` for more. type ExecutionOptions struct { // Force if true then the handler9s) will execute even if the previous (or/and current, depends on the type of the rule) // handler does not calling the `ctx.Next()`, // note that the only way remained to stop a next handler is with the `ctx.StopExecution()` if this option is true. // // If true and `ctx.Next()` exists in the handlers that it shouldn't be, the framework will understand it but use it wisely. // // Defaults to false. Force bool } func (e ExecutionOptions) buildHandler(h context.Handler) context.Handler { if !e.Force { return h } return func(ctx *context.Context) { // Proceed will fire the handler and return false here if it doesn't contain a `ctx.Next()`, // so we add the `ctx.Next()` wherever is necessary in order to eliminate any dev's misuse. // // 26 Feb 2022: check if manually stopped, and if it's then don't call ctx.Next. if hasStopped, hasNext := ctx.ProceedAndReportIfStopped(h); !hasStopped && !hasNext { // `ctx.Next()` always checks for `ctx.IsStopped()` and handler(s) positions by-design. ctx.Next() } } } func (e ExecutionOptions) apply(handlers *context.Handlers) bool { if !e.Force { return false } tmp := *handlers for i, h := range tmp { if h == nil { if len(tmp) == 1 { return false } continue } (*handlers)[i] = e.buildHandler(h) } return true }