"Fossies" - the Fresh Open Source Software Archive

Member "gin-1.7.7/gin.go" (24 Nov 2021, 20808 Bytes) of package /linux/www/gin-1.7.7.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "gin.go": 1.7.6_vs_1.7.7.

    1 // Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
    2 // Use of this source code is governed by a MIT style
    3 // license that can be found in the LICENSE file.
    4 
    5 package gin
    6 
    7 import (
    8     "fmt"
    9     "html/template"
   10     "net"
   11     "net/http"
   12     "os"
   13     "path"
   14     "reflect"
   15     "strings"
   16     "sync"
   17 
   18     "github.com/gin-gonic/gin/internal/bytesconv"
   19     "github.com/gin-gonic/gin/render"
   20 )
   21 
   22 const defaultMultipartMemory = 32 << 20 // 32 MB
   23 
   24 var (
   25     default404Body = []byte("404 page not found")
   26     default405Body = []byte("405 method not allowed")
   27 )
   28 
   29 var defaultPlatform string
   30 
   31 var defaultTrustedCIDRs = []*net.IPNet{{IP: net.IP{0x0, 0x0, 0x0, 0x0}, Mask: net.IPMask{0x0, 0x0, 0x0, 0x0}}} // 0.0.0.0/0
   32 
   33 // HandlerFunc defines the handler used by gin middleware as return value.
   34 type HandlerFunc func(*Context)
   35 
   36 // HandlersChain defines a HandlerFunc array.
   37 type HandlersChain []HandlerFunc
   38 
   39 // Last returns the last handler in the chain. ie. the last handler is the main one.
   40 func (c HandlersChain) Last() HandlerFunc {
   41     if length := len(c); length > 0 {
   42         return c[length-1]
   43     }
   44     return nil
   45 }
   46 
   47 // RouteInfo represents a request route's specification which contains method and path and its handler.
   48 type RouteInfo struct {
   49     Method      string
   50     Path        string
   51     Handler     string
   52     HandlerFunc HandlerFunc
   53 }
   54 
   55 // RoutesInfo defines a RouteInfo array.
   56 type RoutesInfo []RouteInfo
   57 
   58 // Trusted platforms
   59 const (
   60     // When running on Google App Engine. Trust X-Appengine-Remote-Addr
   61     // for determining the client's IP
   62     PlatformGoogleAppEngine = "X-Appengine-Remote-Addr"
   63     // When using Cloudflare's CDN. Trust CF-Connecting-IP for determining
   64     // the client's IP
   65     PlatformCloudflare = "CF-Connecting-IP"
   66 )
   67 
   68 // Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
   69 // Create an instance of Engine, by using New() or Default()
   70 type Engine struct {
   71     RouterGroup
   72 
   73     // Enables automatic redirection if the current route can't be matched but a
   74     // handler for the path with (without) the trailing slash exists.
   75     // For example if /foo/ is requested but a route only exists for /foo, the
   76     // client is redirected to /foo with http status code 301 for GET requests
   77     // and 307 for all other request methods.
   78     RedirectTrailingSlash bool
   79 
   80     // If enabled, the router tries to fix the current request path, if no
   81     // handle is registered for it.
   82     // First superfluous path elements like ../ or // are removed.
   83     // Afterwards the router does a case-insensitive lookup of the cleaned path.
   84     // If a handle can be found for this route, the router makes a redirection
   85     // to the corrected path with status code 301 for GET requests and 307 for
   86     // all other request methods.
   87     // For example /FOO and /..//Foo could be redirected to /foo.
   88     // RedirectTrailingSlash is independent of this option.
   89     RedirectFixedPath bool
   90 
   91     // If enabled, the router checks if another method is allowed for the
   92     // current route, if the current request can not be routed.
   93     // If this is the case, the request is answered with 'Method Not Allowed'
   94     // and HTTP status code 405.
   95     // If no other Method is allowed, the request is delegated to the NotFound
   96     // handler.
   97     HandleMethodNotAllowed bool
   98 
   99     // If enabled, client IP will be parsed from the request's headers that
  100     // match those stored at `(*gin.Engine).RemoteIPHeaders`. If no IP was
  101     // fetched, it falls back to the IP obtained from
  102     // `(*gin.Context).Request.RemoteAddr`.
  103     ForwardedByClientIP bool
  104 
  105     // DEPRECATED: USE `TrustedPlatform` WITH VALUE `gin.GoogleAppEngine` INSTEAD
  106     // #726 #755 If enabled, it will trust some headers starting with
  107     // 'X-AppEngine...' for better integration with that PaaS.
  108     AppEngine bool
  109 
  110     // If enabled, the url.RawPath will be used to find parameters.
  111     UseRawPath bool
  112 
  113     // If true, the path value will be unescaped.
  114     // If UseRawPath is false (by default), the UnescapePathValues effectively is true,
  115     // as url.Path gonna be used, which is already unescaped.
  116     UnescapePathValues bool
  117 
  118     // RemoveExtraSlash a parameter can be parsed from the URL even with extra slashes.
  119     // See the PR #1817 and issue #1644
  120     RemoveExtraSlash bool
  121 
  122     // List of headers used to obtain the client IP when
  123     // `(*gin.Engine).ForwardedByClientIP` is `true` and
  124     // `(*gin.Context).Request.RemoteAddr` is matched by at least one of the
  125     // network origins of list defined by `(*gin.Engine).SetTrustedProxies()`.
  126     RemoteIPHeaders []string
  127 
  128     // If set to a constant of value gin.Platform*, trusts the headers set by
  129     // that platform, for example to determine the client IP
  130     TrustedPlatform string
  131 
  132     // Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
  133     // method call.
  134     MaxMultipartMemory int64
  135 
  136     delims           render.Delims
  137     secureJSONPrefix string
  138     HTMLRender       render.HTMLRender
  139     FuncMap          template.FuncMap
  140     allNoRoute       HandlersChain
  141     allNoMethod      HandlersChain
  142     noRoute          HandlersChain
  143     noMethod         HandlersChain
  144     pool             sync.Pool
  145     trees            methodTrees
  146     maxParams        uint16
  147     maxSections      uint16
  148     trustedProxies   []string
  149     trustedCIDRs     []*net.IPNet
  150 }
  151 
  152 var _ IRouter = &Engine{}
  153 
  154 // New returns a new blank Engine instance without any middleware attached.
  155 // By default the configuration is:
  156 // - RedirectTrailingSlash:  true
  157 // - RedirectFixedPath:      false
  158 // - HandleMethodNotAllowed: false
  159 // - ForwardedByClientIP:    true
  160 // - UseRawPath:             false
  161 // - UnescapePathValues:     true
  162 func New() *Engine {
  163     debugPrintWARNINGNew()
  164     engine := &Engine{
  165         RouterGroup: RouterGroup{
  166             Handlers: nil,
  167             basePath: "/",
  168             root:     true,
  169         },
  170         FuncMap:                template.FuncMap{},
  171         RedirectTrailingSlash:  true,
  172         RedirectFixedPath:      false,
  173         HandleMethodNotAllowed: false,
  174         ForwardedByClientIP:    true,
  175         RemoteIPHeaders:        []string{"X-Forwarded-For", "X-Real-IP"},
  176         TrustedPlatform:        defaultPlatform,
  177         UseRawPath:             false,
  178         RemoveExtraSlash:       false,
  179         UnescapePathValues:     true,
  180         MaxMultipartMemory:     defaultMultipartMemory,
  181         trees:                  make(methodTrees, 0, 9),
  182         delims:                 render.Delims{Left: "{{", Right: "}}"},
  183         secureJSONPrefix:       "while(1);",
  184         trustedProxies:         []string{"0.0.0.0/0"},
  185         trustedCIDRs:           defaultTrustedCIDRs,
  186     }
  187     engine.RouterGroup.engine = engine
  188     engine.pool.New = func() interface{} {
  189         return engine.allocateContext()
  190     }
  191     return engine
  192 }
  193 
  194 // Default returns an Engine instance with the Logger and Recovery middleware already attached.
  195 func Default() *Engine {
  196     debugPrintWARNINGDefault()
  197     engine := New()
  198     engine.Use(Logger(), Recovery())
  199     return engine
  200 }
  201 
  202 func (engine *Engine) allocateContext() *Context {
  203     v := make(Params, 0, engine.maxParams)
  204     skippedNodes := make([]skippedNode, 0, engine.maxSections)
  205     return &Context{engine: engine, params: &v, skippedNodes: &skippedNodes}
  206 }
  207 
  208 // Delims sets template left and right delims and returns a Engine instance.
  209 func (engine *Engine) Delims(left, right string) *Engine {
  210     engine.delims = render.Delims{Left: left, Right: right}
  211     return engine
  212 }
  213 
  214 // SecureJsonPrefix sets the secureJSONPrefix used in Context.SecureJSON.
  215 func (engine *Engine) SecureJsonPrefix(prefix string) *Engine {
  216     engine.secureJSONPrefix = prefix
  217     return engine
  218 }
  219 
  220 // LoadHTMLGlob loads HTML files identified by glob pattern
  221 // and associates the result with HTML renderer.
  222 func (engine *Engine) LoadHTMLGlob(pattern string) {
  223     left := engine.delims.Left
  224     right := engine.delims.Right
  225     templ := template.Must(template.New("").Delims(left, right).Funcs(engine.FuncMap).ParseGlob(pattern))
  226 
  227     if IsDebugging() {
  228         debugPrintLoadTemplate(templ)
  229         engine.HTMLRender = render.HTMLDebug{Glob: pattern, FuncMap: engine.FuncMap, Delims: engine.delims}
  230         return
  231     }
  232 
  233     engine.SetHTMLTemplate(templ)
  234 }
  235 
  236 // LoadHTMLFiles loads a slice of HTML files
  237 // and associates the result with HTML renderer.
  238 func (engine *Engine) LoadHTMLFiles(files ...string) {
  239     if IsDebugging() {
  240         engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims}
  241         return
  242     }
  243 
  244     templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFiles(files...))
  245     engine.SetHTMLTemplate(templ)
  246 }
  247 
  248 // SetHTMLTemplate associate a template with HTML renderer.
  249 func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
  250     if len(engine.trees) > 0 {
  251         debugPrintWARNINGSetHTMLTemplate()
  252     }
  253 
  254     engine.HTMLRender = render.HTMLProduction{Template: templ.Funcs(engine.FuncMap)}
  255 }
  256 
  257 // SetFuncMap sets the FuncMap used for template.FuncMap.
  258 func (engine *Engine) SetFuncMap(funcMap template.FuncMap) {
  259     engine.FuncMap = funcMap
  260 }
  261 
  262 // NoRoute adds handlers for NoRoute. It return a 404 code by default.
  263 func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
  264     engine.noRoute = handlers
  265     engine.rebuild404Handlers()
  266 }
  267 
  268 // NoMethod sets the handlers called when Engine.HandleMethodNotAllowed = true.
  269 func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
  270     engine.noMethod = handlers
  271     engine.rebuild405Handlers()
  272 }
  273 
  274 // Use attaches a global middleware to the router. ie. the middleware attached though Use() will be
  275 // included in the handlers chain for every single request. Even 404, 405, static files...
  276 // For example, this is the right place for a logger or error management middleware.
  277 func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
  278     engine.RouterGroup.Use(middleware...)
  279     engine.rebuild404Handlers()
  280     engine.rebuild405Handlers()
  281     return engine
  282 }
  283 
  284 func (engine *Engine) rebuild404Handlers() {
  285     engine.allNoRoute = engine.combineHandlers(engine.noRoute)
  286 }
  287 
  288 func (engine *Engine) rebuild405Handlers() {
  289     engine.allNoMethod = engine.combineHandlers(engine.noMethod)
  290 }
  291 
  292 func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
  293     assert1(path[0] == '/', "path must begin with '/'")
  294     assert1(method != "", "HTTP method can not be empty")
  295     assert1(len(handlers) > 0, "there must be at least one handler")
  296 
  297     debugPrintRoute(method, path, handlers)
  298 
  299     root := engine.trees.get(method)
  300     if root == nil {
  301         root = new(node)
  302         root.fullPath = "/"
  303         engine.trees = append(engine.trees, methodTree{method: method, root: root})
  304     }
  305     root.addRoute(path, handlers)
  306 
  307     // Update maxParams
  308     if paramsCount := countParams(path); paramsCount > engine.maxParams {
  309         engine.maxParams = paramsCount
  310     }
  311 
  312     if sectionsCount := countSections(path); sectionsCount > engine.maxSections {
  313         engine.maxSections = sectionsCount
  314     }
  315 }
  316 
  317 // Routes returns a slice of registered routes, including some useful information, such as:
  318 // the http method, path and the handler name.
  319 func (engine *Engine) Routes() (routes RoutesInfo) {
  320     for _, tree := range engine.trees {
  321         routes = iterate("", tree.method, routes, tree.root)
  322     }
  323     return routes
  324 }
  325 
  326 func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
  327     path += root.path
  328     if len(root.handlers) > 0 {
  329         handlerFunc := root.handlers.Last()
  330         routes = append(routes, RouteInfo{
  331             Method:      method,
  332             Path:        path,
  333             Handler:     nameOfFunction(handlerFunc),
  334             HandlerFunc: handlerFunc,
  335         })
  336     }
  337     for _, child := range root.children {
  338         routes = iterate(path, method, routes, child)
  339     }
  340     return routes
  341 }
  342 
  343 // Run attaches the router to a http.Server and starts listening and serving HTTP requests.
  344 // It is a shortcut for http.ListenAndServe(addr, router)
  345 // Note: this method will block the calling goroutine indefinitely unless an error happens.
  346 func (engine *Engine) Run(addr ...string) (err error) {
  347     defer func() { debugPrintError(err) }()
  348 
  349     if engine.isUnsafeTrustedProxies() {
  350         debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
  351             "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
  352     }
  353 
  354     address := resolveAddress(addr)
  355     debugPrint("Listening and serving HTTP on %s\n", address)
  356     err = http.ListenAndServe(address, engine)
  357     return
  358 }
  359 
  360 func (engine *Engine) prepareTrustedCIDRs() ([]*net.IPNet, error) {
  361     if engine.trustedProxies == nil {
  362         return nil, nil
  363     }
  364 
  365     cidr := make([]*net.IPNet, 0, len(engine.trustedProxies))
  366     for _, trustedProxy := range engine.trustedProxies {
  367         if !strings.Contains(trustedProxy, "/") {
  368             ip := parseIP(trustedProxy)
  369             if ip == nil {
  370                 return cidr, &net.ParseError{Type: "IP address", Text: trustedProxy}
  371             }
  372 
  373             switch len(ip) {
  374             case net.IPv4len:
  375                 trustedProxy += "/32"
  376             case net.IPv6len:
  377                 trustedProxy += "/128"
  378             }
  379         }
  380         _, cidrNet, err := net.ParseCIDR(trustedProxy)
  381         if err != nil {
  382             return cidr, err
  383         }
  384         cidr = append(cidr, cidrNet)
  385     }
  386     return cidr, nil
  387 }
  388 
  389 // SetTrustedProxies set a list of network origins (IPv4 addresses,
  390 // IPv4 CIDRs, IPv6 addresses or IPv6 CIDRs) from which to trust
  391 // request's headers that contain alternative client IP when
  392 // `(*gin.Engine).ForwardedByClientIP` is `true`. `TrustedProxies`
  393 // feature is enabled by default, and it also trusts all proxies
  394 // by default. If you want to disable this feature, use
  395 // Engine.SetTrustedProxies(nil), then Context.ClientIP() will
  396 // return the remote address directly.
  397 func (engine *Engine) SetTrustedProxies(trustedProxies []string) error {
  398     engine.trustedProxies = trustedProxies
  399     return engine.parseTrustedProxies()
  400 }
  401 
  402 // isUnsafeTrustedProxies compares Engine.trustedCIDRs and defaultTrustedCIDRs, it's not safe if equal (returns true)
  403 func (engine *Engine) isUnsafeTrustedProxies() bool {
  404     return reflect.DeepEqual(engine.trustedCIDRs, defaultTrustedCIDRs)
  405 }
  406 
  407 // parseTrustedProxies parse Engine.trustedProxies to Engine.trustedCIDRs
  408 func (engine *Engine) parseTrustedProxies() error {
  409     trustedCIDRs, err := engine.prepareTrustedCIDRs()
  410     engine.trustedCIDRs = trustedCIDRs
  411     return err
  412 }
  413 
  414 // parseIP parse a string representation of an IP and returns a net.IP with the
  415 // minimum byte representation or nil if input is invalid.
  416 func parseIP(ip string) net.IP {
  417     parsedIP := net.ParseIP(ip)
  418 
  419     if ipv4 := parsedIP.To4(); ipv4 != nil {
  420         // return ip in a 4-byte representation
  421         return ipv4
  422     }
  423 
  424     // return ip in a 16-byte representation or nil
  425     return parsedIP
  426 }
  427 
  428 // RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests.
  429 // It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
  430 // Note: this method will block the calling goroutine indefinitely unless an error happens.
  431 func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) {
  432     debugPrint("Listening and serving HTTPS on %s\n", addr)
  433     defer func() { debugPrintError(err) }()
  434 
  435     if engine.isUnsafeTrustedProxies() {
  436         debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
  437             "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
  438     }
  439 
  440     err = http.ListenAndServeTLS(addr, certFile, keyFile, engine)
  441     return
  442 }
  443 
  444 // RunUnix attaches the router to a http.Server and starts listening and serving HTTP requests
  445 // through the specified unix socket (ie. a file).
  446 // Note: this method will block the calling goroutine indefinitely unless an error happens.
  447 func (engine *Engine) RunUnix(file string) (err error) {
  448     debugPrint("Listening and serving HTTP on unix:/%s", file)
  449     defer func() { debugPrintError(err) }()
  450 
  451     if engine.isUnsafeTrustedProxies() {
  452         debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
  453             "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
  454     }
  455 
  456     listener, err := net.Listen("unix", file)
  457     if err != nil {
  458         return
  459     }
  460     defer listener.Close()
  461     defer os.Remove(file)
  462 
  463     err = http.Serve(listener, engine)
  464     return
  465 }
  466 
  467 // RunFd attaches the router to a http.Server and starts listening and serving HTTP requests
  468 // through the specified file descriptor.
  469 // Note: this method will block the calling goroutine indefinitely unless an error happens.
  470 func (engine *Engine) RunFd(fd int) (err error) {
  471     debugPrint("Listening and serving HTTP on fd@%d", fd)
  472     defer func() { debugPrintError(err) }()
  473 
  474     if engine.isUnsafeTrustedProxies() {
  475         debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
  476             "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
  477     }
  478 
  479     f := os.NewFile(uintptr(fd), fmt.Sprintf("fd@%d", fd))
  480     listener, err := net.FileListener(f)
  481     if err != nil {
  482         return
  483     }
  484     defer listener.Close()
  485     err = engine.RunListener(listener)
  486     return
  487 }
  488 
  489 // RunListener attaches the router to a http.Server and starts listening and serving HTTP requests
  490 // through the specified net.Listener
  491 func (engine *Engine) RunListener(listener net.Listener) (err error) {
  492     debugPrint("Listening and serving HTTP on listener what's bind with address@%s", listener.Addr())
  493     defer func() { debugPrintError(err) }()
  494 
  495     if engine.isUnsafeTrustedProxies() {
  496         debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
  497             "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
  498     }
  499 
  500     err = http.Serve(listener, engine)
  501     return
  502 }
  503 
  504 // ServeHTTP conforms to the http.Handler interface.
  505 func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  506     c := engine.pool.Get().(*Context)
  507     c.writermem.reset(w)
  508     c.Request = req
  509     c.reset()
  510 
  511     engine.handleHTTPRequest(c)
  512 
  513     engine.pool.Put(c)
  514 }
  515 
  516 // HandleContext re-enter a context that has been rewritten.
  517 // This can be done by setting c.Request.URL.Path to your new target.
  518 // Disclaimer: You can loop yourself to death with this, use wisely.
  519 func (engine *Engine) HandleContext(c *Context) {
  520     oldIndexValue := c.index
  521     c.reset()
  522     engine.handleHTTPRequest(c)
  523 
  524     c.index = oldIndexValue
  525 }
  526 
  527 func (engine *Engine) handleHTTPRequest(c *Context) {
  528     httpMethod := c.Request.Method
  529     rPath := c.Request.URL.Path
  530     unescape := false
  531     if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
  532         rPath = c.Request.URL.RawPath
  533         unescape = engine.UnescapePathValues
  534     }
  535 
  536     if engine.RemoveExtraSlash {
  537         rPath = cleanPath(rPath)
  538     }
  539 
  540     // Find root of the tree for the given HTTP method
  541     t := engine.trees
  542     for i, tl := 0, len(t); i < tl; i++ {
  543         if t[i].method != httpMethod {
  544             continue
  545         }
  546         root := t[i].root
  547         // Find route in tree
  548         value := root.getValue(rPath, c.params, c.skippedNodes, unescape)
  549         if value.params != nil {
  550             c.Params = *value.params
  551         }
  552         if value.handlers != nil {
  553             c.handlers = value.handlers
  554             c.fullPath = value.fullPath
  555             c.Next()
  556             c.writermem.WriteHeaderNow()
  557             return
  558         }
  559         if httpMethod != "CONNECT" && rPath != "/" {
  560             if value.tsr && engine.RedirectTrailingSlash {
  561                 redirectTrailingSlash(c)
  562                 return
  563             }
  564             if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
  565                 return
  566             }
  567         }
  568         break
  569     }
  570 
  571     if engine.HandleMethodNotAllowed {
  572         for _, tree := range engine.trees {
  573             if tree.method == httpMethod {
  574                 continue
  575             }
  576             if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {
  577                 c.handlers = engine.allNoMethod
  578                 serveError(c, http.StatusMethodNotAllowed, default405Body)
  579                 return
  580             }
  581         }
  582     }
  583     c.handlers = engine.allNoRoute
  584     serveError(c, http.StatusNotFound, default404Body)
  585 }
  586 
  587 var mimePlain = []string{MIMEPlain}
  588 
  589 func serveError(c *Context, code int, defaultMessage []byte) {
  590     c.writermem.status = code
  591     c.Next()
  592     if c.writermem.Written() {
  593         return
  594     }
  595     if c.writermem.Status() == code {
  596         c.writermem.Header()["Content-Type"] = mimePlain
  597         _, err := c.Writer.Write(defaultMessage)
  598         if err != nil {
  599             debugPrint("cannot write message to writer during serve error: %v", err)
  600         }
  601         return
  602     }
  603     c.writermem.WriteHeaderNow()
  604 }
  605 
  606 func redirectTrailingSlash(c *Context) {
  607     req := c.Request
  608     p := req.URL.Path
  609     if prefix := path.Clean(c.Request.Header.Get("X-Forwarded-Prefix")); prefix != "." {
  610         p = prefix + "/" + req.URL.Path
  611     }
  612     req.URL.Path = p + "/"
  613     if length := len(p); length > 1 && p[length-1] == '/' {
  614         req.URL.Path = p[:length-1]
  615     }
  616     redirectRequest(c)
  617 }
  618 
  619 func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool {
  620     req := c.Request
  621     rPath := req.URL.Path
  622 
  623     if fixedPath, ok := root.findCaseInsensitivePath(cleanPath(rPath), trailingSlash); ok {
  624         req.URL.Path = bytesconv.BytesToString(fixedPath)
  625         redirectRequest(c)
  626         return true
  627     }
  628     return false
  629 }
  630 
  631 func redirectRequest(c *Context) {
  632     req := c.Request
  633     rPath := req.URL.Path
  634     rURL := req.URL.String()
  635 
  636     code := http.StatusMovedPermanently // Permanent redirect, request with GET method
  637     if req.Method != http.MethodGet {
  638         code = http.StatusTemporaryRedirect
  639     }
  640     debugPrint("redirecting request %d: %s --> %s", code, rPath, rURL)
  641     http.Redirect(c.Writer, req, rURL, code)
  642     c.writermem.WriteHeaderNow()
  643 }