handler.go 11 KB

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