application.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. package context
  2. import (
  3. stdContext "context"
  4. "io"
  5. "net/http"
  6. "sync"
  7. "github.com/kataras/golog"
  8. "github.com/tdewolff/minify/v2"
  9. )
  10. // Application is the context's owner.
  11. // This interface contains the functions that can be used with safety inside a Handler
  12. // by `context.Application()`.
  13. type Application interface {
  14. // ConfigurationReadOnly returns all the available configuration values can be used on a request.
  15. ConfigurationReadOnly() ConfigurationReadOnly
  16. // Logger returns the golog logger instance(pointer) that is being used inside the "app".
  17. Logger() *golog.Logger
  18. // IsDebug reports whether the application is running
  19. // under debug/development mode.
  20. // It's just a shortcut of Logger().Level >= golog.DebugLevel.
  21. // The same method existss as Context.IsDebug() too.
  22. IsDebug() bool
  23. // I18nReadOnly returns the i18n's read-only features.
  24. I18nReadOnly() I18nReadOnly
  25. // Validate validates a value and returns nil if passed or
  26. // the failure reason if not.
  27. Validate(interface{}) error
  28. // Minifier returns the minifier instance.
  29. // By default it can minifies:
  30. // - text/html
  31. // - text/css
  32. // - image/svg+xml
  33. // - application/text(javascript, ecmascript, json, xml).
  34. // Use that instance to add custom Minifiers before server ran.
  35. Minifier() *minify.M
  36. // View executes and write the result of a template file to the writer.
  37. //
  38. // Use context.View to render templates to the client instead.
  39. // Returns an error on failure, otherwise nil.
  40. View(writer io.Writer, filename string, layout string, bindingData interface{}) error
  41. // GetContextPool returns the Iris sync.Pool which holds the contexts values.
  42. // Iris automatically releases the request context, so you don't have to use it.
  43. // It's only useful to manually release the context on cases that connection
  44. // is hijacked by a third-party middleware and the http handler return too fast.
  45. GetContextPool() *Pool
  46. // GetContextErrorHandler returns the handler which handles errors
  47. // on JSON write failures.
  48. GetContextErrorHandler() ErrorHandler
  49. // ServeHTTPC is the internal router, it's visible because it can be used for advanced use cases,
  50. // i.e: routing within a foreign context.
  51. //
  52. // It is ready to use after Build state.
  53. ServeHTTPC(ctx *Context)
  54. // ServeHTTP is the main router handler which calls the .Serve and acquires a new context from the pool.
  55. //
  56. // It is ready to use after Build state.
  57. ServeHTTP(w http.ResponseWriter, r *http.Request)
  58. // Shutdown gracefully terminates all the application's server hosts and any tunnels.
  59. // Returns an error on the first failure, otherwise nil.
  60. Shutdown(ctx stdContext.Context) error
  61. // GetRouteReadOnly returns the registered "read-only" route based on its name, otherwise nil.
  62. // One note: "routeName" should be case-sensitive. Used by the context to get the current route.
  63. // It returns an interface instead to reduce wrong usage and to keep the decoupled design between
  64. // the context and the routes.
  65. //
  66. // Look core/router/APIBuilder#GetRoute for more.
  67. GetRouteReadOnly(routeName string) RouteReadOnly
  68. // GetRoutesReadOnly returns the registered "read-only" routes.
  69. //
  70. // Look core/router/APIBuilder#GetRoutes for more.
  71. GetRoutesReadOnly() []RouteReadOnly
  72. // FireErrorCode handles the response's error response.
  73. // If `Configuration.ResetOnFireErrorCode()` is true
  74. // and the response writer was a recorder or a gzip writer one
  75. // then it will try to reset the headers and the body before calling the
  76. // registered (or default) error handler for that error code set by
  77. // `ctx.StatusCode` method.
  78. FireErrorCode(ctx *Context)
  79. // RouteExists reports whether a particular route exists
  80. // It will search from the current subdomain of context's host, if not inside the root domain.
  81. RouteExists(ctx *Context, method, path string) bool
  82. // FindClosestPaths returns a list of "n" paths close to "path" under the given "subdomain".
  83. //
  84. // Order may change.
  85. FindClosestPaths(subdomain, searchPath string, n int) []string
  86. // String returns the Application's Name.
  87. String() string
  88. }
  89. // Notes(@kataras):
  90. // Alternative places...
  91. // 1. in apps/store, but it would require an empty `import _ "....apps/store"
  92. // from end-developers, to avoid the import cycle and *iris.Application access.
  93. // 2. in root package level, that could be the best option, it has access to the *iris.Application
  94. // instead of the context.Application interface, but we try to keep the root package
  95. // as minimum as possible, however: if in the future, those Application instances
  96. // can be registered through network instead of same-process then we must think of that choice.
  97. // 3. this is the best possible place, as the root package and all subpackages
  98. // have access to this context package without import cycles and they already using it,
  99. // the only downside is that we don't have access to the *iris.Application instance
  100. // but this context.Application is designed that way that can execute all important methods
  101. // as the whole Iris code base is so well written.
  102. var (
  103. // registerApps holds all the created iris Applications by this process.
  104. // It's slice instead of map because if IRIS_APP_NAME env var exists,
  105. // by-default all applications running on the same machine
  106. // will have the same name unless `Application.SetName` is called.
  107. registeredApps []Application
  108. onApplicationRegisteredListeners []func(Application)
  109. mu sync.RWMutex
  110. )
  111. // RegisterApplication registers an application to the global shared storage.
  112. func RegisterApplication(app Application) {
  113. if app == nil {
  114. return
  115. }
  116. mu.Lock()
  117. registeredApps = append(registeredApps, app)
  118. mu.Unlock()
  119. mu.RLock()
  120. for _, listener := range onApplicationRegisteredListeners {
  121. listener(app)
  122. }
  123. mu.RUnlock()
  124. }
  125. // OnApplicationRegistered adds a function which fires when a new application
  126. // is registered.
  127. func OnApplicationRegistered(listeners ...func(app Application)) {
  128. mu.Lock()
  129. onApplicationRegisteredListeners = append(onApplicationRegisteredListeners, listeners...)
  130. mu.Unlock()
  131. }
  132. // GetApplications returns a slice of all the registered Applications.
  133. func GetApplications() []Application {
  134. mu.RLock()
  135. // a copy slice but the instances are pointers so be careful what modifications are done
  136. // the return value is read-only but it can be casted to *iris.Application.
  137. apps := make([]Application, 0, len(registeredApps))
  138. copy(apps, registeredApps)
  139. mu.RUnlock()
  140. return apps
  141. }
  142. // LastApplication returns the last registered Application.
  143. // Handlers has access to the current Application,
  144. // use `Context.Application()` instead.
  145. func LastApplication() Application {
  146. mu.RLock()
  147. for i := len(registeredApps) - 1; i >= 0; i-- {
  148. if app := registeredApps[i]; app != nil {
  149. mu.RUnlock()
  150. return app
  151. }
  152. }
  153. mu.RUnlock()
  154. return nil
  155. }
  156. // GetApplication returns a registered Application
  157. // based on its name. If the "appName" is not unique
  158. // across Applications, then it will return the newest one.
  159. func GetApplication(appName string) (Application, bool) {
  160. mu.RLock()
  161. for i := len(registeredApps) - 1; i >= 0; i-- {
  162. if app := registeredApps[i]; app != nil && app.String() == appName {
  163. mu.RUnlock()
  164. return app, true
  165. }
  166. }
  167. mu.RUnlock()
  168. return nil, false
  169. }
  170. // MustGetApplication same as `GetApplication` but it
  171. // panics if "appName" is not a registered Application's name.
  172. func MustGetApplication(appName string) Application {
  173. app, ok := GetApplication(appName)
  174. if !ok || app == nil {
  175. panic(appName + " is not a registered Application")
  176. }
  177. return app
  178. }
  179. // DefaultLogger returns a Logger instance for an Iris module.
  180. // If the program contains at least one registered Iris Application
  181. // before this call then it will return a child of that Application's Logger
  182. // otherwise a fresh child of the `golog.Default` will be returned instead.
  183. //
  184. // It should be used when a module has no access to the Application or its Logger.
  185. func DefaultLogger(prefix string) (logger *golog.Logger) {
  186. if app := LastApplication(); app != nil {
  187. logger = app.Logger()
  188. } else {
  189. logger = golog.Default
  190. }
  191. logger = logger.Child(prefix)
  192. return
  193. }