handler.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. package context
  2. import (
  3. "os"
  4. "path/filepath"
  5. "reflect"
  6. "regexp"
  7. "runtime"
  8. "strings"
  9. "sync"
  10. )
  11. var (
  12. // PackageName is the Iris Go module package name.
  13. PackageName = strings.TrimSuffix(reflect.TypeOf(Handlers{}).PkgPath(), "/context")
  14. // WorkingDir is the (initial) current directory.
  15. WorkingDir, _ = os.Getwd()
  16. )
  17. var (
  18. handlerNames = make(map[*nameExpr]string)
  19. handlerNamesMu sync.RWMutex
  20. )
  21. // SetHandlerName sets a handler name that could be
  22. // fetched through `HandlerName`. The "original" should be
  23. // the Go's original regexp-featured (can be retrieved through a `HandlerName` call) function name.
  24. // The "replacement" should be the custom, human-text of that function name.
  25. //
  26. // If the name starts with "iris" then it replaces that string with the
  27. // full Iris module package name,
  28. // e.g. iris/middleware/logger.(*requestLoggerMiddleware).ServeHTTP-fm to
  29. // github.com/kataras/iris/middleware/logger.(*requestLoggerMiddleware).ServeHTTP-fm
  30. // for convenient between Iris versions.
  31. func SetHandlerName(original string, replacement string) {
  32. if strings.HasPrefix(original, "iris") {
  33. original = PackageName + strings.TrimPrefix(original, "iris")
  34. }
  35. handlerNamesMu.Lock()
  36. // If regexp syntax is wrong
  37. // then its `MatchString` will compare through literal. Fixes an issue
  38. // when a handler name is declared as it's and cause regex parsing expression error,
  39. // e.g. `iris/cache/client.(*Handler).ServeHTTP-fm`
  40. regex, _ := regexp.Compile(original)
  41. handlerNames[&nameExpr{
  42. literal: original,
  43. regex: regex,
  44. }] = replacement
  45. handlerNamesMu.Unlock()
  46. }
  47. type nameExpr struct {
  48. regex *regexp.Regexp
  49. literal string
  50. }
  51. func (expr *nameExpr) MatchString(s string) bool {
  52. if expr.literal == s { // if matches as string, as it's.
  53. return true
  54. }
  55. if expr.regex != nil {
  56. return expr.regex.MatchString(s)
  57. }
  58. return false
  59. }
  60. // A Handler responds to an HTTP request.
  61. // It writes reply headers and data to the Context.ResponseWriter() and then return.
  62. // Returning signals that the request is finished;
  63. // it is not valid to use the Context after or concurrently with the completion of the Handler call.
  64. //
  65. // Depending on the HTTP client software, HTTP protocol version,
  66. // and any intermediaries between the client and the iris server,
  67. // it may not be possible to read from the Context.Request().Body after writing to the Context.ResponseWriter().
  68. // Cautious handlers should read the Context.Request().Body first, and then reply.
  69. //
  70. // Except for reading the body, handlers should not modify the provided Context.
  71. //
  72. // If Handler panics, the server (the caller of Handler) assumes that the effect of the panic was isolated to the active request.
  73. // It recovers the panic, logs a stack trace to the server error log, and hangs up the connection.
  74. type Handler func(*Context)
  75. // Handlers is just a type of slice of []Handler.
  76. //
  77. // See `Handler` for more.
  78. type Handlers []Handler
  79. func valueOf(v interface{}) reflect.Value {
  80. if val, ok := v.(reflect.Value); ok {
  81. return val
  82. }
  83. return reflect.ValueOf(v)
  84. }
  85. // HandlerName returns the handler's function name.
  86. // See `Context.HandlerName` method to get function name of the current running handler in the chain.
  87. // See `SetHandlerName` too.
  88. func HandlerName(h interface{}) string {
  89. pc := valueOf(h).Pointer()
  90. name := runtime.FuncForPC(pc).Name()
  91. handlerNamesMu.RLock()
  92. for expr, newName := range handlerNames {
  93. if expr.MatchString(name) {
  94. name = newName
  95. break
  96. }
  97. }
  98. handlerNamesMu.RUnlock()
  99. return trimHandlerName(name)
  100. }
  101. // HandlersNames returns a slice of "handlers" names
  102. // separated by commas. Can be used for debugging
  103. // or to determinate if end-developer
  104. // called the same exactly Use/UseRouter/Done... API methods
  105. // so framework can give a warning.
  106. func HandlersNames(handlers ...interface{}) string {
  107. if len(handlers) == 1 {
  108. if hs, ok := handlers[0].(Handlers); ok {
  109. asInterfaces := make([]interface{}, 0, len(hs))
  110. for _, h := range hs {
  111. asInterfaces = append(asInterfaces, h)
  112. }
  113. return HandlersNames(asInterfaces...)
  114. }
  115. }
  116. names := make([]string, 0, len(handlers))
  117. for _, h := range handlers {
  118. names = append(names, HandlerName(h))
  119. }
  120. return strings.Join(names, ",")
  121. }
  122. // HandlerFileLine returns the handler's file and line information.
  123. // See `context.HandlerFileLine` to get the file, line of the current running handler in the chain.
  124. func HandlerFileLine(h interface{}) (file string, line int) {
  125. pc := valueOf(h).Pointer()
  126. return runtime.FuncForPC(pc).FileLine(pc)
  127. }
  128. // HandlerFileLineRel same as `HandlerFileLine` but it returns the path
  129. // corresponding to its relative based on the package-level "WorkingDir" variable.
  130. func HandlerFileLineRel(h interface{}) (file string, line int) {
  131. file, line = HandlerFileLine(h)
  132. if relFile, err := filepath.Rel(WorkingDir, file); err == nil {
  133. if !strings.HasPrefix(relFile, "..") {
  134. // Only if it's relative to this path, not parent.
  135. file = "./" + relFile
  136. }
  137. }
  138. return
  139. }
  140. // MainHandlerName tries to find the main handler that end-developer
  141. // registered on the provided chain of handlers and returns its function name.
  142. func MainHandlerName(handlers ...interface{}) (name string, index int) {
  143. if len(handlers) == 0 {
  144. return
  145. }
  146. if hs, ok := handlers[0].(Handlers); ok {
  147. tmp := make([]interface{}, 0, len(hs))
  148. for _, h := range hs {
  149. tmp = append(tmp, h)
  150. }
  151. return MainHandlerName(tmp...)
  152. }
  153. for i := 0; i < len(handlers); i++ {
  154. name = HandlerName(handlers[i])
  155. if name == "" {
  156. continue
  157. }
  158. index = i
  159. if !ingoreMainHandlerName(name) {
  160. break
  161. }
  162. }
  163. return
  164. }
  165. func trimHandlerName(name string) string {
  166. // trim the path for Iris' internal middlewares, e.g.
  167. // irs/mvc.GRPC.Apply.func1
  168. if internalName := PackageName; strings.HasPrefix(name, internalName) {
  169. name = strings.Replace(name, internalName, "iris", 1)
  170. }
  171. if internalName := "github.com/iris-contrib"; strings.HasPrefix(name, internalName) {
  172. name = strings.Replace(name, internalName, "iris-contrib", 1)
  173. }
  174. name = strings.TrimSuffix(name, ".func1")
  175. return name
  176. }
  177. var ignoreHandlerNames = [...]string{
  178. "iris/macro/handler.MakeHandler",
  179. "iris/hero.makeHandler.func2",
  180. "iris/core/router.ExecutionOptions.buildHandler",
  181. "iris/core/router.(*APIBuilder).Favicon",
  182. "iris/core/router.StripPrefix",
  183. }
  184. // IgnoreHandlerName compares a static slice of Iris builtin
  185. // internal methods that should be ignored from trace.
  186. // Some internal methods are kept out of this list for actual debugging.
  187. func IgnoreHandlerName(name string) bool {
  188. for _, ignore := range ignoreHandlerNames {
  189. if name == ignore {
  190. return true
  191. }
  192. }
  193. return false
  194. }
  195. var ignoreMainHandlerNames = [...]string{
  196. "iris.cache",
  197. "iris.basicauth",
  198. "iris.hCaptcha",
  199. "iris.reCAPTCHA",
  200. "iris.profiling",
  201. "iris.recover",
  202. }
  203. // ingoreMainHandlerName reports whether a main handler of "name" should
  204. // be ignored and continue to match the next.
  205. // The ignored main handler names are literals and respects the `ignoreNameHandlers` too.
  206. func ingoreMainHandlerName(name string) bool {
  207. if IgnoreHandlerName(name) {
  208. // If ignored at all, it can't be the main.
  209. return true
  210. }
  211. for _, ignore := range ignoreMainHandlerNames {
  212. if name == ignore {
  213. return true
  214. }
  215. }
  216. return false
  217. }
  218. // Filter is just a type of func(Handler) bool which reports whether an action must be performed
  219. // based on the incoming request.
  220. //
  221. // See `NewConditionalHandler` for more.
  222. type Filter func(*Context) bool
  223. // NewConditionalHandler returns a single Handler which can be registered
  224. // as a middleware.
  225. // Filter is just a type of Handler which returns a boolean.
  226. // Handlers here should act like middleware, they should contain `ctx.Next` to proceed
  227. // to the next handler of the chain. Those "handlers" are registered to the per-request context.
  228. //
  229. //
  230. // It checks the "filter" and if passed then
  231. // it, correctly, executes the "handlers".
  232. //
  233. // If passed, this function makes sure that the Context's information
  234. // about its per-request handler chain based on the new "handlers" is always updated.
  235. //
  236. // If not passed, then simply the Next handler(if any) is executed and "handlers" are ignored.
  237. //
  238. // Example can be found at: _examples/routing/conditional-chain.
  239. func NewConditionalHandler(filter Filter, handlers ...Handler) Handler {
  240. return func(ctx *Context) {
  241. if filter(ctx) {
  242. // Note that we don't want just to fire the incoming handlers, we must make sure
  243. // that it won't break any further handler chain
  244. // information that may be required for the next handlers.
  245. //
  246. // The below code makes sure that this conditional handler does not break
  247. // the ability that iris provides to its end-devs
  248. // to check and modify the per-request handlers chain at runtime.
  249. currIdx := ctx.HandlerIndex(-1)
  250. currHandlers := ctx.Handlers()
  251. if currIdx == len(currHandlers)-1 {
  252. // if this is the last handler of the chain
  253. // just add to the last the new handlers and call Next to fire those.
  254. ctx.AddHandler(handlers...)
  255. ctx.Next()
  256. return
  257. }
  258. // otherwise insert the new handlers in the middle of the current executed chain and the next chain.
  259. newHandlers := append(currHandlers[:currIdx+1], append(handlers, currHandlers[currIdx+1:]...)...)
  260. ctx.SetHandlers(newHandlers)
  261. ctx.Next()
  262. return
  263. }
  264. // if not pass, then just execute the next.
  265. ctx.Next()
  266. }
  267. }
  268. // JoinHandlers returns a copy of "h1" and "h2" Handlers slice joined as one slice of Handlers.
  269. func JoinHandlers(h1 Handlers, h2 Handlers) Handlers {
  270. if len(h1) == 0 {
  271. return h2
  272. }
  273. if len(h2) == 0 {
  274. return h1
  275. }
  276. nowLen := len(h1)
  277. totalLen := nowLen + len(h2)
  278. // create a new slice of Handlers in order to merge the "h1" and "h2"
  279. newHandlers := make(Handlers, totalLen)
  280. // copy the already Handlers to the just created
  281. copy(newHandlers, h1)
  282. // start from there we finish, and store the new Handlers too
  283. copy(newHandlers[nowLen:], h2)
  284. return newHandlers
  285. }
  286. // UpsertHandlers like `JoinHandlers` but it does
  287. // NOT copies the handlers entries and it does remove duplicates.
  288. func UpsertHandlers(h1 Handlers, h2 Handlers) Handlers {
  289. reg:
  290. for _, handler := range h2 {
  291. name := HandlerName(handler)
  292. for i, registeredHandler := range h1 {
  293. registeredName := HandlerName(registeredHandler)
  294. if name == registeredName {
  295. h1[i] = handler // replace this handler with the new one.
  296. continue reg // break and continue to the next handler.
  297. }
  298. }
  299. h1 = append(h1, handler) // or just insert it.
  300. }
  301. return h1
  302. }