2022-09-28 17:30:40 +00:00
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
2021-08-12 19:03:24 +00:00
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package gin
import (
"fmt"
"html/template"
"net"
"net/http"
"os"
"path"
2023-02-25 12:12:40 +00:00
"regexp"
2021-08-12 19:03:24 +00:00
"strings"
"sync"
"github.com/gin-gonic/gin/internal/bytesconv"
"github.com/gin-gonic/gin/render"
2022-09-28 17:30:40 +00:00
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
2021-08-12 19:03:24 +00:00
)
const defaultMultipartMemory = 32 << 20 // 32 MB
var (
default404Body = [ ] byte ( "404 page not found" )
default405Body = [ ] byte ( "405 method not allowed" )
)
var defaultPlatform string
2022-09-28 17:30:40 +00:00
var defaultTrustedCIDRs = [ ] * net . IPNet {
{ // 0.0.0.0/0 (IPv4)
IP : net . IP { 0x0 , 0x0 , 0x0 , 0x0 } ,
Mask : net . IPMask { 0x0 , 0x0 , 0x0 , 0x0 } ,
} ,
{ // ::/0 (IPv6)
IP : net . IP { 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 } ,
Mask : net . IPMask { 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 } ,
} ,
}
2021-11-27 14:26:58 +00:00
2023-02-25 12:12:40 +00:00
var regSafePrefix = regexp . MustCompile ( "[^a-zA-Z0-9/-]+" )
var regRemoveRepeatedChar = regexp . MustCompile ( "/{2,}" )
2021-08-12 19:03:24 +00:00
// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func ( * Context )
2022-09-28 17:30:40 +00:00
// HandlersChain defines a HandlerFunc slice.
2021-08-12 19:03:24 +00:00
type HandlersChain [ ] HandlerFunc
2022-09-28 17:30:40 +00:00
// Last returns the last handler in the chain. i.e. the last handler is the main one.
2021-08-12 19:03:24 +00:00
func ( c HandlersChain ) Last ( ) HandlerFunc {
if length := len ( c ) ; length > 0 {
return c [ length - 1 ]
}
return nil
}
// RouteInfo represents a request route's specification which contains method and path and its handler.
type RouteInfo struct {
Method string
Path string
Handler string
HandlerFunc HandlerFunc
}
2022-09-28 17:30:40 +00:00
// RoutesInfo defines a RouteInfo slice.
2021-08-12 19:03:24 +00:00
type RoutesInfo [ ] RouteInfo
// Trusted platforms
const (
2022-09-28 17:30:40 +00:00
// PlatformGoogleAppEngine when running on Google App Engine. Trust X-Appengine-Remote-Addr
2021-08-12 19:03:24 +00:00
// for determining the client's IP
2021-11-27 14:26:58 +00:00
PlatformGoogleAppEngine = "X-Appengine-Remote-Addr"
2022-09-28 17:30:40 +00:00
// PlatformCloudflare when using Cloudflare's CDN. Trust CF-Connecting-IP for determining
2021-08-12 19:03:24 +00:00
// the client's IP
2021-11-27 14:26:58 +00:00
PlatformCloudflare = "CF-Connecting-IP"
2021-08-12 19:03:24 +00:00
)
// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
// Create an instance of Engine, by using New() or Default()
type Engine struct {
RouterGroup
2022-09-28 17:30:40 +00:00
// RedirectTrailingSlash enables automatic redirection if the current route can't be matched but a
2021-08-12 19:03:24 +00:00
// handler for the path with (without) the trailing slash exists.
// For example if /foo/ is requested but a route only exists for /foo, the
// client is redirected to /foo with http status code 301 for GET requests
// and 307 for all other request methods.
RedirectTrailingSlash bool
2022-09-28 17:30:40 +00:00
// RedirectFixedPath if enabled, the router tries to fix the current request path, if no
2021-08-12 19:03:24 +00:00
// handle is registered for it.
// First superfluous path elements like ../ or // are removed.
// Afterwards the router does a case-insensitive lookup of the cleaned path.
// If a handle can be found for this route, the router makes a redirection
// to the corrected path with status code 301 for GET requests and 307 for
// all other request methods.
// For example /FOO and /..//Foo could be redirected to /foo.
// RedirectTrailingSlash is independent of this option.
RedirectFixedPath bool
2022-09-28 17:30:40 +00:00
// HandleMethodNotAllowed if enabled, the router checks if another method is allowed for the
2021-08-12 19:03:24 +00:00
// current route, if the current request can not be routed.
// If this is the case, the request is answered with 'Method Not Allowed'
// and HTTP status code 405.
// If no other Method is allowed, the request is delegated to the NotFound
// handler.
HandleMethodNotAllowed bool
2022-09-28 17:30:40 +00:00
// ForwardedByClientIP if enabled, client IP will be parsed from the request's headers that
2021-08-12 19:03:24 +00:00
// match those stored at `(*gin.Engine).RemoteIPHeaders`. If no IP was
// fetched, it falls back to the IP obtained from
// `(*gin.Context).Request.RemoteAddr`.
ForwardedByClientIP bool
2022-09-28 17:30:40 +00:00
// AppEngine was deprecated.
// Deprecated: USE `TrustedPlatform` WITH VALUE `gin.PlatformGoogleAppEngine` INSTEAD
2021-08-12 19:03:24 +00:00
// #726 #755 If enabled, it will trust some headers starting with
// 'X-AppEngine...' for better integration with that PaaS.
AppEngine bool
2022-09-28 17:30:40 +00:00
// UseRawPath if enabled, the url.RawPath will be used to find parameters.
2021-08-12 19:03:24 +00:00
UseRawPath bool
2022-09-28 17:30:40 +00:00
// UnescapePathValues if true, the path value will be unescaped.
2021-08-12 19:03:24 +00:00
// If UseRawPath is false (by default), the UnescapePathValues effectively is true,
// as url.Path gonna be used, which is already unescaped.
UnescapePathValues bool
// RemoveExtraSlash a parameter can be parsed from the URL even with extra slashes.
// See the PR #1817 and issue #1644
RemoveExtraSlash bool
2022-09-28 17:30:40 +00:00
// RemoteIPHeaders list of headers used to obtain the client IP when
2021-08-12 19:03:24 +00:00
// `(*gin.Engine).ForwardedByClientIP` is `true` and
// `(*gin.Context).Request.RemoteAddr` is matched by at least one of the
2021-11-27 14:26:58 +00:00
// network origins of list defined by `(*gin.Engine).SetTrustedProxies()`.
2021-08-12 19:03:24 +00:00
RemoteIPHeaders [ ] string
2022-09-28 17:30:40 +00:00
// TrustedPlatform if set to a constant of value gin.Platform*, trusts the headers set by
2021-08-12 19:03:24 +00:00
// that platform, for example to determine the client IP
TrustedPlatform string
2022-09-28 17:30:40 +00:00
// MaxMultipartMemory value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
2021-08-12 19:03:24 +00:00
// method call.
MaxMultipartMemory int64
2022-09-28 17:30:40 +00:00
// UseH2C enable h2c support.
UseH2C bool
// ContextWithFallback enable fallback Context.Deadline(), Context.Done(), Context.Err() and Context.Value() when Context.Request.Context() is not nil.
ContextWithFallback bool
2021-08-12 19:03:24 +00:00
delims render . Delims
secureJSONPrefix string
HTMLRender render . HTMLRender
FuncMap template . FuncMap
allNoRoute HandlersChain
allNoMethod HandlersChain
noRoute HandlersChain
noMethod HandlersChain
pool sync . Pool
trees methodTrees
maxParams uint16
2021-11-27 14:26:58 +00:00
maxSections uint16
trustedProxies [ ] string
2021-08-12 19:03:24 +00:00
trustedCIDRs [ ] * net . IPNet
}
2023-02-25 12:12:40 +00:00
var _ IRouter = ( * Engine ) ( nil )
2021-08-12 19:03:24 +00:00
// New returns a new blank Engine instance without any middleware attached.
2022-09-28 17:30:40 +00:00
// By default, the configuration is:
2021-08-12 19:03:24 +00:00
// - RedirectTrailingSlash: true
// - RedirectFixedPath: false
// - HandleMethodNotAllowed: false
// - ForwardedByClientIP: true
// - UseRawPath: false
// - UnescapePathValues: true
func New ( ) * Engine {
debugPrintWARNINGNew ( )
engine := & Engine {
RouterGroup : RouterGroup {
Handlers : nil ,
basePath : "/" ,
root : true ,
} ,
FuncMap : template . FuncMap { } ,
RedirectTrailingSlash : true ,
RedirectFixedPath : false ,
HandleMethodNotAllowed : false ,
ForwardedByClientIP : true ,
RemoteIPHeaders : [ ] string { "X-Forwarded-For" , "X-Real-IP" } ,
TrustedPlatform : defaultPlatform ,
UseRawPath : false ,
RemoveExtraSlash : false ,
UnescapePathValues : true ,
MaxMultipartMemory : defaultMultipartMemory ,
trees : make ( methodTrees , 0 , 9 ) ,
delims : render . Delims { Left : "{{" , Right : "}}" } ,
secureJSONPrefix : "while(1);" ,
2022-09-28 17:30:40 +00:00
trustedProxies : [ ] string { "0.0.0.0/0" , "::/0" } ,
2021-11-27 14:26:58 +00:00
trustedCIDRs : defaultTrustedCIDRs ,
2021-08-12 19:03:24 +00:00
}
engine . RouterGroup . engine = engine
2022-09-28 17:30:40 +00:00
engine . pool . New = func ( ) any {
2022-12-27 08:29:42 +00:00
return engine . allocateContext ( engine . maxParams )
2021-08-12 19:03:24 +00:00
}
return engine
}
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default ( ) * Engine {
debugPrintWARNINGDefault ( )
engine := New ( )
engine . Use ( Logger ( ) , Recovery ( ) )
return engine
}
2022-09-28 17:30:40 +00:00
func ( engine * Engine ) Handler ( ) http . Handler {
if ! engine . UseH2C {
return engine
}
h2s := & http2 . Server { }
return h2c . NewHandler ( engine , h2s )
}
2022-12-27 08:29:42 +00:00
func ( engine * Engine ) allocateContext ( maxParams uint16 ) * Context {
v := make ( Params , 0 , maxParams )
2021-11-27 14:26:58 +00:00
skippedNodes := make ( [ ] skippedNode , 0 , engine . maxSections )
return & Context { engine : engine , params : & v , skippedNodes : & skippedNodes }
2021-08-12 19:03:24 +00:00
}
2022-09-28 17:30:40 +00:00
// Delims sets template left and right delims and returns an Engine instance.
2021-08-12 19:03:24 +00:00
func ( engine * Engine ) Delims ( left , right string ) * Engine {
engine . delims = render . Delims { Left : left , Right : right }
return engine
}
// SecureJsonPrefix sets the secureJSONPrefix used in Context.SecureJSON.
func ( engine * Engine ) SecureJsonPrefix ( prefix string ) * Engine {
engine . secureJSONPrefix = prefix
return engine
}
// LoadHTMLGlob loads HTML files identified by glob pattern
// and associates the result with HTML renderer.
func ( engine * Engine ) LoadHTMLGlob ( pattern string ) {
left := engine . delims . Left
right := engine . delims . Right
templ := template . Must ( template . New ( "" ) . Delims ( left , right ) . Funcs ( engine . FuncMap ) . ParseGlob ( pattern ) )
if IsDebugging ( ) {
debugPrintLoadTemplate ( templ )
engine . HTMLRender = render . HTMLDebug { Glob : pattern , FuncMap : engine . FuncMap , Delims : engine . delims }
return
}
engine . SetHTMLTemplate ( templ )
}
// LoadHTMLFiles loads a slice of HTML files
// and associates the result with HTML renderer.
func ( engine * Engine ) LoadHTMLFiles ( files ... string ) {
if IsDebugging ( ) {
engine . HTMLRender = render . HTMLDebug { Files : files , FuncMap : engine . FuncMap , Delims : engine . delims }
return
}
templ := template . Must ( template . New ( "" ) . Delims ( engine . delims . Left , engine . delims . Right ) . Funcs ( engine . FuncMap ) . ParseFiles ( files ... ) )
engine . SetHTMLTemplate ( templ )
}
// SetHTMLTemplate associate a template with HTML renderer.
func ( engine * Engine ) SetHTMLTemplate ( templ * template . Template ) {
if len ( engine . trees ) > 0 {
debugPrintWARNINGSetHTMLTemplate ( )
}
engine . HTMLRender = render . HTMLProduction { Template : templ . Funcs ( engine . FuncMap ) }
}
// SetFuncMap sets the FuncMap used for template.FuncMap.
func ( engine * Engine ) SetFuncMap ( funcMap template . FuncMap ) {
engine . FuncMap = funcMap
}
2022-09-28 17:30:40 +00:00
// NoRoute adds handlers for NoRoute. It returns a 404 code by default.
2021-08-12 19:03:24 +00:00
func ( engine * Engine ) NoRoute ( handlers ... HandlerFunc ) {
engine . noRoute = handlers
engine . rebuild404Handlers ( )
}
2021-11-27 14:26:58 +00:00
// NoMethod sets the handlers called when Engine.HandleMethodNotAllowed = true.
2021-08-12 19:03:24 +00:00
func ( engine * Engine ) NoMethod ( handlers ... HandlerFunc ) {
engine . noMethod = handlers
engine . rebuild405Handlers ( )
}
2022-09-28 17:30:40 +00:00
// Use attaches a global middleware to the router. i.e. the middleware attached through Use() will be
2021-08-12 19:03:24 +00:00
// included in the handlers chain for every single request. Even 404, 405, static files...
// For example, this is the right place for a logger or error management middleware.
func ( engine * Engine ) Use ( middleware ... HandlerFunc ) IRoutes {
engine . RouterGroup . Use ( middleware ... )
engine . rebuild404Handlers ( )
engine . rebuild405Handlers ( )
return engine
}
func ( engine * Engine ) rebuild404Handlers ( ) {
engine . allNoRoute = engine . combineHandlers ( engine . noRoute )
}
func ( engine * Engine ) rebuild405Handlers ( ) {
engine . allNoMethod = engine . combineHandlers ( engine . noMethod )
}
func ( engine * Engine ) addRoute ( method , path string , handlers HandlersChain ) {
assert1 ( path [ 0 ] == '/' , "path must begin with '/'" )
assert1 ( method != "" , "HTTP method can not be empty" )
assert1 ( len ( handlers ) > 0 , "there must be at least one handler" )
debugPrintRoute ( method , path , handlers )
root := engine . trees . get ( method )
if root == nil {
root = new ( node )
root . fullPath = "/"
engine . trees = append ( engine . trees , methodTree { method : method , root : root } )
}
root . addRoute ( path , handlers )
// Update maxParams
if paramsCount := countParams ( path ) ; paramsCount > engine . maxParams {
engine . maxParams = paramsCount
}
2021-11-27 14:26:58 +00:00
if sectionsCount := countSections ( path ) ; sectionsCount > engine . maxSections {
engine . maxSections = sectionsCount
}
2021-08-12 19:03:24 +00:00
}
// Routes returns a slice of registered routes, including some useful information, such as:
// the http method, path and the handler name.
func ( engine * Engine ) Routes ( ) ( routes RoutesInfo ) {
for _ , tree := range engine . trees {
routes = iterate ( "" , tree . method , routes , tree . root )
}
return routes
}
func iterate ( path , method string , routes RoutesInfo , root * node ) RoutesInfo {
path += root . path
if len ( root . handlers ) > 0 {
handlerFunc := root . handlers . Last ( )
routes = append ( routes , RouteInfo {
Method : method ,
Path : path ,
Handler : nameOfFunction ( handlerFunc ) ,
HandlerFunc : handlerFunc ,
} )
}
for _ , child := range root . children {
routes = iterate ( path , method , routes , child )
}
return routes
}
// Run attaches the router to a http.Server and starts listening and serving HTTP requests.
// It is a shortcut for http.ListenAndServe(addr, router)
// Note: this method will block the calling goroutine indefinitely unless an error happens.
func ( engine * Engine ) Run ( addr ... string ) ( err error ) {
defer func ( ) { debugPrintError ( err ) } ( )
2021-11-27 14:26:58 +00:00
if engine . isUnsafeTrustedProxies ( ) {
debugPrint ( "[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details." )
2021-08-12 19:03:24 +00:00
}
address := resolveAddress ( addr )
debugPrint ( "Listening and serving HTTP on %s\n" , address )
2022-09-28 17:30:40 +00:00
err = http . ListenAndServe ( address , engine . Handler ( ) )
2021-08-12 19:03:24 +00:00
return
}
func ( engine * Engine ) prepareTrustedCIDRs ( ) ( [ ] * net . IPNet , error ) {
2021-11-27 14:26:58 +00:00
if engine . trustedProxies == nil {
2021-08-12 19:03:24 +00:00
return nil , nil
}
2021-11-27 14:26:58 +00:00
cidr := make ( [ ] * net . IPNet , 0 , len ( engine . trustedProxies ) )
for _ , trustedProxy := range engine . trustedProxies {
2021-08-12 19:03:24 +00:00
if ! strings . Contains ( trustedProxy , "/" ) {
ip := parseIP ( trustedProxy )
if ip == nil {
return cidr , & net . ParseError { Type : "IP address" , Text : trustedProxy }
}
switch len ( ip ) {
case net . IPv4len :
trustedProxy += "/32"
case net . IPv6len :
trustedProxy += "/128"
}
}
_ , cidrNet , err := net . ParseCIDR ( trustedProxy )
if err != nil {
return cidr , err
}
cidr = append ( cidr , cidrNet )
}
return cidr , nil
}
2021-11-27 14:26:58 +00:00
// SetTrustedProxies set a list of network origins (IPv4 addresses,
// IPv4 CIDRs, IPv6 addresses or IPv6 CIDRs) from which to trust
// request's headers that contain alternative client IP when
// `(*gin.Engine).ForwardedByClientIP` is `true`. `TrustedProxies`
// feature is enabled by default, and it also trusts all proxies
// by default. If you want to disable this feature, use
// Engine.SetTrustedProxies(nil), then Context.ClientIP() will
// return the remote address directly.
2021-08-12 19:03:24 +00:00
func ( engine * Engine ) SetTrustedProxies ( trustedProxies [ ] string ) error {
2021-11-27 14:26:58 +00:00
engine . trustedProxies = trustedProxies
2021-08-12 19:03:24 +00:00
return engine . parseTrustedProxies ( )
}
2022-09-28 17:30:40 +00:00
// isUnsafeTrustedProxies checks if Engine.trustedCIDRs contains all IPs, it's not safe if it has (returns true)
2021-11-27 14:26:58 +00:00
func ( engine * Engine ) isUnsafeTrustedProxies ( ) bool {
2022-09-28 17:30:40 +00:00
return engine . isTrustedProxy ( net . ParseIP ( "0.0.0.0" ) ) || engine . isTrustedProxy ( net . ParseIP ( "::" ) )
2021-11-27 14:26:58 +00:00
}
// parseTrustedProxies parse Engine.trustedProxies to Engine.trustedCIDRs
2021-08-12 19:03:24 +00:00
func ( engine * Engine ) parseTrustedProxies ( ) error {
trustedCIDRs , err := engine . prepareTrustedCIDRs ( )
engine . trustedCIDRs = trustedCIDRs
return err
}
2022-09-28 17:30:40 +00:00
// isTrustedProxy will check whether the IP address is included in the trusted list according to Engine.trustedCIDRs
func ( engine * Engine ) isTrustedProxy ( ip net . IP ) bool {
if engine . trustedCIDRs == nil {
return false
}
for _ , cidr := range engine . trustedCIDRs {
if cidr . Contains ( ip ) {
return true
}
}
return false
}
// validateHeader will parse X-Forwarded-For header and return the trusted client IP address
func ( engine * Engine ) validateHeader ( header string ) ( clientIP string , valid bool ) {
if header == "" {
return "" , false
}
items := strings . Split ( header , "," )
for i := len ( items ) - 1 ; i >= 0 ; i -- {
ipStr := strings . TrimSpace ( items [ i ] )
ip := net . ParseIP ( ipStr )
if ip == nil {
break
}
// X-Forwarded-For is appended by proxy
// Check IPs in reverse order and stop when find untrusted proxy
if ( i == 0 ) || ( ! engine . isTrustedProxy ( ip ) ) {
return ipStr , true
}
}
return "" , false
}
2021-08-12 19:03:24 +00:00
// parseIP parse a string representation of an IP and returns a net.IP with the
// minimum byte representation or nil if input is invalid.
func parseIP ( ip string ) net . IP {
parsedIP := net . ParseIP ( ip )
if ipv4 := parsedIP . To4 ( ) ; ipv4 != nil {
// return ip in a 4-byte representation
return ipv4
}
// return ip in a 16-byte representation or nil
return parsedIP
}
// RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests.
// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
// Note: this method will block the calling goroutine indefinitely unless an error happens.
func ( engine * Engine ) RunTLS ( addr , certFile , keyFile string ) ( err error ) {
debugPrint ( "Listening and serving HTTPS on %s\n" , addr )
defer func ( ) { debugPrintError ( err ) } ( )
2021-11-27 14:26:58 +00:00
if engine . isUnsafeTrustedProxies ( ) {
debugPrint ( "[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details." )
2021-08-12 19:03:24 +00:00
}
2022-09-28 17:30:40 +00:00
err = http . ListenAndServeTLS ( addr , certFile , keyFile , engine . Handler ( ) )
2021-08-12 19:03:24 +00:00
return
}
// RunUnix attaches the router to a http.Server and starts listening and serving HTTP requests
2022-09-28 17:30:40 +00:00
// through the specified unix socket (i.e. a file).
2021-08-12 19:03:24 +00:00
// Note: this method will block the calling goroutine indefinitely unless an error happens.
func ( engine * Engine ) RunUnix ( file string ) ( err error ) {
debugPrint ( "Listening and serving HTTP on unix:/%s" , file )
defer func ( ) { debugPrintError ( err ) } ( )
2021-11-27 14:26:58 +00:00
if engine . isUnsafeTrustedProxies ( ) {
debugPrint ( "[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
2023-06-01 21:20:16 +00:00
"Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details." )
2021-08-12 19:03:24 +00:00
}
listener , err := net . Listen ( "unix" , file )
if err != nil {
return
}
defer listener . Close ( )
defer os . Remove ( file )
2022-09-28 17:30:40 +00:00
err = http . Serve ( listener , engine . Handler ( ) )
2021-08-12 19:03:24 +00:00
return
}
// RunFd attaches the router to a http.Server and starts listening and serving HTTP requests
// through the specified file descriptor.
// Note: this method will block the calling goroutine indefinitely unless an error happens.
func ( engine * Engine ) RunFd ( fd int ) ( err error ) {
debugPrint ( "Listening and serving HTTP on fd@%d" , fd )
defer func ( ) { debugPrintError ( err ) } ( )
2021-11-27 14:26:58 +00:00
if engine . isUnsafeTrustedProxies ( ) {
debugPrint ( "[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
2023-06-01 21:20:16 +00:00
"Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details." )
2021-08-12 19:03:24 +00:00
}
f := os . NewFile ( uintptr ( fd ) , fmt . Sprintf ( "fd@%d" , fd ) )
listener , err := net . FileListener ( f )
if err != nil {
return
}
defer listener . Close ( )
err = engine . RunListener ( listener )
return
}
// RunListener attaches the router to a http.Server and starts listening and serving HTTP requests
// through the specified net.Listener
func ( engine * Engine ) RunListener ( listener net . Listener ) ( err error ) {
debugPrint ( "Listening and serving HTTP on listener what's bind with address@%s" , listener . Addr ( ) )
defer func ( ) { debugPrintError ( err ) } ( )
2021-11-27 14:26:58 +00:00
if engine . isUnsafeTrustedProxies ( ) {
debugPrint ( "[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
2023-06-01 21:20:16 +00:00
"Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details." )
2021-08-12 19:03:24 +00:00
}
2022-09-28 17:30:40 +00:00
err = http . Serve ( listener , engine . Handler ( ) )
2021-08-12 19:03:24 +00:00
return
}
// ServeHTTP conforms to the http.Handler interface.
func ( engine * Engine ) ServeHTTP ( w http . ResponseWriter , req * http . Request ) {
c := engine . pool . Get ( ) . ( * Context )
c . writermem . reset ( w )
c . Request = req
c . reset ( )
engine . handleHTTPRequest ( c )
engine . pool . Put ( c )
}
2022-09-28 17:30:40 +00:00
// HandleContext re-enters a context that has been rewritten.
2021-08-12 19:03:24 +00:00
// This can be done by setting c.Request.URL.Path to your new target.
2022-09-28 17:30:40 +00:00
// Disclaimer: You can loop yourself to deal with this, use wisely.
2021-08-12 19:03:24 +00:00
func ( engine * Engine ) HandleContext ( c * Context ) {
oldIndexValue := c . index
c . reset ( )
engine . handleHTTPRequest ( c )
c . index = oldIndexValue
}
func ( engine * Engine ) handleHTTPRequest ( c * Context ) {
httpMethod := c . Request . Method
rPath := c . Request . URL . Path
unescape := false
if engine . UseRawPath && len ( c . Request . URL . RawPath ) > 0 {
rPath = c . Request . URL . RawPath
unescape = engine . UnescapePathValues
}
if engine . RemoveExtraSlash {
rPath = cleanPath ( rPath )
}
// Find root of the tree for the given HTTP method
t := engine . trees
for i , tl := 0 , len ( t ) ; i < tl ; i ++ {
if t [ i ] . method != httpMethod {
continue
}
root := t [ i ] . root
// Find route in tree
2021-11-27 14:26:58 +00:00
value := root . getValue ( rPath , c . params , c . skippedNodes , unescape )
2021-08-12 19:03:24 +00:00
if value . params != nil {
c . Params = * value . params
}
if value . handlers != nil {
c . handlers = value . handlers
c . fullPath = value . fullPath
c . Next ( )
c . writermem . WriteHeaderNow ( )
return
}
2022-09-28 17:30:40 +00:00
if httpMethod != http . MethodConnect && rPath != "/" {
2021-08-12 19:03:24 +00:00
if value . tsr && engine . RedirectTrailingSlash {
redirectTrailingSlash ( c )
return
}
if engine . RedirectFixedPath && redirectFixedPath ( c , root , engine . RedirectFixedPath ) {
return
}
}
break
}
if engine . HandleMethodNotAllowed {
for _ , tree := range engine . trees {
if tree . method == httpMethod {
continue
}
2021-11-27 14:26:58 +00:00
if value := tree . root . getValue ( rPath , nil , c . skippedNodes , unescape ) ; value . handlers != nil {
2021-08-12 19:03:24 +00:00
c . handlers = engine . allNoMethod
serveError ( c , http . StatusMethodNotAllowed , default405Body )
return
}
}
}
c . handlers = engine . allNoRoute
serveError ( c , http . StatusNotFound , default404Body )
}
var mimePlain = [ ] string { MIMEPlain }
func serveError ( c * Context , code int , defaultMessage [ ] byte ) {
c . writermem . status = code
c . Next ( )
if c . writermem . Written ( ) {
return
}
if c . writermem . Status ( ) == code {
c . writermem . Header ( ) [ "Content-Type" ] = mimePlain
_ , err := c . Writer . Write ( defaultMessage )
if err != nil {
debugPrint ( "cannot write message to writer during serve error: %v" , err )
}
return
}
c . writermem . WriteHeaderNow ( )
}
func redirectTrailingSlash ( c * Context ) {
req := c . Request
p := req . URL . Path
if prefix := path . Clean ( c . Request . Header . Get ( "X-Forwarded-Prefix" ) ) ; prefix != "." {
2023-02-25 12:12:40 +00:00
prefix = regSafePrefix . ReplaceAllString ( prefix , "" )
prefix = regRemoveRepeatedChar . ReplaceAllString ( prefix , "/" )
2021-08-12 19:03:24 +00:00
p = prefix + "/" + req . URL . Path
}
req . URL . Path = p + "/"
if length := len ( p ) ; length > 1 && p [ length - 1 ] == '/' {
req . URL . Path = p [ : length - 1 ]
}
redirectRequest ( c )
}
func redirectFixedPath ( c * Context , root * node , trailingSlash bool ) bool {
req := c . Request
rPath := req . URL . Path
if fixedPath , ok := root . findCaseInsensitivePath ( cleanPath ( rPath ) , trailingSlash ) ; ok {
req . URL . Path = bytesconv . BytesToString ( fixedPath )
redirectRequest ( c )
return true
}
return false
}
func redirectRequest ( c * Context ) {
req := c . Request
rPath := req . URL . Path
rURL := req . URL . String ( )
code := http . StatusMovedPermanently // Permanent redirect, request with GET method
if req . Method != http . MethodGet {
code = http . StatusTemporaryRedirect
}
debugPrint ( "redirecting request %d: %s --> %s" , code , rPath , rURL )
http . Redirect ( c . Writer , req , rURL , code )
c . writermem . WriteHeaderNow ( )
}