ctx.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831
  1. // ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
  2. // 🤖 GitHub Repository: https://github.com/gofiber/fiber
  3. // 📌 API Documentation: https://docs.gofiber.io
  4. package fiber
  5. import (
  6. "context"
  7. "crypto/tls"
  8. "fmt"
  9. "io"
  10. "maps"
  11. "mime/multipart"
  12. "strings"
  13. "sync/atomic"
  14. "time"
  15. "github.com/gofiber/utils/v2"
  16. utilsbytes "github.com/gofiber/utils/v2/bytes"
  17. "github.com/valyala/bytebufferpool"
  18. "github.com/valyala/fasthttp"
  19. )
  20. const (
  21. schemeHTTP = "http"
  22. schemeHTTPS = "https"
  23. )
  24. const (
  25. // maxParams defines the maximum number of parameters per route.
  26. maxParams = 30
  27. maxDetectionPaths = 3
  28. )
  29. var (
  30. _ io.Writer = (*DefaultCtx)(nil) // Compile-time check
  31. _ context.Context = (*DefaultCtx)(nil) // Compile-time check
  32. )
  33. // The contextKey type is unexported to prevent collisions with context keys defined in
  34. // other packages.
  35. type contextKey int
  36. // userContextKey define the key name for storing context.Context in *fasthttp.RequestCtx
  37. const (
  38. userContextKey contextKey = iota // __local_user_context__
  39. )
  40. // DefaultCtx is the default implementation of the Ctx interface
  41. // generation tool `go install github.com/vburenin/ifacemaker@f30b6f9bdbed4b5c4804ec9ba4a04a999525c202`
  42. // https://github.com/vburenin/ifacemaker/blob/f30b6f9bdbed4b5c4804ec9ba4a04a999525c202/ifacemaker.go#L14-L31
  43. //
  44. //go:generate ifacemaker --file ctx.go --file req.go --file res.go --struct DefaultCtx --iface Ctx --pkg fiber --promoted --output ctx_interface_gen.go --not-exported true --iface-comment "Ctx represents the Context which hold the HTTP request and response.\nIt has methods for the request query string, parameters, body, HTTP headers and so on."
  45. type DefaultCtx struct {
  46. handlerCtx CustomCtx // Active custom context implementation, if any
  47. DefaultReq // Default request api
  48. DefaultRes // Default response api
  49. app *App // Reference to *App
  50. route *Route // Reference to *Route
  51. fasthttp *fasthttp.RequestCtx // Reference to *fasthttp.RequestCtx
  52. bind *Bind // Default bind reference
  53. redirect *Redirect // Default redirect reference
  54. viewBindMap Map // Default view map to bind template engine
  55. values [maxParams]string // Route parameter values
  56. baseURI string // HTTP base uri
  57. pathOriginal string // Original HTTP path
  58. flashMessages redirectionMsgs // Flash messages
  59. path []byte // HTTP path with the modifications by the configuration
  60. detectionPath []byte // Route detection path
  61. treePathHash int // Hash of the path for the search in the tree
  62. indexRoute int // Index of the current route
  63. indexHandler int // Index of the current handler
  64. methodInt int // HTTP method INT equivalent
  65. abandoned atomic.Bool // If true, ctx won't be pooled until ForceRelease is called
  66. matched bool // Non use route matched
  67. skipNonUseRoutes bool // Skip non-use routes while iterating middleware
  68. }
  69. // TLSHandler hosts the callback hooks Fiber invokes while negotiating TLS
  70. // connections, including optional client certificate lookups.
  71. type TLSHandler struct {
  72. clientHelloInfo *tls.ClientHelloInfo
  73. }
  74. // GetClientInfo Callback function to set ClientHelloInfo
  75. // Must comply with the method structure of https://cs.opensource.google/go/go/+/refs/tags/go1.20:src/crypto/tls/common.go;l=554-563
  76. // Since we overlay the method of the TLS config in the listener method
  77. func (t *TLSHandler) GetClientInfo(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
  78. t.clientHelloInfo = info
  79. return nil, nil //nolint:nilnil // Not returning anything useful here is probably fine
  80. }
  81. // Views is the interface that wraps the Render function.
  82. type Views interface {
  83. Load() error
  84. Render(out io.Writer, name string, binding any, layout ...string) error
  85. }
  86. // App returns the *App reference to the instance of the Fiber application
  87. func (c *DefaultCtx) App() *App {
  88. return c.app
  89. }
  90. // BaseURL returns (protocol + host + base path).
  91. func (c *DefaultCtx) BaseURL() string {
  92. // TODO: Could be improved: 53.8 ns/op 32 B/op 1 allocs/op
  93. // Should work like https://codeigniter.com/user_guide/helpers/url_helper.html
  94. if c.baseURI != "" {
  95. return c.baseURI
  96. }
  97. scheme := c.Scheme()
  98. host := c.Host()
  99. buf := make([]byte, 0, len(scheme)+len("://")+len(host))
  100. buf = append(buf, scheme...)
  101. buf = append(buf, "://"...)
  102. buf = append(buf, host...)
  103. c.baseURI = c.app.toString(buf)
  104. return c.baseURI
  105. }
  106. // RequestCtx returns *fasthttp.RequestCtx that carries a deadline
  107. // a cancellation signal, and other values across API boundaries.
  108. func (c *DefaultCtx) RequestCtx() *fasthttp.RequestCtx {
  109. return c.fasthttp
  110. }
  111. // Context returns a context implementation that was set by
  112. // user earlier or returns a non-nil, empty context, if it was not set earlier.
  113. func (c *DefaultCtx) Context() context.Context {
  114. if c.fasthttp == nil {
  115. return context.Background()
  116. }
  117. if ctx, ok := c.fasthttp.UserValue(userContextKey).(context.Context); ok && ctx != nil {
  118. return ctx
  119. }
  120. ctx := context.Background()
  121. c.SetContext(ctx)
  122. return ctx
  123. }
  124. // SetContext sets a context implementation by user.
  125. func (c *DefaultCtx) SetContext(ctx context.Context) {
  126. if c.fasthttp == nil {
  127. return
  128. }
  129. c.fasthttp.SetUserValue(userContextKey, ctx)
  130. }
  131. // Deadline returns the time when work done on behalf of this context
  132. // should be canceled. Deadline returns ok==false when no deadline is
  133. // set. Successive calls to Deadline return the same results.
  134. //
  135. // Due to current limitations in how fasthttp works, Deadline operates as a nop.
  136. // See: https://github.com/valyala/fasthttp/issues/965#issuecomment-777268945
  137. func (*DefaultCtx) Deadline() (time.Time, bool) {
  138. return time.Time{}, false
  139. }
  140. // Done returns a channel that's closed when work done on behalf of this
  141. // context should be canceled. Done may return nil if this context can
  142. // never be canceled. Successive calls to Done return the same value.
  143. // The close of the Done channel may happen asynchronously,
  144. // after the cancel function returns.
  145. //
  146. // Due to current limitations in how fasthttp works, Done operates as a nop.
  147. // See: https://github.com/valyala/fasthttp/issues/965#issuecomment-777268945
  148. func (*DefaultCtx) Done() <-chan struct{} {
  149. return nil
  150. }
  151. // Err mirrors context.Err, returning nil until cancellation and then the terminal error value.
  152. //
  153. // Due to current limitations in how fasthttp works, Err operates as a nop.
  154. // See: https://github.com/valyala/fasthttp/issues/965#issuecomment-777268945
  155. func (*DefaultCtx) Err() error {
  156. return nil
  157. }
  158. // Request return the *fasthttp.Request object
  159. // This allows you to use all fasthttp request methods
  160. // https://godoc.org/github.com/valyala/fasthttp#Request
  161. // Returns nil if the context has been released.
  162. func (c *DefaultCtx) Request() *fasthttp.Request {
  163. if c.fasthttp == nil {
  164. return nil
  165. }
  166. return &c.fasthttp.Request
  167. }
  168. // Response return the *fasthttp.Response object
  169. // This allows you to use all fasthttp response methods
  170. // https://godoc.org/github.com/valyala/fasthttp#Response
  171. // Returns nil if the context has been released.
  172. func (c *DefaultCtx) Response() *fasthttp.Response {
  173. if c.fasthttp == nil {
  174. return nil
  175. }
  176. return &c.fasthttp.Response
  177. }
  178. // Get returns the HTTP request header specified by field.
  179. // Field names are case-insensitive
  180. // Returned value is only valid within the handler. Do not store any references.
  181. // Make copies or use the Immutable setting instead.
  182. func (c *DefaultCtx) Get(key string, defaultValue ...string) string {
  183. return c.DefaultReq.Get(key, defaultValue...)
  184. }
  185. // GetHeaders returns the HTTP request headers.
  186. // Returned value is only valid within the handler. Do not store any references.
  187. // Make copies or use the Immutable setting instead.
  188. func (c *DefaultCtx) GetHeaders() map[string][]string {
  189. return c.DefaultReq.GetHeaders()
  190. }
  191. // GetReqHeaders returns the HTTP request headers.
  192. // Returned value is only valid within the handler. Do not store any references.
  193. // Make copies or use the Immutable setting instead.
  194. func (c *DefaultCtx) GetReqHeaders() map[string][]string {
  195. return c.DefaultReq.GetHeaders()
  196. }
  197. // GetRespHeader returns the HTTP response header specified by field.
  198. // Field names are case-insensitive
  199. // Returned value is only valid within the handler. Do not store any references.
  200. // Make copies or use the Immutable setting instead.
  201. func (c *DefaultCtx) GetRespHeader(key string, defaultValue ...string) string {
  202. return c.DefaultRes.Get(key, defaultValue...)
  203. }
  204. // GetRespHeaders returns the HTTP response headers.
  205. // Returned value is only valid within the handler. Do not store any references.
  206. // Make copies or use the Immutable setting instead.
  207. func (c *DefaultCtx) GetRespHeaders() map[string][]string {
  208. return c.DefaultRes.GetHeaders()
  209. }
  210. // ClientHelloInfo return CHI from context
  211. func (c *DefaultCtx) ClientHelloInfo() *tls.ClientHelloInfo {
  212. if c.app.tlsHandler != nil {
  213. return c.app.tlsHandler.clientHelloInfo
  214. }
  215. return nil
  216. }
  217. // Next executes the next method in the stack that matches the current route.
  218. func (c *DefaultCtx) Next() error {
  219. // Increment handler index
  220. c.indexHandler++
  221. // Did we execute all route handlers?
  222. if c.indexHandler < len(c.route.Handlers) {
  223. if c.handlerCtx != nil {
  224. return c.route.Handlers[c.indexHandler](c.handlerCtx)
  225. }
  226. return c.route.Handlers[c.indexHandler](c)
  227. }
  228. if c.handlerCtx != nil {
  229. _, err := c.app.nextCustom(c.handlerCtx)
  230. return err
  231. }
  232. _, err := c.app.next(c)
  233. return err
  234. }
  235. // RestartRouting instead of going to the next handler. This may be useful after
  236. // changing the request path. Note that handlers might be executed again.
  237. func (c *DefaultCtx) RestartRouting() error {
  238. c.indexRoute = -1
  239. if c.handlerCtx != nil {
  240. _, err := c.app.nextCustom(c.handlerCtx)
  241. return err
  242. }
  243. _, err := c.app.next(c)
  244. return err
  245. }
  246. func (c *DefaultCtx) setHandlerCtx(ctx CustomCtx) {
  247. if ctx == nil {
  248. c.handlerCtx = nil
  249. return
  250. }
  251. if defaultCtx, ok := ctx.(*DefaultCtx); ok && defaultCtx == c {
  252. c.handlerCtx = nil
  253. return
  254. }
  255. c.handlerCtx = ctx
  256. }
  257. // OriginalURL contains the original request URL.
  258. // Returned value is only valid within the handler. Do not store any references.
  259. // Make copies or use the Immutable setting to use the value outside the Handler.
  260. func (c *DefaultCtx) OriginalURL() string {
  261. return c.app.toString(c.fasthttp.Request.Header.RequestURI())
  262. }
  263. // Path returns the path part of the request URL.
  264. // Optionally, you could override the path.
  265. // Make copies or use the Immutable setting to use the value outside the Handler.
  266. func (c *DefaultCtx) Path(override ...string) string {
  267. if len(override) != 0 && string(c.path) != override[0] {
  268. // Set new path to context
  269. c.pathOriginal = override[0]
  270. // Set new path to request context
  271. c.fasthttp.Request.URI().SetPath(c.pathOriginal)
  272. // Prettify path
  273. c.configDependentPaths()
  274. }
  275. return c.app.toString(c.path)
  276. }
  277. // RequestID returns the request identifier from the response header or request header.
  278. func (c *DefaultCtx) RequestID() string {
  279. if requestID := c.GetRespHeader(HeaderXRequestID); requestID != "" {
  280. return requestID
  281. }
  282. return c.Get(HeaderXRequestID)
  283. }
  284. // Req returns a convenience type whose API is limited to operations
  285. // on the incoming request.
  286. func (c *DefaultCtx) Req() Req {
  287. return &c.DefaultReq
  288. }
  289. // Res returns a convenience type whose API is limited to operations
  290. // on the outgoing response.
  291. func (c *DefaultCtx) Res() Res {
  292. return &c.DefaultRes
  293. }
  294. // Redirect returns the Redirect reference.
  295. // Use Redirect().Status() to set custom redirection status code.
  296. // If status is not specified, status defaults to 303 See Other.
  297. // You can use Redirect().To(), Redirect().Route() and Redirect().Back() for redirection.
  298. func (c *DefaultCtx) Redirect() *Redirect {
  299. if c.redirect == nil {
  300. c.redirect = AcquireRedirect()
  301. c.redirect.c = c
  302. }
  303. return c.redirect
  304. }
  305. // ViewBind Add vars to default view var map binding to template engine.
  306. // Variables are read by the Render method and may be overwritten.
  307. func (c *DefaultCtx) ViewBind(vars Map) error {
  308. // init viewBindMap - lazy map
  309. if c.viewBindMap == nil {
  310. c.viewBindMap = make(Map, len(vars))
  311. }
  312. maps.Copy(c.viewBindMap, vars)
  313. return nil
  314. }
  315. // Route returns the matched Route struct.
  316. func (c *DefaultCtx) Route() *Route {
  317. if c.route == nil {
  318. // Fallback for fasthttp error handler
  319. return &Route{
  320. path: c.pathOriginal,
  321. Path: c.pathOriginal,
  322. Method: c.Method(),
  323. Handlers: make([]Handler, 0),
  324. Params: make([]string, 0),
  325. }
  326. }
  327. return c.route
  328. }
  329. // FullPath returns the matched route path, including any group prefixes.
  330. func (c *DefaultCtx) FullPath() string {
  331. return c.Route().Path
  332. }
  333. // Matched returns true if the current request path was matched by the router.
  334. func (c *DefaultCtx) Matched() bool {
  335. return c.getMatched()
  336. }
  337. // IsMiddleware returns true if the current request handler was registered as middleware.
  338. func (c *DefaultCtx) IsMiddleware() bool {
  339. if c.route == nil {
  340. return false
  341. }
  342. if c.route.use {
  343. return true
  344. }
  345. // For route-level middleware, there will be a next handler in the chain
  346. return c.indexHandler+1 < len(c.route.Handlers)
  347. }
  348. // HasBody returns true if the request declares a body via Content-Length, Transfer-Encoding, or already buffered payload data.
  349. func (c *DefaultCtx) HasBody() bool {
  350. hdr := &c.fasthttp.Request.Header
  351. //nolint:revive // switch is exhaustive for all ContentLength() cases
  352. switch cl := hdr.ContentLength(); {
  353. case cl > 0:
  354. return true
  355. case cl == -1:
  356. // fasthttp reports -1 for Transfer-Encoding: chunked bodies.
  357. return true
  358. case cl == 0:
  359. if hasTransferEncodingBody(hdr) {
  360. return true
  361. }
  362. }
  363. return len(c.fasthttp.Request.Body()) > 0
  364. }
  365. // OverrideParam overwrites a route parameter value by name.
  366. // If the parameter name does not exist in the route, this method does nothing.
  367. func (c *DefaultCtx) OverrideParam(name, value string) {
  368. // If no route is matched, there are no parameters to update
  369. if !c.Matched() {
  370. return
  371. }
  372. // Normalize wildcard (*) and plus (+) tokens to their internal
  373. // representations (*1, +1) used by the router.
  374. if name == "*" || name == "+" {
  375. name += "1"
  376. }
  377. if c.app.config.CaseSensitive {
  378. for i, param := range c.route.Params {
  379. if param == name {
  380. c.values[i] = value
  381. return
  382. }
  383. }
  384. return
  385. }
  386. nameBytes := utils.UnsafeBytes(name)
  387. for i, param := range c.route.Params {
  388. if utils.EqualFold(utils.UnsafeBytes(param), nameBytes) {
  389. c.values[i] = value
  390. return
  391. }
  392. }
  393. }
  394. func hasTransferEncodingBody(hdr *fasthttp.RequestHeader) bool {
  395. teBytes := hdr.Peek(HeaderTransferEncoding)
  396. var te string
  397. if len(teBytes) > 0 {
  398. te = utils.UnsafeString(teBytes)
  399. } else {
  400. for key, value := range hdr.All() {
  401. if !utils.EqualFold(utils.UnsafeString(key), HeaderTransferEncoding) {
  402. continue
  403. }
  404. te = utils.UnsafeString(value)
  405. break
  406. }
  407. }
  408. if te == "" {
  409. return false
  410. }
  411. hasEncoding := false
  412. for raw := range strings.SplitSeq(te, ",") {
  413. token := utils.TrimSpace(raw)
  414. if token == "" {
  415. continue
  416. }
  417. if idx := strings.IndexByte(token, ';'); idx >= 0 {
  418. token = utils.TrimSpace(token[:idx])
  419. }
  420. if token == "" {
  421. continue
  422. }
  423. if utils.EqualFold(token, "identity") {
  424. continue
  425. }
  426. hasEncoding = true
  427. }
  428. return hasEncoding
  429. }
  430. // IsWebSocket returns true if the request includes a WebSocket upgrade handshake.
  431. func (c *DefaultCtx) IsWebSocket() bool {
  432. conn := c.fasthttp.Request.Header.Peek(HeaderConnection)
  433. var isUpgrade bool
  434. for v := range strings.SplitSeq(utils.UnsafeString(conn), ",") {
  435. if utils.EqualFold(utils.TrimSpace(v), "upgrade") {
  436. isUpgrade = true
  437. break
  438. }
  439. }
  440. if !isUpgrade {
  441. return false
  442. }
  443. return utils.EqualFold(c.fasthttp.Request.Header.Peek(HeaderUpgrade), websocketBytes)
  444. }
  445. // IsPreflight returns true if the request is a CORS preflight.
  446. func (c *DefaultCtx) IsPreflight() bool {
  447. if c.Method() != MethodOptions {
  448. return false
  449. }
  450. hdr := &c.fasthttp.Request.Header
  451. if len(hdr.Peek(HeaderAccessControlRequestMethod)) == 0 {
  452. return false
  453. }
  454. return len(hdr.Peek(HeaderOrigin)) > 0
  455. }
  456. // SaveFile saves any multipart file to disk.
  457. func (*DefaultCtx) SaveFile(fileheader *multipart.FileHeader, path string) error {
  458. return fasthttp.SaveMultipartFile(fileheader, path)
  459. }
  460. // SaveFileToStorage saves any multipart file to an external storage system.
  461. func (c *DefaultCtx) SaveFileToStorage(fileheader *multipart.FileHeader, path string, storage Storage) error {
  462. file, err := fileheader.Open()
  463. if err != nil {
  464. return fmt.Errorf("failed to open: %w", err)
  465. }
  466. defer file.Close() //nolint:errcheck // not needed
  467. maxUploadSize := c.app.config.BodyLimit
  468. if maxUploadSize <= 0 {
  469. maxUploadSize = DefaultBodyLimit
  470. }
  471. if fileheader.Size > 0 && fileheader.Size > int64(maxUploadSize) {
  472. return fmt.Errorf("failed to read: %w", fasthttp.ErrBodyTooLarge)
  473. }
  474. buf := bytebufferpool.Get()
  475. defer bytebufferpool.Put(buf)
  476. limitedReader := io.LimitReader(file, int64(maxUploadSize)+1)
  477. if _, err = buf.ReadFrom(limitedReader); err != nil {
  478. return fmt.Errorf("failed to read: %w", err)
  479. }
  480. if buf.Len() > maxUploadSize {
  481. return fmt.Errorf("failed to read: %w", fasthttp.ErrBodyTooLarge)
  482. }
  483. data := append([]byte(nil), buf.Bytes()...)
  484. if err := storage.SetWithContext(c.Context(), path, data, 0); err != nil {
  485. return fmt.Errorf("failed to store: %w", err)
  486. }
  487. return nil
  488. }
  489. // Secure returns whether a secure connection was established.
  490. func (c *DefaultCtx) Secure() bool {
  491. return c.Protocol() == schemeHTTPS
  492. }
  493. // Status sets the HTTP status for the response.
  494. // This method is chainable.
  495. func (c *DefaultCtx) Status(status int) Ctx {
  496. c.fasthttp.Response.SetStatusCode(status)
  497. return c
  498. }
  499. // String returns unique string representation of the ctx.
  500. //
  501. // The returned value may be useful for logging.
  502. func (c *DefaultCtx) String() string {
  503. // Get buffer from pool
  504. buf := bytebufferpool.Get()
  505. // Start with the ID, converting it to a hex string without fmt.Sprintf
  506. buf.WriteByte('#')
  507. const hex = "0123456789abcdef"
  508. var id [16]byte
  509. ctxID := c.fasthttp.ID()
  510. for i := len(id) - 1; i >= 0; i-- {
  511. id[i] = hex[ctxID&0xf]
  512. ctxID >>= 4
  513. }
  514. buf.Write(id[:])
  515. buf.WriteString(" - ")
  516. // Add local and remote addresses directly
  517. buf.WriteString(c.fasthttp.LocalAddr().String())
  518. buf.WriteString(" <-> ")
  519. buf.WriteString(c.fasthttp.RemoteAddr().String())
  520. buf.WriteString(" - ")
  521. // Add method and URI
  522. buf.Write(c.fasthttp.Request.Header.Method())
  523. buf.WriteByte(' ')
  524. buf.Write(c.fasthttp.URI().FullURI())
  525. // Allocate string
  526. str := buf.String()
  527. // Reset buffer
  528. buf.Reset()
  529. bytebufferpool.Put(buf)
  530. return str
  531. }
  532. // Value makes it possible to retrieve values (Locals) under keys scoped to the request
  533. // and therefore available to all following routes that match the request. If the context
  534. // has been released and c.fasthttp is nil (for example, after ReleaseCtx), Value returns nil.
  535. func (c *DefaultCtx) Value(key any) any {
  536. if c.fasthttp == nil {
  537. return nil
  538. }
  539. return c.fasthttp.UserValue(key)
  540. }
  541. var (
  542. // xmlHTTPRequestBytes is precomputed for XHR detection
  543. xmlHTTPRequestBytes = []byte("xmlhttprequest")
  544. // websocketBytes is precomputed for WebSocket upgrade detection
  545. websocketBytes = []byte("websocket")
  546. )
  547. // XHR returns a Boolean property, that is true, if the request's X-Requested-With header field is XMLHttpRequest,
  548. // indicating that the request was issued by a client library (such as jQuery).
  549. func (c *DefaultCtx) XHR() bool {
  550. return utils.EqualFold(c.fasthttp.Request.Header.Peek(HeaderXRequestedWith), xmlHTTPRequestBytes)
  551. }
  552. // configDependentPaths set paths for route recognition and prepared paths for the user,
  553. // here the features for caseSensitive, decoded paths, strict paths are evaluated
  554. func (c *DefaultCtx) configDependentPaths() {
  555. c.path = append(c.path[:0], c.pathOriginal...)
  556. // If UnescapePath enabled, we decode the path and save it for the framework user
  557. if c.app.config.UnescapePath {
  558. c.path = fasthttp.AppendUnquotedArg(c.path[:0], c.path)
  559. }
  560. // another path is specified which is for routing recognition only
  561. // use the path that was changed by the previous configuration flags
  562. c.detectionPath = append(c.detectionPath[:0], c.path...)
  563. // If CaseSensitive is disabled, we lowercase the original path
  564. if !c.app.config.CaseSensitive {
  565. c.detectionPath = utilsbytes.UnsafeToLower(c.detectionPath)
  566. }
  567. // If StrictRouting is disabled, we strip all trailing slashes
  568. if !c.app.config.StrictRouting && len(c.detectionPath) > 1 && c.detectionPath[len(c.detectionPath)-1] == '/' {
  569. c.detectionPath = utils.TrimRight(c.detectionPath, '/')
  570. }
  571. // Define the path for dividing routes into areas for fast tree detection, so that fewer routes need to be traversed,
  572. // since the first three characters area select a list of routes
  573. c.treePathHash = 0
  574. if len(c.detectionPath) >= maxDetectionPaths {
  575. c.treePathHash = int(c.detectionPath[0])<<16 |
  576. int(c.detectionPath[1])<<8 |
  577. int(c.detectionPath[2])
  578. }
  579. }
  580. // Reset is a method to reset context fields by given request when to use server handlers.
  581. func (c *DefaultCtx) Reset(fctx *fasthttp.RequestCtx) {
  582. // Reset route and handler index
  583. c.indexRoute = -1
  584. c.indexHandler = 0
  585. // Reset matched flag
  586. c.matched = false
  587. c.skipNonUseRoutes = false
  588. // Set paths
  589. c.pathOriginal = c.app.toString(fctx.URI().PathOriginal())
  590. // Set method
  591. c.methodInt = c.app.methodInt(utils.UnsafeString(fctx.Request.Header.Method()))
  592. // Attach *fasthttp.RequestCtx to ctx
  593. c.fasthttp = fctx
  594. // reset base uri
  595. c.baseURI = ""
  596. // Prettify path
  597. c.configDependentPaths()
  598. c.DefaultReq.c = c
  599. c.DefaultRes.c = c
  600. c.fasthttp.SetUserValue(userContextKey, nil)
  601. }
  602. // release is a method to reset context fields when to use ReleaseCtx()
  603. func (c *DefaultCtx) release() {
  604. c.route = nil
  605. c.fasthttp = nil
  606. if c.bind != nil {
  607. ReleaseBind(c.bind)
  608. c.bind = nil
  609. }
  610. c.flashMessages = c.flashMessages[:0]
  611. // Clear viewBindMap by deleting all keys (reuse underlying map if possible)
  612. clear(c.viewBindMap)
  613. if c.redirect != nil {
  614. ReleaseRedirect(c.redirect)
  615. c.redirect = nil
  616. }
  617. c.skipNonUseRoutes = false
  618. // performance: no need for using c.abandoned.Store(false) here, as it is always set to false when it was true in ForceRelease
  619. c.handlerCtx = nil
  620. c.DefaultReq.release()
  621. c.DefaultRes.release()
  622. }
  623. // Abandon marks this context as abandoned. An abandoned context will not be
  624. // returned to the pool when ReleaseCtx is called.
  625. //
  626. // This is used by the timeout middleware to return immediately while the
  627. // handler goroutine continues using the context safely.
  628. //
  629. // Only call ForceRelease after Abandon if you can guarantee no other goroutine
  630. // (including Fiber's requestHandler and ErrorHandler) will touch the context.
  631. // The timeout middleware intentionally does NOT call ForceRelease to avoid
  632. // races, which means timed-out requests leak their contexts until a safe
  633. // reclamation strategy exists.
  634. func (c *DefaultCtx) Abandon() {
  635. c.abandoned.Store(true)
  636. }
  637. // IsAbandoned returns true if Abandon() was called on this context.
  638. func (c *DefaultCtx) IsAbandoned() bool {
  639. return c.abandoned.Load()
  640. }
  641. // ForceRelease releases an abandoned context back to the pool.
  642. // This MUST only be called after all goroutines (including requestHandler and
  643. // ErrorHandler) have completely finished using this context. Calling it while
  644. // any goroutine is still running causes races.
  645. func (c *DefaultCtx) ForceRelease() {
  646. c.abandoned.Store(false)
  647. c.app.ReleaseCtx(c)
  648. }
  649. func (c *DefaultCtx) renderExtensions(bind any) {
  650. if bindMap, ok := bind.(Map); ok {
  651. // Bind view map
  652. for key, value := range c.viewBindMap {
  653. if _, ok := bindMap[key]; !ok {
  654. bindMap[key] = value
  655. }
  656. }
  657. // Check if the PassLocalsToViews option is enabled (by default it is disabled)
  658. if c.app.config.PassLocalsToViews {
  659. // Loop through each local and set it in the map
  660. c.fasthttp.VisitUserValues(func(key []byte, val any) {
  661. // check if bindMap doesn't contain the key
  662. if _, ok := bindMap[c.app.toString(key)]; !ok {
  663. // Set the key and value in the bindMap
  664. bindMap[c.app.toString(key)] = val
  665. }
  666. })
  667. }
  668. }
  669. if len(c.app.mountFields.appListKeys) == 0 {
  670. c.app.generateAppListKeys()
  671. }
  672. }
  673. // Bind You can bind body, cookie, headers etc. into the map, map slice, struct easily by using Binding method.
  674. // It gives custom binding support, detailed binding options and more.
  675. // Replacement of: BodyParser, ParamsParser, GetReqHeaders, GetRespHeaders, AllParams, QueryParser, ReqHeaderParser
  676. func (c *DefaultCtx) Bind() *Bind {
  677. if c.bind == nil {
  678. c.bind = AcquireBind()
  679. }
  680. c.bind.ctx = c
  681. return c.bind
  682. }
  683. // Methods to use with next stack.
  684. func (c *DefaultCtx) getMethodInt() int {
  685. return c.methodInt
  686. }
  687. func (c *DefaultCtx) getIndexRoute() int {
  688. return c.indexRoute
  689. }
  690. func (c *DefaultCtx) getTreePathHash() int {
  691. return c.treePathHash
  692. }
  693. func (c *DefaultCtx) getDetectionPath() string {
  694. return c.app.toString(c.detectionPath)
  695. }
  696. func (c *DefaultCtx) getValues() *[maxParams]string {
  697. return &c.values
  698. }
  699. func (c *DefaultCtx) getMatched() bool {
  700. return c.matched
  701. }
  702. func (c *DefaultCtx) getSkipNonUseRoutes() bool {
  703. return c.skipNonUseRoutes
  704. }
  705. func (c *DefaultCtx) setIndexHandler(handler int) {
  706. c.indexHandler = handler
  707. }
  708. func (c *DefaultCtx) setIndexRoute(route int) {
  709. c.indexRoute = route
  710. }
  711. func (c *DefaultCtx) setMatched(matched bool) {
  712. c.matched = matched
  713. }
  714. func (c *DefaultCtx) setSkipNonUseRoutes(skip bool) {
  715. c.skipNonUseRoutes = skip
  716. }
  717. func (c *DefaultCtx) setRoute(route *Route) {
  718. c.route = route
  719. }
  720. func (c *DefaultCtx) getPathOriginal() string {
  721. return c.pathOriginal
  722. }