Files

832 lines
25 KiB
Go

// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
// 🤖 GitHub Repository: https://github.com/gofiber/fiber
// 📌 API Documentation: https://docs.gofiber.io
package fiber
import (
"context"
"crypto/tls"
"fmt"
"io"
"maps"
"mime/multipart"
"strings"
"sync/atomic"
"time"
"github.com/gofiber/utils/v2"
utilsbytes "github.com/gofiber/utils/v2/bytes"
"github.com/valyala/bytebufferpool"
"github.com/valyala/fasthttp"
)
const (
schemeHTTP = "http"
schemeHTTPS = "https"
)
const (
// maxParams defines the maximum number of parameters per route.
maxParams = 30
maxDetectionPaths = 3
)
var (
_ io.Writer = (*DefaultCtx)(nil) // Compile-time check
_ context.Context = (*DefaultCtx)(nil) // Compile-time check
)
// The contextKey type is unexported to prevent collisions with context keys defined in
// other packages.
type contextKey int
// userContextKey define the key name for storing context.Context in *fasthttp.RequestCtx
const (
userContextKey contextKey = iota // __local_user_context__
)
// DefaultCtx is the default implementation of the Ctx interface
// generation tool `go install github.com/vburenin/ifacemaker@f30b6f9bdbed4b5c4804ec9ba4a04a999525c202`
// https://github.com/vburenin/ifacemaker/blob/f30b6f9bdbed4b5c4804ec9ba4a04a999525c202/ifacemaker.go#L14-L31
//
//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."
type DefaultCtx struct {
handlerCtx CustomCtx // Active custom context implementation, if any
DefaultReq // Default request api
DefaultRes // Default response api
app *App // Reference to *App
route *Route // Reference to *Route
fasthttp *fasthttp.RequestCtx // Reference to *fasthttp.RequestCtx
bind *Bind // Default bind reference
redirect *Redirect // Default redirect reference
viewBindMap Map // Default view map to bind template engine
values [maxParams]string // Route parameter values
baseURI string // HTTP base uri
pathOriginal string // Original HTTP path
flashMessages redirectionMsgs // Flash messages
path []byte // HTTP path with the modifications by the configuration
detectionPath []byte // Route detection path
treePathHash int // Hash of the path for the search in the tree
indexRoute int // Index of the current route
indexHandler int // Index of the current handler
methodInt int // HTTP method INT equivalent
abandoned atomic.Bool // If true, ctx won't be pooled until ForceRelease is called
matched bool // Non use route matched
skipNonUseRoutes bool // Skip non-use routes while iterating middleware
}
// TLSHandler hosts the callback hooks Fiber invokes while negotiating TLS
// connections, including optional client certificate lookups.
type TLSHandler struct {
clientHelloInfo *tls.ClientHelloInfo
}
// GetClientInfo Callback function to set ClientHelloInfo
// 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
// Since we overlay the method of the TLS config in the listener method
func (t *TLSHandler) GetClientInfo(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
t.clientHelloInfo = info
return nil, nil //nolint:nilnil // Not returning anything useful here is probably fine
}
// Views is the interface that wraps the Render function.
type Views interface {
Load() error
Render(out io.Writer, name string, binding any, layout ...string) error
}
// App returns the *App reference to the instance of the Fiber application
func (c *DefaultCtx) App() *App {
return c.app
}
// BaseURL returns (protocol + host + base path).
func (c *DefaultCtx) BaseURL() string {
// TODO: Could be improved: 53.8 ns/op 32 B/op 1 allocs/op
// Should work like https://codeigniter.com/user_guide/helpers/url_helper.html
if c.baseURI != "" {
return c.baseURI
}
scheme := c.Scheme()
host := c.Host()
buf := make([]byte, 0, len(scheme)+len("://")+len(host))
buf = append(buf, scheme...)
buf = append(buf, "://"...)
buf = append(buf, host...)
c.baseURI = c.app.toString(buf)
return c.baseURI
}
// RequestCtx returns *fasthttp.RequestCtx that carries a deadline
// a cancellation signal, and other values across API boundaries.
func (c *DefaultCtx) RequestCtx() *fasthttp.RequestCtx {
return c.fasthttp
}
// Context returns a context implementation that was set by
// user earlier or returns a non-nil, empty context, if it was not set earlier.
func (c *DefaultCtx) Context() context.Context {
if c.fasthttp == nil {
return context.Background()
}
if ctx, ok := c.fasthttp.UserValue(userContextKey).(context.Context); ok && ctx != nil {
return ctx
}
ctx := context.Background()
c.SetContext(ctx)
return ctx
}
// SetContext sets a context implementation by user.
func (c *DefaultCtx) SetContext(ctx context.Context) {
if c.fasthttp == nil {
return
}
c.fasthttp.SetUserValue(userContextKey, ctx)
}
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
//
// Due to current limitations in how fasthttp works, Deadline operates as a nop.
// See: https://github.com/valyala/fasthttp/issues/965#issuecomment-777268945
func (*DefaultCtx) Deadline() (time.Time, bool) {
return time.Time{}, false
}
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
// The close of the Done channel may happen asynchronously,
// after the cancel function returns.
//
// Due to current limitations in how fasthttp works, Done operates as a nop.
// See: https://github.com/valyala/fasthttp/issues/965#issuecomment-777268945
func (*DefaultCtx) Done() <-chan struct{} {
return nil
}
// Err mirrors context.Err, returning nil until cancellation and then the terminal error value.
//
// Due to current limitations in how fasthttp works, Err operates as a nop.
// See: https://github.com/valyala/fasthttp/issues/965#issuecomment-777268945
func (*DefaultCtx) Err() error {
return nil
}
// Request return the *fasthttp.Request object
// This allows you to use all fasthttp request methods
// https://godoc.org/github.com/valyala/fasthttp#Request
// Returns nil if the context has been released.
func (c *DefaultCtx) Request() *fasthttp.Request {
if c.fasthttp == nil {
return nil
}
return &c.fasthttp.Request
}
// Response return the *fasthttp.Response object
// This allows you to use all fasthttp response methods
// https://godoc.org/github.com/valyala/fasthttp#Response
// Returns nil if the context has been released.
func (c *DefaultCtx) Response() *fasthttp.Response {
if c.fasthttp == nil {
return nil
}
return &c.fasthttp.Response
}
// Get returns the HTTP request header specified by field.
// Field names are case-insensitive
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting instead.
func (c *DefaultCtx) Get(key string, defaultValue ...string) string {
return c.DefaultReq.Get(key, defaultValue...)
}
// GetHeaders returns the HTTP request headers.
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting instead.
func (c *DefaultCtx) GetHeaders() map[string][]string {
return c.DefaultReq.GetHeaders()
}
// GetReqHeaders returns the HTTP request headers.
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting instead.
func (c *DefaultCtx) GetReqHeaders() map[string][]string {
return c.DefaultReq.GetHeaders()
}
// GetRespHeader returns the HTTP response header specified by field.
// Field names are case-insensitive
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting instead.
func (c *DefaultCtx) GetRespHeader(key string, defaultValue ...string) string {
return c.DefaultRes.Get(key, defaultValue...)
}
// GetRespHeaders returns the HTTP response headers.
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting instead.
func (c *DefaultCtx) GetRespHeaders() map[string][]string {
return c.DefaultRes.GetHeaders()
}
// ClientHelloInfo return CHI from context
func (c *DefaultCtx) ClientHelloInfo() *tls.ClientHelloInfo {
if c.app.tlsHandler != nil {
return c.app.tlsHandler.clientHelloInfo
}
return nil
}
// Next executes the next method in the stack that matches the current route.
func (c *DefaultCtx) Next() error {
// Increment handler index
c.indexHandler++
// Did we execute all route handlers?
if c.indexHandler < len(c.route.Handlers) {
if c.handlerCtx != nil {
return c.route.Handlers[c.indexHandler](c.handlerCtx)
}
return c.route.Handlers[c.indexHandler](c)
}
if c.handlerCtx != nil {
_, err := c.app.nextCustom(c.handlerCtx)
return err
}
_, err := c.app.next(c)
return err
}
// RestartRouting instead of going to the next handler. This may be useful after
// changing the request path. Note that handlers might be executed again.
func (c *DefaultCtx) RestartRouting() error {
c.indexRoute = -1
if c.handlerCtx != nil {
_, err := c.app.nextCustom(c.handlerCtx)
return err
}
_, err := c.app.next(c)
return err
}
func (c *DefaultCtx) setHandlerCtx(ctx CustomCtx) {
if ctx == nil {
c.handlerCtx = nil
return
}
if defaultCtx, ok := ctx.(*DefaultCtx); ok && defaultCtx == c {
c.handlerCtx = nil
return
}
c.handlerCtx = ctx
}
// OriginalURL contains the original request URL.
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting to use the value outside the Handler.
func (c *DefaultCtx) OriginalURL() string {
return c.app.toString(c.fasthttp.Request.Header.RequestURI())
}
// Path returns the path part of the request URL.
// Optionally, you could override the path.
// Make copies or use the Immutable setting to use the value outside the Handler.
func (c *DefaultCtx) Path(override ...string) string {
if len(override) != 0 && string(c.path) != override[0] {
// Set new path to context
c.pathOriginal = override[0]
// Set new path to request context
c.fasthttp.Request.URI().SetPath(c.pathOriginal)
// Prettify path
c.configDependentPaths()
}
return c.app.toString(c.path)
}
// RequestID returns the request identifier from the response header or request header.
func (c *DefaultCtx) RequestID() string {
if requestID := c.GetRespHeader(HeaderXRequestID); requestID != "" {
return requestID
}
return c.Get(HeaderXRequestID)
}
// Req returns a convenience type whose API is limited to operations
// on the incoming request.
func (c *DefaultCtx) Req() Req {
return &c.DefaultReq
}
// Res returns a convenience type whose API is limited to operations
// on the outgoing response.
func (c *DefaultCtx) Res() Res {
return &c.DefaultRes
}
// Redirect returns the Redirect reference.
// Use Redirect().Status() to set custom redirection status code.
// If status is not specified, status defaults to 303 See Other.
// You can use Redirect().To(), Redirect().Route() and Redirect().Back() for redirection.
func (c *DefaultCtx) Redirect() *Redirect {
if c.redirect == nil {
c.redirect = AcquireRedirect()
c.redirect.c = c
}
return c.redirect
}
// ViewBind Add vars to default view var map binding to template engine.
// Variables are read by the Render method and may be overwritten.
func (c *DefaultCtx) ViewBind(vars Map) error {
// init viewBindMap - lazy map
if c.viewBindMap == nil {
c.viewBindMap = make(Map, len(vars))
}
maps.Copy(c.viewBindMap, vars)
return nil
}
// Route returns the matched Route struct.
func (c *DefaultCtx) Route() *Route {
if c.route == nil {
// Fallback for fasthttp error handler
return &Route{
path: c.pathOriginal,
Path: c.pathOriginal,
Method: c.Method(),
Handlers: make([]Handler, 0),
Params: make([]string, 0),
}
}
return c.route
}
// FullPath returns the matched route path, including any group prefixes.
func (c *DefaultCtx) FullPath() string {
return c.Route().Path
}
// Matched returns true if the current request path was matched by the router.
func (c *DefaultCtx) Matched() bool {
return c.getMatched()
}
// IsMiddleware returns true if the current request handler was registered as middleware.
func (c *DefaultCtx) IsMiddleware() bool {
if c.route == nil {
return false
}
if c.route.use {
return true
}
// For route-level middleware, there will be a next handler in the chain
return c.indexHandler+1 < len(c.route.Handlers)
}
// HasBody returns true if the request declares a body via Content-Length, Transfer-Encoding, or already buffered payload data.
func (c *DefaultCtx) HasBody() bool {
hdr := &c.fasthttp.Request.Header
//nolint:revive // switch is exhaustive for all ContentLength() cases
switch cl := hdr.ContentLength(); {
case cl > 0:
return true
case cl == -1:
// fasthttp reports -1 for Transfer-Encoding: chunked bodies.
return true
case cl == 0:
if hasTransferEncodingBody(hdr) {
return true
}
}
return len(c.fasthttp.Request.Body()) > 0
}
// OverrideParam overwrites a route parameter value by name.
// If the parameter name does not exist in the route, this method does nothing.
func (c *DefaultCtx) OverrideParam(name, value string) {
// If no route is matched, there are no parameters to update
if !c.Matched() {
return
}
// Normalize wildcard (*) and plus (+) tokens to their internal
// representations (*1, +1) used by the router.
if name == "*" || name == "+" {
name += "1"
}
if c.app.config.CaseSensitive {
for i, param := range c.route.Params {
if param == name {
c.values[i] = value
return
}
}
return
}
nameBytes := utils.UnsafeBytes(name)
for i, param := range c.route.Params {
if utils.EqualFold(utils.UnsafeBytes(param), nameBytes) {
c.values[i] = value
return
}
}
}
func hasTransferEncodingBody(hdr *fasthttp.RequestHeader) bool {
teBytes := hdr.Peek(HeaderTransferEncoding)
var te string
if len(teBytes) > 0 {
te = utils.UnsafeString(teBytes)
} else {
for key, value := range hdr.All() {
if !utils.EqualFold(utils.UnsafeString(key), HeaderTransferEncoding) {
continue
}
te = utils.UnsafeString(value)
break
}
}
if te == "" {
return false
}
hasEncoding := false
for raw := range strings.SplitSeq(te, ",") {
token := utils.TrimSpace(raw)
if token == "" {
continue
}
if idx := strings.IndexByte(token, ';'); idx >= 0 {
token = utils.TrimSpace(token[:idx])
}
if token == "" {
continue
}
if utils.EqualFold(token, "identity") {
continue
}
hasEncoding = true
}
return hasEncoding
}
// IsWebSocket returns true if the request includes a WebSocket upgrade handshake.
func (c *DefaultCtx) IsWebSocket() bool {
conn := c.fasthttp.Request.Header.Peek(HeaderConnection)
var isUpgrade bool
for v := range strings.SplitSeq(utils.UnsafeString(conn), ",") {
if utils.EqualFold(utils.TrimSpace(v), "upgrade") {
isUpgrade = true
break
}
}
if !isUpgrade {
return false
}
return utils.EqualFold(c.fasthttp.Request.Header.Peek(HeaderUpgrade), websocketBytes)
}
// IsPreflight returns true if the request is a CORS preflight.
func (c *DefaultCtx) IsPreflight() bool {
if c.Method() != MethodOptions {
return false
}
hdr := &c.fasthttp.Request.Header
if len(hdr.Peek(HeaderAccessControlRequestMethod)) == 0 {
return false
}
return len(hdr.Peek(HeaderOrigin)) > 0
}
// SaveFile saves any multipart file to disk.
func (*DefaultCtx) SaveFile(fileheader *multipart.FileHeader, path string) error {
return fasthttp.SaveMultipartFile(fileheader, path)
}
// SaveFileToStorage saves any multipart file to an external storage system.
func (c *DefaultCtx) SaveFileToStorage(fileheader *multipart.FileHeader, path string, storage Storage) error {
file, err := fileheader.Open()
if err != nil {
return fmt.Errorf("failed to open: %w", err)
}
defer file.Close() //nolint:errcheck // not needed
maxUploadSize := c.app.config.BodyLimit
if maxUploadSize <= 0 {
maxUploadSize = DefaultBodyLimit
}
if fileheader.Size > 0 && fileheader.Size > int64(maxUploadSize) {
return fmt.Errorf("failed to read: %w", fasthttp.ErrBodyTooLarge)
}
buf := bytebufferpool.Get()
defer bytebufferpool.Put(buf)
limitedReader := io.LimitReader(file, int64(maxUploadSize)+1)
if _, err = buf.ReadFrom(limitedReader); err != nil {
return fmt.Errorf("failed to read: %w", err)
}
if buf.Len() > maxUploadSize {
return fmt.Errorf("failed to read: %w", fasthttp.ErrBodyTooLarge)
}
data := append([]byte(nil), buf.Bytes()...)
if err := storage.SetWithContext(c.Context(), path, data, 0); err != nil {
return fmt.Errorf("failed to store: %w", err)
}
return nil
}
// Secure returns whether a secure connection was established.
func (c *DefaultCtx) Secure() bool {
return c.Protocol() == schemeHTTPS
}
// Status sets the HTTP status for the response.
// This method is chainable.
func (c *DefaultCtx) Status(status int) Ctx {
c.fasthttp.Response.SetStatusCode(status)
return c
}
// String returns unique string representation of the ctx.
//
// The returned value may be useful for logging.
func (c *DefaultCtx) String() string {
// Get buffer from pool
buf := bytebufferpool.Get()
// Start with the ID, converting it to a hex string without fmt.Sprintf
buf.WriteByte('#')
const hex = "0123456789abcdef"
var id [16]byte
ctxID := c.fasthttp.ID()
for i := len(id) - 1; i >= 0; i-- {
id[i] = hex[ctxID&0xf]
ctxID >>= 4
}
buf.Write(id[:])
buf.WriteString(" - ")
// Add local and remote addresses directly
buf.WriteString(c.fasthttp.LocalAddr().String())
buf.WriteString(" <-> ")
buf.WriteString(c.fasthttp.RemoteAddr().String())
buf.WriteString(" - ")
// Add method and URI
buf.Write(c.fasthttp.Request.Header.Method())
buf.WriteByte(' ')
buf.Write(c.fasthttp.URI().FullURI())
// Allocate string
str := buf.String()
// Reset buffer
buf.Reset()
bytebufferpool.Put(buf)
return str
}
// Value makes it possible to retrieve values (Locals) under keys scoped to the request
// and therefore available to all following routes that match the request. If the context
// has been released and c.fasthttp is nil (for example, after ReleaseCtx), Value returns nil.
func (c *DefaultCtx) Value(key any) any {
if c.fasthttp == nil {
return nil
}
return c.fasthttp.UserValue(key)
}
var (
// xmlHTTPRequestBytes is precomputed for XHR detection
xmlHTTPRequestBytes = []byte("xmlhttprequest")
// websocketBytes is precomputed for WebSocket upgrade detection
websocketBytes = []byte("websocket")
)
// XHR returns a Boolean property, that is true, if the request's X-Requested-With header field is XMLHttpRequest,
// indicating that the request was issued by a client library (such as jQuery).
func (c *DefaultCtx) XHR() bool {
return utils.EqualFold(c.fasthttp.Request.Header.Peek(HeaderXRequestedWith), xmlHTTPRequestBytes)
}
// configDependentPaths set paths for route recognition and prepared paths for the user,
// here the features for caseSensitive, decoded paths, strict paths are evaluated
func (c *DefaultCtx) configDependentPaths() {
c.path = append(c.path[:0], c.pathOriginal...)
// If UnescapePath enabled, we decode the path and save it for the framework user
if c.app.config.UnescapePath {
c.path = fasthttp.AppendUnquotedArg(c.path[:0], c.path)
}
// another path is specified which is for routing recognition only
// use the path that was changed by the previous configuration flags
c.detectionPath = append(c.detectionPath[:0], c.path...)
// If CaseSensitive is disabled, we lowercase the original path
if !c.app.config.CaseSensitive {
c.detectionPath = utilsbytes.UnsafeToLower(c.detectionPath)
}
// If StrictRouting is disabled, we strip all trailing slashes
if !c.app.config.StrictRouting && len(c.detectionPath) > 1 && c.detectionPath[len(c.detectionPath)-1] == '/' {
c.detectionPath = utils.TrimRight(c.detectionPath, '/')
}
// Define the path for dividing routes into areas for fast tree detection, so that fewer routes need to be traversed,
// since the first three characters area select a list of routes
c.treePathHash = 0
if len(c.detectionPath) >= maxDetectionPaths {
c.treePathHash = int(c.detectionPath[0])<<16 |
int(c.detectionPath[1])<<8 |
int(c.detectionPath[2])
}
}
// Reset is a method to reset context fields by given request when to use server handlers.
func (c *DefaultCtx) Reset(fctx *fasthttp.RequestCtx) {
// Reset route and handler index
c.indexRoute = -1
c.indexHandler = 0
// Reset matched flag
c.matched = false
c.skipNonUseRoutes = false
// Set paths
c.pathOriginal = c.app.toString(fctx.URI().PathOriginal())
// Set method
c.methodInt = c.app.methodInt(utils.UnsafeString(fctx.Request.Header.Method()))
// Attach *fasthttp.RequestCtx to ctx
c.fasthttp = fctx
// reset base uri
c.baseURI = ""
// Prettify path
c.configDependentPaths()
c.DefaultReq.c = c
c.DefaultRes.c = c
c.fasthttp.SetUserValue(userContextKey, nil)
}
// release is a method to reset context fields when to use ReleaseCtx()
func (c *DefaultCtx) release() {
c.route = nil
c.fasthttp = nil
if c.bind != nil {
ReleaseBind(c.bind)
c.bind = nil
}
c.flashMessages = c.flashMessages[:0]
// Clear viewBindMap by deleting all keys (reuse underlying map if possible)
clear(c.viewBindMap)
if c.redirect != nil {
ReleaseRedirect(c.redirect)
c.redirect = nil
}
c.skipNonUseRoutes = false
// performance: no need for using c.abandoned.Store(false) here, as it is always set to false when it was true in ForceRelease
c.handlerCtx = nil
c.DefaultReq.release()
c.DefaultRes.release()
}
// Abandon marks this context as abandoned. An abandoned context will not be
// returned to the pool when ReleaseCtx is called.
//
// This is used by the timeout middleware to return immediately while the
// handler goroutine continues using the context safely.
//
// Only call ForceRelease after Abandon if you can guarantee no other goroutine
// (including Fiber's requestHandler and ErrorHandler) will touch the context.
// The timeout middleware intentionally does NOT call ForceRelease to avoid
// races, which means timed-out requests leak their contexts until a safe
// reclamation strategy exists.
func (c *DefaultCtx) Abandon() {
c.abandoned.Store(true)
}
// IsAbandoned returns true if Abandon() was called on this context.
func (c *DefaultCtx) IsAbandoned() bool {
return c.abandoned.Load()
}
// ForceRelease releases an abandoned context back to the pool.
// This MUST only be called after all goroutines (including requestHandler and
// ErrorHandler) have completely finished using this context. Calling it while
// any goroutine is still running causes races.
func (c *DefaultCtx) ForceRelease() {
c.abandoned.Store(false)
c.app.ReleaseCtx(c)
}
func (c *DefaultCtx) renderExtensions(bind any) {
if bindMap, ok := bind.(Map); ok {
// Bind view map
for key, value := range c.viewBindMap {
if _, ok := bindMap[key]; !ok {
bindMap[key] = value
}
}
// Check if the PassLocalsToViews option is enabled (by default it is disabled)
if c.app.config.PassLocalsToViews {
// Loop through each local and set it in the map
c.fasthttp.VisitUserValues(func(key []byte, val any) {
// check if bindMap doesn't contain the key
if _, ok := bindMap[c.app.toString(key)]; !ok {
// Set the key and value in the bindMap
bindMap[c.app.toString(key)] = val
}
})
}
}
if len(c.app.mountFields.appListKeys) == 0 {
c.app.generateAppListKeys()
}
}
// Bind You can bind body, cookie, headers etc. into the map, map slice, struct easily by using Binding method.
// It gives custom binding support, detailed binding options and more.
// Replacement of: BodyParser, ParamsParser, GetReqHeaders, GetRespHeaders, AllParams, QueryParser, ReqHeaderParser
func (c *DefaultCtx) Bind() *Bind {
if c.bind == nil {
c.bind = AcquireBind()
}
c.bind.ctx = c
return c.bind
}
// Methods to use with next stack.
func (c *DefaultCtx) getMethodInt() int {
return c.methodInt
}
func (c *DefaultCtx) getIndexRoute() int {
return c.indexRoute
}
func (c *DefaultCtx) getTreePathHash() int {
return c.treePathHash
}
func (c *DefaultCtx) getDetectionPath() string {
return c.app.toString(c.detectionPath)
}
func (c *DefaultCtx) getValues() *[maxParams]string {
return &c.values
}
func (c *DefaultCtx) getMatched() bool {
return c.matched
}
func (c *DefaultCtx) getSkipNonUseRoutes() bool {
return c.skipNonUseRoutes
}
func (c *DefaultCtx) setIndexHandler(handler int) {
c.indexHandler = handler
}
func (c *DefaultCtx) setIndexRoute(route int) {
c.indexRoute = route
}
func (c *DefaultCtx) setMatched(matched bool) {
c.matched = matched
}
func (c *DefaultCtx) setSkipNonUseRoutes(skip bool) {
c.skipNonUseRoutes = skip
}
func (c *DefaultCtx) setRoute(route *Route) {
c.route = route
}
func (c *DefaultCtx) getPathOriginal() string {
return c.pathOriginal
}