context.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // Copyright 2014 beego Author. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Package context provide the context utils
  15. // Usage:
  16. //
  17. // import "github.com/cnlh/nps/vender/github.com/astaxie/beego/context"
  18. //
  19. // ctx := context.Context{Request:req,ResponseWriter:rw}
  20. //
  21. // more docs http://beego.me/docs/module/context.md
  22. package context
  23. import (
  24. "bufio"
  25. "crypto/hmac"
  26. "crypto/sha1"
  27. "encoding/base64"
  28. "errors"
  29. "fmt"
  30. "net"
  31. "net/http"
  32. "strconv"
  33. "strings"
  34. "time"
  35. "github.com/cnlh/nps/vender/github.com/astaxie/beego/utils"
  36. )
  37. //commonly used mime-types
  38. const (
  39. ApplicationJSON = "application/json"
  40. ApplicationXML = "application/xml"
  41. ApplicationYAML = "application/x-yaml"
  42. TextXML = "text/xml"
  43. )
  44. // NewContext return the Context with Input and Output
  45. func NewContext() *Context {
  46. return &Context{
  47. Input: NewInput(),
  48. Output: NewOutput(),
  49. }
  50. }
  51. // Context Http request context struct including BeegoInput, BeegoOutput, http.Request and http.ResponseWriter.
  52. // BeegoInput and BeegoOutput provides some api to operate request and response more easily.
  53. type Context struct {
  54. Input *BeegoInput
  55. Output *BeegoOutput
  56. Request *http.Request
  57. ResponseWriter *Response
  58. _xsrfToken string
  59. }
  60. // Reset init Context, BeegoInput and BeegoOutput
  61. func (ctx *Context) Reset(rw http.ResponseWriter, r *http.Request) {
  62. ctx.Request = r
  63. if ctx.ResponseWriter == nil {
  64. ctx.ResponseWriter = &Response{}
  65. }
  66. ctx.ResponseWriter.reset(rw)
  67. ctx.Input.Reset(ctx)
  68. ctx.Output.Reset(ctx)
  69. ctx._xsrfToken = ""
  70. }
  71. // Redirect does redirection to localurl with http header status code.
  72. func (ctx *Context) Redirect(status int, localurl string) {
  73. http.Redirect(ctx.ResponseWriter, ctx.Request, localurl, status)
  74. }
  75. // Abort stops this request.
  76. // if beego.ErrorMaps exists, panic body.
  77. func (ctx *Context) Abort(status int, body string) {
  78. ctx.Output.SetStatus(status)
  79. panic(body)
  80. }
  81. // WriteString Write string to response body.
  82. // it sends response body.
  83. func (ctx *Context) WriteString(content string) {
  84. ctx.ResponseWriter.Write([]byte(content))
  85. }
  86. // GetCookie Get cookie from request by a given key.
  87. // It's alias of BeegoInput.Cookie.
  88. func (ctx *Context) GetCookie(key string) string {
  89. return ctx.Input.Cookie(key)
  90. }
  91. // SetCookie Set cookie for response.
  92. // It's alias of BeegoOutput.Cookie.
  93. func (ctx *Context) SetCookie(name string, value string, others ...interface{}) {
  94. ctx.Output.Cookie(name, value, others...)
  95. }
  96. // GetSecureCookie Get secure cookie from request by a given key.
  97. func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) {
  98. val := ctx.Input.Cookie(key)
  99. if val == "" {
  100. return "", false
  101. }
  102. parts := strings.SplitN(val, "|", 3)
  103. if len(parts) != 3 {
  104. return "", false
  105. }
  106. vs := parts[0]
  107. timestamp := parts[1]
  108. sig := parts[2]
  109. h := hmac.New(sha1.New, []byte(Secret))
  110. fmt.Fprintf(h, "%s%s", vs, timestamp)
  111. if fmt.Sprintf("%02x", h.Sum(nil)) != sig {
  112. return "", false
  113. }
  114. res, _ := base64.URLEncoding.DecodeString(vs)
  115. return string(res), true
  116. }
  117. // SetSecureCookie Set Secure cookie for response.
  118. func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) {
  119. vs := base64.URLEncoding.EncodeToString([]byte(value))
  120. timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
  121. h := hmac.New(sha1.New, []byte(Secret))
  122. fmt.Fprintf(h, "%s%s", vs, timestamp)
  123. sig := fmt.Sprintf("%02x", h.Sum(nil))
  124. cookie := strings.Join([]string{vs, timestamp, sig}, "|")
  125. ctx.Output.Cookie(name, cookie, others...)
  126. }
  127. // XSRFToken creates a xsrf token string and returns.
  128. func (ctx *Context) XSRFToken(key string, expire int64) string {
  129. if ctx._xsrfToken == "" {
  130. token, ok := ctx.GetSecureCookie(key, "_xsrf")
  131. if !ok {
  132. token = string(utils.RandomCreateBytes(32))
  133. ctx.SetSecureCookie(key, "_xsrf", token, expire)
  134. }
  135. ctx._xsrfToken = token
  136. }
  137. return ctx._xsrfToken
  138. }
  139. // CheckXSRFCookie checks xsrf token in this request is valid or not.
  140. // the token can provided in request header "X-Xsrftoken" and "X-CsrfToken"
  141. // or in form field value named as "_xsrf".
  142. func (ctx *Context) CheckXSRFCookie() bool {
  143. token := ctx.Input.Query("_xsrf")
  144. if token == "" {
  145. token = ctx.Request.Header.Get("X-Xsrftoken")
  146. }
  147. if token == "" {
  148. token = ctx.Request.Header.Get("X-Csrftoken")
  149. }
  150. if token == "" {
  151. ctx.Abort(403, "'_xsrf' argument missing from POST")
  152. return false
  153. }
  154. if ctx._xsrfToken != token {
  155. ctx.Abort(403, "XSRF cookie does not match POST argument")
  156. return false
  157. }
  158. return true
  159. }
  160. // RenderMethodResult renders the return value of a controller method to the output
  161. func (ctx *Context) RenderMethodResult(result interface{}) {
  162. if result != nil {
  163. renderer, ok := result.(Renderer)
  164. if !ok {
  165. err, ok := result.(error)
  166. if ok {
  167. renderer = errorRenderer(err)
  168. } else {
  169. renderer = jsonRenderer(result)
  170. }
  171. }
  172. renderer.Render(ctx)
  173. }
  174. }
  175. //Response is a wrapper for the http.ResponseWriter
  176. //started set to true if response was written to then don't execute other handler
  177. type Response struct {
  178. http.ResponseWriter
  179. Started bool
  180. Status int
  181. }
  182. func (r *Response) reset(rw http.ResponseWriter) {
  183. r.ResponseWriter = rw
  184. r.Status = 0
  185. r.Started = false
  186. }
  187. // Write writes the data to the connection as part of an HTTP reply,
  188. // and sets `started` to true.
  189. // started means the response has sent out.
  190. func (r *Response) Write(p []byte) (int, error) {
  191. r.Started = true
  192. return r.ResponseWriter.Write(p)
  193. }
  194. // WriteHeader sends an HTTP response header with status code,
  195. // and sets `started` to true.
  196. func (r *Response) WriteHeader(code int) {
  197. if r.Status > 0 {
  198. //prevent multiple response.WriteHeader calls
  199. return
  200. }
  201. r.Status = code
  202. r.Started = true
  203. r.ResponseWriter.WriteHeader(code)
  204. }
  205. // Hijack hijacker for http
  206. func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
  207. hj, ok := r.ResponseWriter.(http.Hijacker)
  208. if !ok {
  209. return nil, nil, errors.New("webserver doesn't support hijacking")
  210. }
  211. return hj.Hijack()
  212. }
  213. // Flush http.Flusher
  214. func (r *Response) Flush() {
  215. if f, ok := r.ResponseWriter.(http.Flusher); ok {
  216. f.Flush()
  217. }
  218. }
  219. // CloseNotify http.CloseNotifier
  220. func (r *Response) CloseNotify() <-chan bool {
  221. if cn, ok := r.ResponseWriter.(http.CloseNotifier); ok {
  222. return cn.CloseNotify()
  223. }
  224. return nil
  225. }
  226. // Pusher http.Pusher
  227. func (r *Response) Pusher() (pusher http.Pusher) {
  228. if pusher, ok := r.ResponseWriter.(http.Pusher); ok {
  229. return pusher
  230. }
  231. return nil
  232. }