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