You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

335 lines
8.9KB

  1. // +build go1.3
  2. // Copyright 2014 The Macaron Authors
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License"): you may
  5. // not use this file except in compliance with the License. You may obtain
  6. // a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. // License for the specific language governing permissions and limitations
  14. // under the License.
  15. // Package macaron is a high productive and modular web framework in Go.
  16. package macaron
  17. import (
  18. "io"
  19. "log"
  20. "net/http"
  21. "os"
  22. "reflect"
  23. "strings"
  24. "sync"
  25. "github.com/unknwon/com"
  26. "gopkg.in/ini.v1"
  27. "gitea.com/macaron/inject"
  28. )
  29. const _VERSION = "1.3.2.1216"
  30. func Version() string {
  31. return _VERSION
  32. }
  33. // Handler can be any callable function.
  34. // Macaron attempts to inject services into the handler's argument list,
  35. // and panics if an argument could not be fullfilled via dependency injection.
  36. type Handler interface{}
  37. // handlerFuncInvoker is an inject.FastInvoker wrapper of func(http.ResponseWriter, *http.Request).
  38. type handlerFuncInvoker func(http.ResponseWriter, *http.Request)
  39. func (invoke handlerFuncInvoker) Invoke(params []interface{}) ([]reflect.Value, error) {
  40. invoke(params[0].(http.ResponseWriter), params[1].(*http.Request))
  41. return nil, nil
  42. }
  43. // internalServerErrorInvoker is an inject.FastInvoker wrapper of func(rw http.ResponseWriter, err error).
  44. type internalServerErrorInvoker func(rw http.ResponseWriter, err error)
  45. func (invoke internalServerErrorInvoker) Invoke(params []interface{}) ([]reflect.Value, error) {
  46. invoke(params[0].(http.ResponseWriter), params[1].(error))
  47. return nil, nil
  48. }
  49. // validateAndWrapHandler makes sure a handler is a callable function, it panics if not.
  50. // When the handler is also potential to be any built-in inject.FastInvoker,
  51. // it wraps the handler automatically to have some performance gain.
  52. func validateAndWrapHandler(h Handler) Handler {
  53. if reflect.TypeOf(h).Kind() != reflect.Func {
  54. panic("Macaron handler must be a callable function")
  55. }
  56. if !inject.IsFastInvoker(h) {
  57. switch v := h.(type) {
  58. case func(*Context):
  59. return ContextInvoker(v)
  60. case func(*Context, *log.Logger):
  61. return LoggerInvoker(v)
  62. case func(http.ResponseWriter, *http.Request):
  63. return handlerFuncInvoker(v)
  64. case func(http.ResponseWriter, error):
  65. return internalServerErrorInvoker(v)
  66. }
  67. }
  68. return h
  69. }
  70. // validateAndWrapHandlers preforms validation and wrapping for each input handler.
  71. // It accepts an optional wrapper function to perform custom wrapping on handlers.
  72. func validateAndWrapHandlers(handlers []Handler, wrappers ...func(Handler) Handler) []Handler {
  73. var wrapper func(Handler) Handler
  74. if len(wrappers) > 0 {
  75. wrapper = wrappers[0]
  76. }
  77. wrappedHandlers := make([]Handler, len(handlers))
  78. for i, h := range handlers {
  79. h = validateAndWrapHandler(h)
  80. if wrapper != nil && !inject.IsFastInvoker(h) {
  81. h = wrapper(h)
  82. }
  83. wrappedHandlers[i] = h
  84. }
  85. return wrappedHandlers
  86. }
  87. // Macaron represents the top level web application.
  88. // inject.Injector methods can be invoked to map services on a global level.
  89. type Macaron struct {
  90. inject.Injector
  91. befores []BeforeHandler
  92. handlers []Handler
  93. action Handler
  94. hasURLPrefix bool
  95. urlPrefix string // For suburl support.
  96. *Router
  97. logger *log.Logger
  98. }
  99. // NewWithLogger creates a bare bones Macaron instance.
  100. // Use this method if you want to have full control over the middleware that is used.
  101. // You can specify logger output writer with this function.
  102. func NewWithLogger(out io.Writer) *Macaron {
  103. m := &Macaron{
  104. Injector: inject.New(),
  105. action: func() {},
  106. Router: NewRouter(),
  107. logger: log.New(out, "[Macaron] ", 0),
  108. }
  109. m.Router.m = m
  110. m.Map(m.logger)
  111. m.Map(defaultReturnHandler())
  112. m.NotFound(http.NotFound)
  113. m.InternalServerError(func(rw http.ResponseWriter, err error) {
  114. http.Error(rw, err.Error(), 500)
  115. })
  116. return m
  117. }
  118. // New creates a bare bones Macaron instance.
  119. // Use this method if you want to have full control over the middleware that is used.
  120. func New() *Macaron {
  121. return NewWithLogger(os.Stdout)
  122. }
  123. // Classic creates a classic Macaron with some basic default middleware:
  124. // macaron.Logger, macaron.Recovery and macaron.Static.
  125. func Classic() *Macaron {
  126. m := New()
  127. m.Use(Logger())
  128. m.Use(Recovery())
  129. m.Use(Static("public"))
  130. return m
  131. }
  132. // Handlers sets the entire middleware stack with the given Handlers.
  133. // This will clear any current middleware handlers,
  134. // and panics if any of the handlers is not a callable function
  135. func (m *Macaron) Handlers(handlers ...Handler) {
  136. m.handlers = make([]Handler, 0)
  137. for _, handler := range handlers {
  138. m.Use(handler)
  139. }
  140. }
  141. // Action sets the handler that will be called after all the middleware has been invoked.
  142. // This is set to macaron.Router in a macaron.Classic().
  143. func (m *Macaron) Action(handler Handler) {
  144. handler = validateAndWrapHandler(handler)
  145. m.action = handler
  146. }
  147. // BeforeHandler represents a handler executes at beginning of every request.
  148. // Macaron stops future process when it returns true.
  149. type BeforeHandler func(rw http.ResponseWriter, req *http.Request) bool
  150. func (m *Macaron) Before(handler BeforeHandler) {
  151. m.befores = append(m.befores, handler)
  152. }
  153. // Use adds a middleware Handler to the stack,
  154. // and panics if the handler is not a callable func.
  155. // Middleware Handlers are invoked in the order that they are added.
  156. func (m *Macaron) Use(handler Handler) {
  157. handler = validateAndWrapHandler(handler)
  158. m.handlers = append(m.handlers, handler)
  159. }
  160. func (m *Macaron) createContext(rw http.ResponseWriter, req *http.Request) *Context {
  161. c := &Context{
  162. Injector: inject.New(),
  163. handlers: m.handlers,
  164. action: m.action,
  165. index: 0,
  166. Router: m.Router,
  167. Req: Request{req},
  168. Resp: NewResponseWriter(req.Method, rw),
  169. Render: &DummyRender{rw},
  170. Data: make(map[string]interface{}),
  171. }
  172. c.SetParent(m)
  173. c.Map(c)
  174. c.MapTo(c.Resp, (*http.ResponseWriter)(nil))
  175. c.Map(req)
  176. return c
  177. }
  178. // ServeHTTP is the HTTP Entry point for a Macaron instance.
  179. // Useful if you want to control your own HTTP server.
  180. // Be aware that none of middleware will run without registering any router.
  181. func (m *Macaron) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
  182. if m.hasURLPrefix {
  183. req.URL.Path = strings.TrimPrefix(req.URL.Path, m.urlPrefix)
  184. }
  185. for _, h := range m.befores {
  186. if h(rw, req) {
  187. return
  188. }
  189. }
  190. m.Router.ServeHTTP(rw, req)
  191. }
  192. func GetDefaultListenInfo() (string, int) {
  193. host := os.Getenv("HOST")
  194. if len(host) == 0 {
  195. host = "0.0.0.0"
  196. }
  197. port := com.StrTo(os.Getenv("PORT")).MustInt()
  198. if port == 0 {
  199. port = 4000
  200. }
  201. return host, port
  202. }
  203. // Run the http server. Listening on os.GetEnv("PORT") or 4000 by default.
  204. func (m *Macaron) Run(args ...interface{}) {
  205. host, port := GetDefaultListenInfo()
  206. if len(args) == 1 {
  207. switch arg := args[0].(type) {
  208. case string:
  209. host = arg
  210. case int:
  211. port = arg
  212. }
  213. } else if len(args) >= 2 {
  214. if arg, ok := args[0].(string); ok {
  215. host = arg
  216. }
  217. if arg, ok := args[1].(int); ok {
  218. port = arg
  219. }
  220. }
  221. addr := host + ":" + com.ToStr(port)
  222. logger := m.GetVal(reflect.TypeOf(m.logger)).Interface().(*log.Logger)
  223. logger.Printf("listening on %s (%s)\n", addr, safeEnv())
  224. logger.Fatalln(http.ListenAndServe(addr, m))
  225. }
  226. // SetURLPrefix sets URL prefix of router layer, so that it support suburl.
  227. func (m *Macaron) SetURLPrefix(prefix string) {
  228. m.urlPrefix = prefix
  229. m.hasURLPrefix = len(m.urlPrefix) > 0
  230. }
  231. // ____ ____ .__ ___. .__
  232. // \ \ / /____ _______|__|____ \_ |__ | | ____ ______
  233. // \ Y /\__ \\_ __ \ \__ \ | __ \| | _/ __ \ / ___/
  234. // \ / / __ \| | \/ |/ __ \| \_\ \ |_\ ___/ \___ \
  235. // \___/ (____ /__| |__(____ /___ /____/\___ >____ >
  236. // \/ \/ \/ \/ \/
  237. const (
  238. DEV = "development"
  239. PROD = "production"
  240. TEST = "test"
  241. )
  242. var (
  243. // Env is the environment that Macaron is executing in.
  244. // The MACARON_ENV is read on initialization to set this variable.
  245. Env = DEV
  246. envLock sync.Mutex
  247. // Path of work directory.
  248. Root string
  249. // Flash applies to current request.
  250. FlashNow bool
  251. // Configuration convention object.
  252. cfg *ini.File
  253. )
  254. func setENV(e string) {
  255. envLock.Lock()
  256. defer envLock.Unlock()
  257. if len(e) > 0 {
  258. Env = e
  259. }
  260. }
  261. func safeEnv() string {
  262. envLock.Lock()
  263. defer envLock.Unlock()
  264. return Env
  265. }
  266. func init() {
  267. setENV(os.Getenv("MACARON_ENV"))
  268. var err error
  269. Root, err = os.Getwd()
  270. if err != nil {
  271. panic("error getting work directory: " + err.Error())
  272. }
  273. }
  274. // SetConfig sets data sources for configuration.
  275. func SetConfig(source interface{}, others ...interface{}) (_ *ini.File, err error) {
  276. cfg, err = ini.Load(source, others...)
  277. return Config(), err
  278. }
  279. // Config returns configuration convention object.
  280. // It returns an empty object if there is no one available.
  281. func Config() *ini.File {
  282. if cfg == nil {
  283. return ini.Empty()
  284. }
  285. return cfg
  286. }