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