context.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. // Copyright 2014 The Macaron Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License"): you may
  4. // not use this file except in compliance with the License. You may obtain
  5. // 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, WITHOUT
  11. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. // License for the specific language governing permissions and limitations
  13. // under the License.
  14. package macaron
  15. import (
  16. "crypto/md5"
  17. "encoding/hex"
  18. "html/template"
  19. "io"
  20. "io/ioutil"
  21. "mime/multipart"
  22. "net/http"
  23. "net/url"
  24. "os"
  25. "path"
  26. "path/filepath"
  27. "reflect"
  28. "strconv"
  29. "strings"
  30. "time"
  31. "github.com/Unknwon/com"
  32. "github.com/go-macaron/inject"
  33. )
  34. // Locale reprents a localization interface.
  35. type Locale interface {
  36. Language() string
  37. Tr(string, ...interface{}) string
  38. }
  39. // RequestBody represents a request body.
  40. type RequestBody struct {
  41. reader io.ReadCloser
  42. }
  43. // Bytes reads and returns content of request body in bytes.
  44. func (rb *RequestBody) Bytes() ([]byte, error) {
  45. return ioutil.ReadAll(rb.reader)
  46. }
  47. // String reads and returns content of request body in string.
  48. func (rb *RequestBody) String() (string, error) {
  49. data, err := rb.Bytes()
  50. return string(data), err
  51. }
  52. // ReadCloser returns a ReadCloser for request body.
  53. func (rb *RequestBody) ReadCloser() io.ReadCloser {
  54. return rb.reader
  55. }
  56. // Request represents an HTTP request received by a server or to be sent by a client.
  57. type Request struct {
  58. *http.Request
  59. }
  60. func (r *Request) Body() *RequestBody {
  61. return &RequestBody{r.Request.Body}
  62. }
  63. // ContextInvoker is an inject.FastInvoker wrapper of func(ctx *Context).
  64. type ContextInvoker func(ctx *Context)
  65. func (invoke ContextInvoker) Invoke(params []interface{}) ([]reflect.Value, error) {
  66. invoke(params[0].(*Context))
  67. return nil, nil
  68. }
  69. // Context represents the runtime context of current request of Macaron instance.
  70. // It is the integration of most frequently used middlewares and helper methods.
  71. type Context struct {
  72. inject.Injector
  73. handlers []Handler
  74. action Handler
  75. index int
  76. *Router
  77. Req Request
  78. Resp ResponseWriter
  79. params Params
  80. Render
  81. Locale
  82. Data map[string]interface{}
  83. }
  84. func (c *Context) handler() Handler {
  85. if c.index < len(c.handlers) {
  86. return c.handlers[c.index]
  87. }
  88. if c.index == len(c.handlers) {
  89. return c.action
  90. }
  91. panic("invalid index for context handler")
  92. }
  93. func (c *Context) Next() {
  94. c.index += 1
  95. c.run()
  96. }
  97. func (c *Context) Written() bool {
  98. return c.Resp.Written()
  99. }
  100. func (c *Context) run() {
  101. for c.index <= len(c.handlers) {
  102. vals, err := c.Invoke(c.handler())
  103. if err != nil {
  104. panic(err)
  105. }
  106. c.index += 1
  107. // if the handler returned something, write it to the http response
  108. if len(vals) > 0 {
  109. ev := c.GetVal(reflect.TypeOf(ReturnHandler(nil)))
  110. handleReturn := ev.Interface().(ReturnHandler)
  111. handleReturn(c, vals)
  112. }
  113. if c.Written() {
  114. return
  115. }
  116. }
  117. }
  118. // RemoteAddr returns more real IP address.
  119. func (ctx *Context) RemoteAddr() string {
  120. addr := ctx.Req.Header.Get("X-Real-IP")
  121. if len(addr) == 0 {
  122. addr = ctx.Req.Header.Get("X-Forwarded-For")
  123. if addr == "" {
  124. addr = ctx.Req.RemoteAddr
  125. if i := strings.LastIndex(addr, ":"); i > -1 {
  126. addr = addr[:i]
  127. }
  128. }
  129. }
  130. return addr
  131. }
  132. func (ctx *Context) renderHTML(status int, setName, tplName string, data ...interface{}) {
  133. if len(data) <= 0 {
  134. ctx.Render.HTMLSet(status, setName, tplName, ctx.Data)
  135. } else if len(data) == 1 {
  136. ctx.Render.HTMLSet(status, setName, tplName, data[0])
  137. } else {
  138. ctx.Render.HTMLSet(status, setName, tplName, data[0], data[1].(HTMLOptions))
  139. }
  140. }
  141. // HTML calls Render.HTML but allows less arguments.
  142. func (ctx *Context) HTML(status int, name string, data ...interface{}) {
  143. ctx.renderHTML(status, DEFAULT_TPL_SET_NAME, name, data...)
  144. }
  145. // HTML calls Render.HTMLSet but allows less arguments.
  146. func (ctx *Context) HTMLSet(status int, setName, tplName string, data ...interface{}) {
  147. ctx.renderHTML(status, setName, tplName, data...)
  148. }
  149. func (ctx *Context) Redirect(location string, status ...int) {
  150. code := http.StatusFound
  151. if len(status) == 1 {
  152. code = status[0]
  153. }
  154. http.Redirect(ctx.Resp, ctx.Req.Request, location, code)
  155. }
  156. // Maximum amount of memory to use when parsing a multipart form.
  157. // Set this to whatever value you prefer; default is 10 MB.
  158. var MaxMemory = int64(1024 * 1024 * 10)
  159. func (ctx *Context) parseForm() {
  160. if ctx.Req.Form != nil {
  161. return
  162. }
  163. contentType := ctx.Req.Header.Get(_CONTENT_TYPE)
  164. if (ctx.Req.Method == "POST" || ctx.Req.Method == "PUT") &&
  165. len(contentType) > 0 && strings.Contains(contentType, "multipart/form-data") {
  166. ctx.Req.ParseMultipartForm(MaxMemory)
  167. } else {
  168. ctx.Req.ParseForm()
  169. }
  170. }
  171. // Query querys form parameter.
  172. func (ctx *Context) Query(name string) string {
  173. ctx.parseForm()
  174. return ctx.Req.Form.Get(name)
  175. }
  176. // QueryTrim querys and trims spaces form parameter.
  177. func (ctx *Context) QueryTrim(name string) string {
  178. return strings.TrimSpace(ctx.Query(name))
  179. }
  180. // QueryStrings returns a list of results by given query name.
  181. func (ctx *Context) QueryStrings(name string) []string {
  182. ctx.parseForm()
  183. vals, ok := ctx.Req.Form[name]
  184. if !ok {
  185. return []string{}
  186. }
  187. return vals
  188. }
  189. // QueryEscape returns escapred query result.
  190. func (ctx *Context) QueryEscape(name string) string {
  191. return template.HTMLEscapeString(ctx.Query(name))
  192. }
  193. // QueryBool returns query result in bool type.
  194. func (ctx *Context) QueryBool(name string) bool {
  195. v, _ := strconv.ParseBool(ctx.Query(name))
  196. return v
  197. }
  198. // QueryInt returns query result in int type.
  199. func (ctx *Context) QueryInt(name string) int {
  200. return com.StrTo(ctx.Query(name)).MustInt()
  201. }
  202. // QueryInt64 returns query result in int64 type.
  203. func (ctx *Context) QueryInt64(name string) int64 {
  204. return com.StrTo(ctx.Query(name)).MustInt64()
  205. }
  206. // QueryFloat64 returns query result in float64 type.
  207. func (ctx *Context) QueryFloat64(name string) float64 {
  208. v, _ := strconv.ParseFloat(ctx.Query(name), 64)
  209. return v
  210. }
  211. // Params returns value of given param name.
  212. // e.g. ctx.Params(":uid") or ctx.Params("uid")
  213. func (ctx *Context) Params(name string) string {
  214. if len(name) == 0 {
  215. return ""
  216. }
  217. if len(name) > 1 && name[0] != ':' {
  218. name = ":" + name
  219. }
  220. return ctx.params[name]
  221. }
  222. // SetParams sets value of param with given name.
  223. func (ctx *Context) SetParams(name, val string) {
  224. if !strings.HasPrefix(name, ":") {
  225. name = ":" + name
  226. }
  227. ctx.params[name] = val
  228. }
  229. // ParamsEscape returns escapred params result.
  230. // e.g. ctx.ParamsEscape(":uname")
  231. func (ctx *Context) ParamsEscape(name string) string {
  232. return template.HTMLEscapeString(ctx.Params(name))
  233. }
  234. // ParamsInt returns params result in int type.
  235. // e.g. ctx.ParamsInt(":uid")
  236. func (ctx *Context) ParamsInt(name string) int {
  237. return com.StrTo(ctx.Params(name)).MustInt()
  238. }
  239. // ParamsInt64 returns params result in int64 type.
  240. // e.g. ctx.ParamsInt64(":uid")
  241. func (ctx *Context) ParamsInt64(name string) int64 {
  242. return com.StrTo(ctx.Params(name)).MustInt64()
  243. }
  244. // ParamsFloat64 returns params result in int64 type.
  245. // e.g. ctx.ParamsFloat64(":uid")
  246. func (ctx *Context) ParamsFloat64(name string) float64 {
  247. v, _ := strconv.ParseFloat(ctx.Params(name), 64)
  248. return v
  249. }
  250. // GetFile returns information about user upload file by given form field name.
  251. func (ctx *Context) GetFile(name string) (multipart.File, *multipart.FileHeader, error) {
  252. return ctx.Req.FormFile(name)
  253. }
  254. // SaveToFile reads a file from request by field name and saves to given path.
  255. func (ctx *Context) SaveToFile(name, savePath string) error {
  256. fr, _, err := ctx.GetFile(name)
  257. if err != nil {
  258. return err
  259. }
  260. defer fr.Close()
  261. fw, err := os.OpenFile(savePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
  262. if err != nil {
  263. return err
  264. }
  265. defer fw.Close()
  266. _, err = io.Copy(fw, fr)
  267. return err
  268. }
  269. // SetCookie sets given cookie value to response header.
  270. // FIXME: IE support? http://golanghome.com/post/620#reply2
  271. func (ctx *Context) SetCookie(name string, value string, others ...interface{}) {
  272. cookie := http.Cookie{}
  273. cookie.Name = name
  274. cookie.Value = url.QueryEscape(value)
  275. if len(others) > 0 {
  276. switch v := others[0].(type) {
  277. case int:
  278. cookie.MaxAge = v
  279. case int64:
  280. cookie.MaxAge = int(v)
  281. case int32:
  282. cookie.MaxAge = int(v)
  283. }
  284. }
  285. cookie.Path = "/"
  286. if len(others) > 1 {
  287. if v, ok := others[1].(string); ok && len(v) > 0 {
  288. cookie.Path = v
  289. }
  290. }
  291. if len(others) > 2 {
  292. if v, ok := others[2].(string); ok && len(v) > 0 {
  293. cookie.Domain = v
  294. }
  295. }
  296. if len(others) > 3 {
  297. switch v := others[3].(type) {
  298. case bool:
  299. cookie.Secure = v
  300. default:
  301. if others[3] != nil {
  302. cookie.Secure = true
  303. }
  304. }
  305. }
  306. if len(others) > 4 {
  307. if v, ok := others[4].(bool); ok && v {
  308. cookie.HttpOnly = true
  309. }
  310. }
  311. if len(others) > 5 {
  312. if v, ok := others[5].(time.Time); ok {
  313. cookie.Expires = v
  314. cookie.RawExpires = v.Format(time.UnixDate)
  315. }
  316. }
  317. ctx.Resp.Header().Add("Set-Cookie", cookie.String())
  318. }
  319. // GetCookie returns given cookie value from request header.
  320. func (ctx *Context) GetCookie(name string) string {
  321. cookie, err := ctx.Req.Cookie(name)
  322. if err != nil {
  323. return ""
  324. }
  325. val, _ := url.QueryUnescape(cookie.Value)
  326. return val
  327. }
  328. // GetCookieInt returns cookie result in int type.
  329. func (ctx *Context) GetCookieInt(name string) int {
  330. return com.StrTo(ctx.GetCookie(name)).MustInt()
  331. }
  332. // GetCookieInt64 returns cookie result in int64 type.
  333. func (ctx *Context) GetCookieInt64(name string) int64 {
  334. return com.StrTo(ctx.GetCookie(name)).MustInt64()
  335. }
  336. // GetCookieFloat64 returns cookie result in float64 type.
  337. func (ctx *Context) GetCookieFloat64(name string) float64 {
  338. v, _ := strconv.ParseFloat(ctx.GetCookie(name), 64)
  339. return v
  340. }
  341. var defaultCookieSecret string
  342. // SetDefaultCookieSecret sets global default secure cookie secret.
  343. func (m *Macaron) SetDefaultCookieSecret(secret string) {
  344. defaultCookieSecret = secret
  345. }
  346. // SetSecureCookie sets given cookie value to response header with default secret string.
  347. func (ctx *Context) SetSecureCookie(name, value string, others ...interface{}) {
  348. ctx.SetSuperSecureCookie(defaultCookieSecret, name, value, others...)
  349. }
  350. // GetSecureCookie returns given cookie value from request header with default secret string.
  351. func (ctx *Context) GetSecureCookie(key string) (string, bool) {
  352. return ctx.GetSuperSecureCookie(defaultCookieSecret, key)
  353. }
  354. // SetSuperSecureCookie sets given cookie value to response header with secret string.
  355. func (ctx *Context) SetSuperSecureCookie(secret, name, value string, others ...interface{}) {
  356. m := md5.Sum([]byte(secret))
  357. secret = hex.EncodeToString(m[:])
  358. text, err := com.AESEncrypt([]byte(secret), []byte(value))
  359. if err != nil {
  360. panic("error encrypting cookie: " + err.Error())
  361. }
  362. ctx.SetCookie(name, hex.EncodeToString(text), others...)
  363. }
  364. // GetSuperSecureCookie returns given cookie value from request header with secret string.
  365. func (ctx *Context) GetSuperSecureCookie(secret, key string) (string, bool) {
  366. val := ctx.GetCookie(key)
  367. if val == "" {
  368. return "", false
  369. }
  370. data, err := hex.DecodeString(val)
  371. if err != nil {
  372. return "", false
  373. }
  374. m := md5.Sum([]byte(secret))
  375. secret = hex.EncodeToString(m[:])
  376. text, err := com.AESDecrypt([]byte(secret), data)
  377. return string(text), err == nil
  378. }
  379. func (ctx *Context) setRawContentHeader() {
  380. ctx.Resp.Header().Set("Content-Description", "Raw content")
  381. ctx.Resp.Header().Set("Content-Type", "text/plain")
  382. ctx.Resp.Header().Set("Expires", "0")
  383. ctx.Resp.Header().Set("Cache-Control", "must-revalidate")
  384. ctx.Resp.Header().Set("Pragma", "public")
  385. }
  386. // ServeContent serves given content to response.
  387. func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) {
  388. modtime := time.Now()
  389. for _, p := range params {
  390. switch v := p.(type) {
  391. case time.Time:
  392. modtime = v
  393. }
  394. }
  395. ctx.setRawContentHeader()
  396. http.ServeContent(ctx.Resp, ctx.Req.Request, name, modtime, r)
  397. }
  398. // ServeFileContent serves given file as content to response.
  399. func (ctx *Context) ServeFileContent(file string, names ...string) {
  400. var name string
  401. if len(names) > 0 {
  402. name = names[0]
  403. } else {
  404. name = path.Base(file)
  405. }
  406. f, err := os.Open(file)
  407. if err != nil {
  408. if Env == PROD {
  409. http.Error(ctx.Resp, "Internal Server Error", 500)
  410. } else {
  411. http.Error(ctx.Resp, err.Error(), 500)
  412. }
  413. return
  414. }
  415. defer f.Close()
  416. ctx.setRawContentHeader()
  417. http.ServeContent(ctx.Resp, ctx.Req.Request, name, time.Now(), f)
  418. }
  419. // ServeFile serves given file to response.
  420. func (ctx *Context) ServeFile(file string, names ...string) {
  421. var name string
  422. if len(names) > 0 {
  423. name = names[0]
  424. } else {
  425. name = path.Base(file)
  426. }
  427. ctx.Resp.Header().Set("Content-Description", "File Transfer")
  428. ctx.Resp.Header().Set("Content-Type", "application/octet-stream")
  429. ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+name)
  430. ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary")
  431. ctx.Resp.Header().Set("Expires", "0")
  432. ctx.Resp.Header().Set("Cache-Control", "must-revalidate")
  433. ctx.Resp.Header().Set("Pragma", "public")
  434. http.ServeFile(ctx.Resp, ctx.Req.Request, file)
  435. }
  436. // ChangeStaticPath changes static path from old to new one.
  437. func (ctx *Context) ChangeStaticPath(oldPath, newPath string) {
  438. if !filepath.IsAbs(oldPath) {
  439. oldPath = filepath.Join(Root, oldPath)
  440. }
  441. dir := statics.Get(oldPath)
  442. if dir != nil {
  443. statics.Delete(oldPath)
  444. if !filepath.IsAbs(newPath) {
  445. newPath = filepath.Join(Root, newPath)
  446. }
  447. *dir = http.Dir(newPath)
  448. statics.Set(dir)
  449. }
  450. }