...

Source file src/github.com/gin-gonic/gin/gin.go

Documentation: github.com/gin-gonic/gin

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

View as plain text