context.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. // Copyright 2014 Manu Martinez-Almeida. All rights reserved.
  2. // Use of this source code is governed by a MIT style
  3. // license that can be found in the LICENSE file.
  4. package gin
  5. import (
  6. "bytes"
  7. "errors"
  8. "fmt"
  9. "log"
  10. "net"
  11. "math"
  12. "net/http"
  13. "strings"
  14. "github.com/gin-gonic/gin/binding"
  15. "github.com/gin-gonic/gin/render"
  16. "github.com/julienschmidt/httprouter"
  17. )
  18. const (
  19. ErrorTypeInternal = 1 << iota
  20. ErrorTypeExternal = 1 << iota
  21. ErrorTypeAll = 0xffffffff
  22. )
  23. // Used internally to collect errors that occurred during an http request.
  24. type errorMsg struct {
  25. Err string `json:"error"`
  26. Type uint32 `json:"-"`
  27. Meta interface{} `json:"meta"`
  28. }
  29. type errorMsgs []errorMsg
  30. func (a errorMsgs) ByType(typ uint32) errorMsgs {
  31. if len(a) == 0 {
  32. return a
  33. }
  34. result := make(errorMsgs, 0, len(a))
  35. for _, msg := range a {
  36. if msg.Type&typ > 0 {
  37. result = append(result, msg)
  38. }
  39. }
  40. return result
  41. }
  42. func (a errorMsgs) String() string {
  43. if len(a) == 0 {
  44. return ""
  45. }
  46. var buffer bytes.Buffer
  47. for i, msg := range a {
  48. text := fmt.Sprintf("Error #%02d: %s \n Meta: %v\n", (i + 1), msg.Err, msg.Meta)
  49. buffer.WriteString(text)
  50. }
  51. return buffer.String()
  52. }
  53. const AbortIndex = math.MaxInt8 / 2
  54. // Context is the most important part of gin. It allows us to pass variables between middleware,
  55. // manage the flow, validate the JSON of a request and render a JSON response for example.
  56. type Context struct {
  57. writermem responseWriter
  58. Request *http.Request
  59. Writer ResponseWriter
  60. Keys map[string]interface{}
  61. Errors errorMsgs
  62. Params httprouter.Params
  63. Engine *Engine
  64. handlers []HandlerFunc
  65. index int8
  66. accepted []string
  67. }
  68. /************************************/
  69. /********** CONTEXT CREATION ********/
  70. /************************************/
  71. func (engine *Engine) createContext(w http.ResponseWriter, req *http.Request, params httprouter.Params, handlers []HandlerFunc) *Context {
  72. c := engine.pool.Get().(*Context)
  73. c.reset()
  74. c.writermem.reset(w)
  75. c.Request = req
  76. c.Params = params
  77. c.handlers = handlers
  78. return c
  79. }
  80. func (engine *Engine) reuseContext(c *Context) {
  81. engine.pool.Put(c)
  82. }
  83. func (c *Context) reset() {
  84. c.Keys = nil
  85. c.index = -1
  86. c.accepted = nil
  87. c.Errors = c.Errors[0:0]
  88. }
  89. func (c *Context) Copy() *Context {
  90. var cp Context = *c
  91. cp.index = AbortIndex
  92. cp.handlers = nil
  93. return &cp
  94. }
  95. /************************************/
  96. /*************** FLOW ***************/
  97. /************************************/
  98. // Next should be used only in the middlewares.
  99. // It executes the pending handlers in the chain inside the calling handler.
  100. // See example in github.
  101. func (c *Context) Next() {
  102. c.index++
  103. s := int8(len(c.handlers))
  104. for ; c.index < s; c.index++ {
  105. c.handlers[c.index](c)
  106. }
  107. }
  108. // Forces the system to not continue calling the pending handlers in the chain.
  109. func (c *Context) Abort() {
  110. c.index = AbortIndex
  111. }
  112. // Same than AbortWithStatus() but also writes the specified response status code.
  113. // For example, the first handler checks if the request is authorized. If it's not, context.AbortWithStatus(401) should be called.
  114. func (c *Context) AbortWithStatus(code int) {
  115. c.Writer.WriteHeader(code)
  116. c.Abort()
  117. }
  118. /************************************/
  119. /********* ERROR MANAGEMENT *********/
  120. /************************************/
  121. // Fail is the same as Abort plus an error message.
  122. // Calling `context.Fail(500, err)` is equivalent to:
  123. // ```
  124. // context.Error("Operation aborted", err)
  125. // context.AbortWithStatus(500)
  126. // ```
  127. func (c *Context) Fail(code int, err error) {
  128. c.Error(err, "Operation aborted")
  129. c.AbortWithStatus(code)
  130. }
  131. func (c *Context) ErrorTyped(err error, typ uint32, meta interface{}) {
  132. c.Errors = append(c.Errors, errorMsg{
  133. Err: err.Error(),
  134. Type: typ,
  135. Meta: meta,
  136. })
  137. }
  138. // Attaches an error to the current context. The error is pushed to a list of errors.
  139. // It's a good idea to call Error for each error that occurred during the resolution of a request.
  140. // A middleware can be used to collect all the errors and push them to a database together, print a log, or append it in the HTTP response.
  141. func (c *Context) Error(err error, meta interface{}) {
  142. c.ErrorTyped(err, ErrorTypeExternal, meta)
  143. }
  144. func (c *Context) LastError() error {
  145. nuErrors := len(c.Errors)
  146. if nuErrors > 0 {
  147. return errors.New(c.Errors[nuErrors-1].Err)
  148. } else {
  149. return nil
  150. }
  151. }
  152. /************************************/
  153. /******** METADATA MANAGEMENT********/
  154. /************************************/
  155. // Sets a new pair key/value just for the specified context.
  156. // It also lazy initializes the hashmap.
  157. func (c *Context) Set(key string, item interface{}) {
  158. if c.Keys == nil {
  159. c.Keys = make(map[string]interface{})
  160. }
  161. c.Keys[key] = item
  162. }
  163. // Get returns the value for the given key or an error if the key does not exist.
  164. func (c *Context) Get(key string) (interface{}, error) {
  165. if c.Keys != nil {
  166. value, ok := c.Keys[key]
  167. if ok {
  168. return value, nil
  169. }
  170. }
  171. return nil, errors.New("Key %s does not exist")
  172. }
  173. // MustGet returns the value for the given key or panics if the value doesn't exist.
  174. func (c *Context) MustGet(key string) interface{} {
  175. value, err := c.Get(key)
  176. if err != nil {
  177. log.Panic(err.Error())
  178. }
  179. return value
  180. }
  181. func ipInMasks(ip net.IP, masks []interface{}) bool {
  182. for _, proxy := range masks {
  183. var mask *net.IPNet
  184. var err error
  185. switch t := proxy.(type) {
  186. case string:
  187. if _, mask, err = net.ParseCIDR(t); err != nil {
  188. log.Panic(err)
  189. }
  190. case net.IP:
  191. mask = &net.IPNet{IP: t, Mask: net.CIDRMask(len(t)*8, len(t)*8)}
  192. case net.IPNet:
  193. mask = &t
  194. }
  195. if mask.Contains(ip) {
  196. return true
  197. }
  198. }
  199. return false
  200. }
  201. // the ForwardedFor middleware unwraps the X-Forwarded-For headers, be careful to only use this
  202. // middleware if you've got servers in front of this server. The list with (known) proxies and
  203. // local ips are being filtered out of the forwarded for list, giving the last not local ip being
  204. // the real client ip.
  205. func ForwardedFor(proxies ...interface{}) HandlerFunc {
  206. if len(proxies) == 0 {
  207. // default to local ips
  208. var reservedLocalIps = []string{"10.0.0.0/8", "127.0.0.1/32", "172.16.0.0/12", "192.168.0.0/16"}
  209. proxies = make([]interface{}, len(reservedLocalIps))
  210. for i, v := range reservedLocalIps {
  211. proxies[i] = v
  212. }
  213. }
  214. return func(c *Context) {
  215. // the X-Forwarded-For header contains an array with left most the client ip, then
  216. // comma separated, all proxies the request passed. The last proxy appears
  217. // as the remote address of the request. Returning the client
  218. // ip to comply with default RemoteAddr response.
  219. // check if remoteaddr is local ip or in list of defined proxies
  220. remoteIp := net.ParseIP(strings.Split(c.Request.RemoteAddr, ":")[0])
  221. if !ipInMasks(remoteIp, proxies) {
  222. return
  223. }
  224. if forwardedFor := c.Request.Header.Get("X-Forwarded-For"); forwardedFor != "" {
  225. parts := strings.Split(forwardedFor, ",")
  226. for i := len(parts) - 1; i >= 0; i-- {
  227. part := parts[i]
  228. ip := net.ParseIP(strings.TrimSpace(part))
  229. if ipInMasks(ip, proxies) {
  230. continue
  231. }
  232. // returning remote addr conform the original remote addr format
  233. c.Request.RemoteAddr = ip.String() + ":0"
  234. // remove forwarded for address
  235. c.Request.Header.Set("X-Forwarded-For", "")
  236. return
  237. }
  238. }
  239. }
  240. }
  241. func (c *Context) ClientIP() string {
  242. return c.Request.RemoteAddr
  243. }
  244. /************************************/
  245. /********* PARSING REQUEST **********/
  246. /************************************/
  247. // This function checks the Content-Type to select a binding engine automatically,
  248. // Depending the "Content-Type" header different bindings are used:
  249. // "application/json" --> JSON binding
  250. // "application/xml" --> XML binding
  251. // else --> returns an error
  252. // if Parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. It decodes the json payload into the struct specified as a pointer.Like ParseBody() but this method also writes a 400 error if the json is not valid.
  253. func (c *Context) Bind(obj interface{}) bool {
  254. var b binding.Binding
  255. ctype := filterFlags(c.Request.Header.Get("Content-Type"))
  256. switch {
  257. case c.Request.Method == "GET" || ctype == MIMEPOSTForm:
  258. b = binding.Form
  259. case ctype == MIMEMultipartPOSTForm:
  260. b = binding.MultipartForm
  261. case ctype == MIMEJSON:
  262. b = binding.JSON
  263. case ctype == MIMEXML || ctype == MIMEXML2:
  264. b = binding.XML
  265. default:
  266. c.Fail(400, errors.New("unknown content-type: "+ctype))
  267. return false
  268. }
  269. return c.BindWith(obj, b)
  270. }
  271. func (c *Context) BindWith(obj interface{}, b binding.Binding) bool {
  272. if err := b.Bind(c.Request, obj); err != nil {
  273. c.Fail(400, err)
  274. return false
  275. }
  276. return true
  277. }
  278. /************************************/
  279. /******** RESPONSE RENDERING ********/
  280. /************************************/
  281. func (c *Context) Render(code int, render render.Render, obj ...interface{}) {
  282. if err := render.Render(c.Writer, code, obj...); err != nil {
  283. c.ErrorTyped(err, ErrorTypeInternal, obj)
  284. c.AbortWithStatus(500)
  285. }
  286. }
  287. // Serializes the given struct as JSON into the response body in a fast and efficient way.
  288. // It also sets the Content-Type as "application/json".
  289. func (c *Context) JSON(code int, obj interface{}) {
  290. c.Render(code, render.JSON, obj)
  291. }
  292. // Serializes the given struct as XML into the response body in a fast and efficient way.
  293. // It also sets the Content-Type as "application/xml".
  294. func (c *Context) XML(code int, obj interface{}) {
  295. c.Render(code, render.XML, obj)
  296. }
  297. // Renders the HTTP template specified by its file name.
  298. // It also updates the HTTP code and sets the Content-Type as "text/html".
  299. // See http://golang.org/doc/articles/wiki/
  300. func (c *Context) HTML(code int, name string, obj interface{}) {
  301. c.Render(code, c.Engine.HTMLRender, name, obj)
  302. }
  303. // Writes the given string into the response body and sets the Content-Type to "text/plain".
  304. func (c *Context) String(code int, format string, values ...interface{}) {
  305. c.Render(code, render.Plain, format, values)
  306. }
  307. // Writes the given string into the response body and sets the Content-Type to "text/html" without template.
  308. func (c *Context) HTMLString(code int, format string, values ...interface{}) {
  309. c.Render(code, render.HTMLPlain, format, values)
  310. }
  311. // Returns a HTTP redirect to the specific location.
  312. func (c *Context) Redirect(code int, location string) {
  313. if code >= 300 && code <= 308 {
  314. c.Render(code, render.Redirect, location)
  315. } else {
  316. log.Panicf("Cannot send a redirect with status code %d", code)
  317. }
  318. }
  319. // Writes some data into the body stream and updates the HTTP code.
  320. func (c *Context) Data(code int, contentType string, data []byte) {
  321. if len(contentType) > 0 {
  322. c.Writer.Header().Set("Content-Type", contentType)
  323. }
  324. c.Writer.WriteHeader(code)
  325. c.Writer.Write(data)
  326. }
  327. // Writes the specified file into the body stream
  328. func (c *Context) File(filepath string) {
  329. http.ServeFile(c.Writer, c.Request, filepath)
  330. }
  331. /************************************/
  332. /******** CONTENT NEGOTIATION *******/
  333. /************************************/
  334. type Negotiate struct {
  335. Offered []string
  336. HTMLPath string
  337. HTMLData interface{}
  338. JSONData interface{}
  339. XMLData interface{}
  340. Data interface{}
  341. }
  342. func (c *Context) Negotiate(code int, config Negotiate) {
  343. switch c.NegotiateFormat(config.Offered...) {
  344. case MIMEJSON:
  345. data := chooseData(config.JSONData, config.Data)
  346. c.JSON(code, data)
  347. case MIMEHTML:
  348. data := chooseData(config.HTMLData, config.Data)
  349. if len(config.HTMLPath) == 0 {
  350. log.Panic("negotiate config is wrong. html path is needed")
  351. }
  352. c.HTML(code, config.HTMLPath, data)
  353. case MIMEXML:
  354. data := chooseData(config.XMLData, config.Data)
  355. c.XML(code, data)
  356. default:
  357. c.Fail(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server"))
  358. }
  359. }
  360. func (c *Context) NegotiateFormat(offered ...string) string {
  361. if len(offered) == 0 {
  362. log.Panic("you must provide at least one offer")
  363. }
  364. if c.accepted == nil {
  365. c.accepted = parseAccept(c.Request.Header.Get("Accept"))
  366. }
  367. if len(c.accepted) == 0 {
  368. return offered[0]
  369. } else {
  370. for _, accepted := range c.accepted {
  371. for _, offert := range offered {
  372. if accepted == offert {
  373. return offert
  374. }
  375. }
  376. }
  377. return ""
  378. }
  379. }
  380. func (c *Context) SetAccepted(formats ...string) {
  381. c.accepted = formats
  382. }