1
2
3
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
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 {
35 IP: net.IP{0x0, 0x0, 0x0, 0x0},
36 Mask: net.IPMask{0x0, 0x0, 0x0, 0x0},
37 },
38 {
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
48 type HandlerFunc func(*Context)
49
50
51 type HandlersChain []HandlerFunc
52
53
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
62 type RouteInfo struct {
63 Method string
64 Path string
65 Handler string
66 HandlerFunc HandlerFunc
67 }
68
69
70 type RoutesInfo []RouteInfo
71
72
73 const (
74
75
76 PlatformGoogleAppEngine = "X-Appengine-Remote-Addr"
77
78
79 PlatformCloudflare = "CF-Connecting-IP"
80 )
81
82
83
84 type Engine struct {
85 RouterGroup
86
87
88
89
90
91
92 RedirectTrailingSlash bool
93
94
95
96
97
98
99
100
101
102
103 RedirectFixedPath bool
104
105
106
107
108
109
110
111 HandleMethodNotAllowed bool
112
113
114
115
116
117 ForwardedByClientIP bool
118
119
120
121
122
123 AppEngine bool
124
125
126 UseRawPath bool
127
128
129
130
131 UnescapePathValues bool
132
133
134
135 RemoveExtraSlash bool
136
137
138
139
140
141 RemoteIPHeaders []string
142
143
144
145 TrustedPlatform string
146
147
148
149 MaxMultipartMemory int64
150
151
152 UseH2C bool
153
154
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
176
177
178
179
180
181
182
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
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
239 func (engine *Engine) Delims(left, right string) *Engine {
240 engine.delims = render.Delims{Left: left, Right: right}
241 return engine
242 }
243
244
245 func (engine *Engine) SecureJsonPrefix(prefix string) *Engine {
246 engine.secureJSONPrefix = prefix
247 return engine
248 }
249
250
251
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
267
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
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
288 func (engine *Engine) SetFuncMap(funcMap template.FuncMap) {
289 engine.FuncMap = funcMap
290 }
291
292
293 func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
294 engine.noRoute = handlers
295 engine.rebuild404Handlers()
296 }
297
298
299 func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
300 engine.noMethod = handlers
301 engine.rebuild405Handlers()
302 }
303
304
305
306
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
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
348
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
374
375
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
420
421
422
423
424
425
426
427 func (engine *Engine) SetTrustedProxies(trustedProxies []string) error {
428 engine.trustedProxies = trustedProxies
429 return engine.parseTrustedProxies()
430 }
431
432
433 func (engine *Engine) isUnsafeTrustedProxies() bool {
434 return engine.isTrustedProxy(net.ParseIP("0.0.0.0")) || engine.isTrustedProxy(net.ParseIP("::"))
435 }
436
437
438 func (engine *Engine) parseTrustedProxies() error {
439 trustedCIDRs, err := engine.prepareTrustedCIDRs()
440 engine.trustedCIDRs = trustedCIDRs
441 return err
442 }
443
444
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
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
471
472 if (i == 0) || (!engine.isTrustedProxy(ip)) {
473 return ipStr, true
474 }
475 }
476 return "", false
477 }
478
479
480
481 func parseIP(ip string) net.IP {
482 parsedIP := net.ParseIP(ip)
483
484 if ipv4 := parsedIP.To4(); ipv4 != nil {
485
486 return ipv4
487 }
488
489
490 return parsedIP
491 }
492
493
494
495
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
510
511
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
533
534
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
555
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
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
582
583
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
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
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
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