Micro & pluggable web framework for Go
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.

291 lines
6.5KB

  1. // Copyright 2015 The Tango Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package tango
  5. import (
  6. "net/http"
  7. "os"
  8. "strconv"
  9. "strings"
  10. "sync"
  11. )
  12. // Version returns tango's version
  13. func Version() string {
  14. return "0.5.5.0412"
  15. }
  16. // Tango describes tango object
  17. type Tango struct {
  18. http.Server
  19. Router
  20. handlers []Handler
  21. logger Logger
  22. ErrHandler Handler
  23. ctxPool sync.Pool
  24. respPool sync.Pool
  25. }
  26. var (
  27. // ClassicHandlers the default handlers
  28. ClassicHandlers = []Handler{
  29. Logging(),
  30. Recovery(false),
  31. Compresses([]string{}),
  32. Static(StaticOptions{Prefix: "public"}),
  33. Return(),
  34. Param(),
  35. Contexts(),
  36. }
  37. )
  38. // Logger returns logger interface
  39. func (t *Tango) Logger() Logger {
  40. return t.logger
  41. }
  42. // Get sets a route with GET method
  43. func (t *Tango) Get(url string, c interface{}, middlewares ...Handler) {
  44. t.Route([]string{"GET", "HEAD:Get"}, url, c, middlewares...)
  45. }
  46. // Post sets a route with POST method
  47. func (t *Tango) Post(url string, c interface{}, middlewares ...Handler) {
  48. t.Route([]string{"POST"}, url, c, middlewares...)
  49. }
  50. // Head sets a route with HEAD method
  51. func (t *Tango) Head(url string, c interface{}, middlewares ...Handler) {
  52. t.Route([]string{"HEAD"}, url, c, middlewares...)
  53. }
  54. // Options sets a route with OPTIONS method
  55. func (t *Tango) Options(url string, c interface{}, middlewares ...Handler) {
  56. t.Route([]string{"OPTIONS"}, url, c, middlewares...)
  57. }
  58. // Trace sets a route with TRACE method
  59. func (t *Tango) Trace(url string, c interface{}, middlewares ...Handler) {
  60. t.Route([]string{"TRACE"}, url, c, middlewares...)
  61. }
  62. // Patch sets a route with PATCH method
  63. func (t *Tango) Patch(url string, c interface{}, middlewares ...Handler) {
  64. t.Route([]string{"PATCH"}, url, c, middlewares...)
  65. }
  66. // Delete sets a route with DELETE method
  67. func (t *Tango) Delete(url string, c interface{}, middlewares ...Handler) {
  68. t.Route([]string{"DELETE"}, url, c, middlewares...)
  69. }
  70. // Put sets a route with PUT method
  71. func (t *Tango) Put(url string, c interface{}, middlewares ...Handler) {
  72. t.Route([]string{"PUT"}, url, c, middlewares...)
  73. }
  74. // Any sets a route every support method is OK.
  75. func (t *Tango) Any(url string, c interface{}, middlewares ...Handler) {
  76. t.Route(SupportMethods, url, c, middlewares...)
  77. t.Route([]string{"HEAD:Get"}, url, c, middlewares...)
  78. }
  79. // Use addes some global handlers
  80. func (t *Tango) Use(handlers ...Handler) {
  81. t.handlers = append(t.handlers, handlers...)
  82. }
  83. // GetAddress parses address
  84. func getAddress(args ...interface{}) string {
  85. var host string
  86. var port int
  87. if len(args) == 1 {
  88. switch arg := args[0].(type) {
  89. case string:
  90. addrs := strings.Split(args[0].(string), ":")
  91. if len(addrs) == 1 {
  92. host = addrs[0]
  93. } else if len(addrs) >= 2 {
  94. host = addrs[0]
  95. _port, _ := strconv.ParseInt(addrs[1], 10, 0)
  96. port = int(_port)
  97. }
  98. case int:
  99. port = arg
  100. }
  101. } else if len(args) >= 2 {
  102. if arg, ok := args[0].(string); ok {
  103. host = arg
  104. }
  105. if arg, ok := args[1].(int); ok {
  106. port = arg
  107. }
  108. }
  109. if envHost := os.Getenv("HOST"); len(envHost) != 0 {
  110. host = envHost
  111. } else if len(host) == 0 {
  112. host = "0.0.0.0"
  113. }
  114. if envPort, _ := strconv.ParseInt(os.Getenv("PORT"), 10, 32); envPort != 0 {
  115. port = int(envPort)
  116. } else if port == 0 {
  117. port = 8000
  118. }
  119. addr := host + ":" + strconv.FormatInt(int64(port), 10)
  120. return addr
  121. }
  122. // Run the http server. Listening on os.GetEnv("PORT") or 8000 by default.
  123. func (t *Tango) Run(args ...interface{}) {
  124. addr := getAddress(args...)
  125. t.logger.Info("Listening on http://" + addr)
  126. t.Server.Addr = addr
  127. t.Server.Handler = t
  128. err := t.ListenAndServe()
  129. if err != nil {
  130. t.logger.Error(err)
  131. }
  132. }
  133. // RunTLS runs the https server with special cert and key files
  134. func (t *Tango) RunTLS(certFile, keyFile string, args ...interface{}) {
  135. addr := getAddress(args...)
  136. t.logger.Info("Listening on https://" + addr)
  137. t.Server.Addr = addr
  138. t.Server.Handler = t
  139. err := t.ListenAndServeTLS(certFile, keyFile)
  140. if err != nil {
  141. t.logger.Error(err)
  142. }
  143. }
  144. // HandlerFunc describes the handle function
  145. type HandlerFunc func(ctx *Context)
  146. // Handle executes the handler
  147. func (h HandlerFunc) Handle(ctx *Context) {
  148. h(ctx)
  149. }
  150. // WrapBefore wraps a http standard handler to tango's before action executes
  151. func WrapBefore(handler http.Handler) HandlerFunc {
  152. return func(ctx *Context) {
  153. handler.ServeHTTP(ctx.ResponseWriter, ctx.Req())
  154. ctx.Next()
  155. }
  156. }
  157. // WrapAfter wraps a http standard handler to tango's after action executes
  158. func WrapAfter(handler http.Handler) HandlerFunc {
  159. return func(ctx *Context) {
  160. ctx.Next()
  161. handler.ServeHTTP(ctx.ResponseWriter, ctx.Req())
  162. }
  163. }
  164. // UseHandler adds a standard http handler to tango's
  165. func (t *Tango) UseHandler(handler http.Handler) {
  166. t.Use(WrapBefore(handler))
  167. }
  168. // ServeHTTP implementes net/http interface so that it could run with net/http
  169. func (t *Tango) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  170. resp := t.respPool.Get().(*responseWriter)
  171. resp.reset(w)
  172. ctx := t.ctxPool.Get().(*Context)
  173. ctx.tan = t
  174. ctx.reset(req, resp)
  175. ctx.invoke()
  176. // if there is no logging or error handle, so the last written check.
  177. if !ctx.Written() {
  178. p := req.URL.Path
  179. if len(req.URL.RawQuery) > 0 {
  180. p = p + "?" + req.URL.RawQuery
  181. }
  182. if ctx.Route() != nil {
  183. if ctx.Result == nil {
  184. ctx.WriteString("")
  185. t.logger.Info(req.Method, ctx.Status(), p)
  186. t.ctxPool.Put(ctx)
  187. t.respPool.Put(resp)
  188. return
  189. }
  190. panic("result should be handler before")
  191. }
  192. if ctx.Result == nil {
  193. ctx.Result = NotFound()
  194. }
  195. ctx.HandleError()
  196. t.logger.Error(req.Method, ctx.Status(), p)
  197. }
  198. t.ctxPool.Put(ctx)
  199. t.respPool.Put(resp)
  200. }
  201. // NewWithLog creates tango with the special logger and handlers
  202. func NewWithLog(logger Logger, handlers ...Handler) *Tango {
  203. tan := &Tango{
  204. Router: newRouter(),
  205. logger: logger,
  206. handlers: make([]Handler, 0),
  207. ErrHandler: Errors(),
  208. }
  209. tan.ctxPool.New = func() interface{} {
  210. return &Context{
  211. tan: tan,
  212. Logger: tan.logger,
  213. }
  214. }
  215. tan.respPool.New = func() interface{} {
  216. return &responseWriter{}
  217. }
  218. tan.Use(handlers...)
  219. return tan
  220. }
  221. // New creates tango with the default logger and handlers
  222. func New(handlers ...Handler) *Tango {
  223. return NewWithLog(NewLogger(os.Stdout), handlers...)
  224. }
  225. // Classic returns the tango with default handlers and logger
  226. func Classic(l ...Logger) *Tango {
  227. var logger Logger
  228. if len(l) == 0 {
  229. logger = NewLogger(os.Stdout)
  230. } else {
  231. logger = l[0]
  232. }
  233. return NewWithLog(
  234. logger,
  235. ClassicHandlers...,
  236. )
  237. }