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
	"strings"
15
	"sync"
16

17
	"github.com/gin-gonic/gin/internal/bytesconv"
18
	"github.com/gin-gonic/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 16
	if length := len(c); length > 0 {
39 16
		return c[length-1]
40
	}
41 16
	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, it will trust the CF-Connecting-IP header to determine the
109
	// IP of the client.
110
	CloudflareProxy bool
111

112
	// If enabled, the url.RawPath will be used to find parameters.
113
	UseRawPath bool
114

115
	// If true, the path value will be unescaped.
116
	// If UseRawPath is false (by default), the UnescapePathValues effectively is true,
117
	// as url.Path gonna be used, which is already unescaped.
118
	UnescapePathValues bool
119

120
	// Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
121
	// method call.
122
	MaxMultipartMemory int64
123

124
	// RemoveExtraSlash a parameter can be parsed from the URL even with extra slashes.
125
	// See the PR #1817 and issue #1644
126
	RemoveExtraSlash bool
127

128
	delims           render.Delims
129
	secureJSONPrefix string
130
	HTMLRender       render.HTMLRender
131
	FuncMap          template.FuncMap
132
	allNoRoute       HandlersChain
133
	allNoMethod      HandlersChain
134
	noRoute          HandlersChain
135
	noMethod         HandlersChain
136
	pool             sync.Pool
137
	trees            methodTrees
138
	maxParams        uint16
139
	trustedCIDRs     []*net.IPNet
140
}
141

142
var _ IRouter = &Engine{}
143

144
// New returns a new blank Engine instance without any middleware attached.
145
// By default the configuration is:
146
// - RedirectTrailingSlash:  true
147
// - RedirectFixedPath:      false
148
// - HandleMethodNotAllowed: false
149
// - ForwardedByClientIP:    true
150
// - UseRawPath:             false
151
// - UnescapePathValues:     true
152
func New() *Engine {
153 16
	debugPrintWARNINGNew()
154 16
	engine := &Engine{
155 16
		RouterGroup: RouterGroup{
156 16
			Handlers: nil,
157 16
			basePath: "/",
158 16
			root:     true,
159 16
		},
160 16
		FuncMap:                template.FuncMap{},
161 16
		RedirectTrailingSlash:  true,
162 16
		RedirectFixedPath:      false,
163 16
		HandleMethodNotAllowed: false,
164 16
		ForwardedByClientIP:    true,
165 16
		RemoteIPHeaders:        []string{"X-Forwarded-For", "X-Real-IP"},
166 16
		TrustedProxies:         []string{"0.0.0.0/0"},
167 16
		AppEngine:              defaultAppEngine,
168 16
		UseRawPath:             false,
169 16
		RemoveExtraSlash:       false,
170 16
		UnescapePathValues:     true,
171 16
		MaxMultipartMemory:     defaultMultipartMemory,
172 16
		trees:                  make(methodTrees, 0, 9),
173 16
		delims:                 render.Delims{Left: "{{", Right: "}}"},
174 16
		secureJSONPrefix:       "while(1);",
175
	}
176 16
	engine.RouterGroup.engine = engine
177 16
	engine.pool.New = func() interface{} {
178 16
		return engine.allocateContext()
179
	}
180 16
	return engine
181
}
182

183
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
184
func Default() *Engine {
185 16
	debugPrintWARNINGDefault()
186 16
	engine := New()
187 16
	engine.Use(Logger(), Recovery())
188 16
	return engine
189
}
190

191
func (engine *Engine) allocateContext() *Context {
192 16
	v := make(Params, 0, engine.maxParams)
193 16
	return &Context{engine: engine, params: &v}
194
}
195

196
// Delims sets template left and right delims and returns a Engine instance.
197
func (engine *Engine) Delims(left, right string) *Engine {
198 16
	engine.delims = render.Delims{Left: left, Right: right}
199 16
	return engine
200
}
201

202
// SecureJsonPrefix sets the secureJSONPrefix used in Context.SecureJSON.
203
func (engine *Engine) SecureJsonPrefix(prefix string) *Engine {
204 16
	engine.secureJSONPrefix = prefix
205 16
	return engine
206
}
207

208
// LoadHTMLGlob loads HTML files identified by glob pattern
209
// and associates the result with HTML renderer.
210
func (engine *Engine) LoadHTMLGlob(pattern string) {
211 16
	left := engine.delims.Left
212 16
	right := engine.delims.Right
213 16
	templ := template.Must(template.New("").Delims(left, right).Funcs(engine.FuncMap).ParseGlob(pattern))
214

215 16
	if IsDebugging() {
216 16
		debugPrintLoadTemplate(templ)
217 16
		engine.HTMLRender = render.HTMLDebug{Glob: pattern, FuncMap: engine.FuncMap, Delims: engine.delims}
218 16
		return
219
	}
220

221 16
	engine.SetHTMLTemplate(templ)
222
}
223

224
// LoadHTMLFiles loads a slice of HTML files
225
// and associates the result with HTML renderer.
226
func (engine *Engine) LoadHTMLFiles(files ...string) {
227 16
	if IsDebugging() {
228 16
		engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims}
229 16
		return
230
	}
231

232 16
	templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFiles(files...))
233 16
	engine.SetHTMLTemplate(templ)
234
}
235

236
// SetHTMLTemplate associate a template with HTML renderer.
237
func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
238 16
	if len(engine.trees) > 0 {
239 16
		debugPrintWARNINGSetHTMLTemplate()
240
	}
241

242 16
	engine.HTMLRender = render.HTMLProduction{Template: templ.Funcs(engine.FuncMap)}
243
}
244

245
// SetFuncMap sets the FuncMap used for template.FuncMap.
246
func (engine *Engine) SetFuncMap(funcMap template.FuncMap) {
247 16
	engine.FuncMap = funcMap
248
}
249

250
// NoRoute adds handlers for NoRoute. It return a 404 code by default.
251
func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
252 16
	engine.noRoute = handlers
253 16
	engine.rebuild404Handlers()
254
}
255

256
// NoMethod sets the handlers called when... TODO.
257
func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
258 16
	engine.noMethod = handlers
259 16
	engine.rebuild405Handlers()
260
}
261

262
// Use attaches a global middleware to the router. ie. the middleware attached though Use() will be
263
// included in the handlers chain for every single request. Even 404, 405, static files...
264
// For example, this is the right place for a logger or error management middleware.
265
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
266 16
	engine.RouterGroup.Use(middleware...)
267 16
	engine.rebuild404Handlers()
268 16
	engine.rebuild405Handlers()
269 16
	return engine
270
}
271

272
func (engine *Engine) rebuild404Handlers() {
273 16
	engine.allNoRoute = engine.combineHandlers(engine.noRoute)
274
}
275

276
func (engine *Engine) rebuild405Handlers() {
277 16
	engine.allNoMethod = engine.combineHandlers(engine.noMethod)
278
}
279

280
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
281 16
	assert1(path[0] == '/', "path must begin with '/'")
282 16
	assert1(method != "", "HTTP method can not be empty")
283 16
	assert1(len(handlers) > 0, "there must be at least one handler")
284

285 16
	debugPrintRoute(method, path, handlers)
286

287 16
	root := engine.trees.get(method)
288 16
	if root == nil {
289 16
		root = new(node)
290 16
		root.fullPath = "/"
291 16
		engine.trees = append(engine.trees, methodTree{method: method, root: root})
292
	}
293 16
	root.addRoute(path, handlers)
294

295
	// Update maxParams
296 16
	if paramsCount := countParams(path); paramsCount > engine.maxParams {
297 16
		engine.maxParams = paramsCount
298
	}
299
}
300

301
// Routes returns a slice of registered routes, including some useful information, such as:
302
// the http method, path and the handler name.
303
func (engine *Engine) Routes() (routes RoutesInfo) {
304
	for _, tree := range engine.trees {
305 16
		routes = iterate("", tree.method, routes, tree.root)
306
	}
307 16
	return routes
308
}
309

310
func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
311 16
	path += root.path
312 16
	if len(root.handlers) > 0 {
313 16
		handlerFunc := root.handlers.Last()
314 16
		routes = append(routes, RouteInfo{
315 16
			Method:      method,
316 16
			Path:        path,
317 16
			Handler:     nameOfFunction(handlerFunc),
318 16
			HandlerFunc: handlerFunc,
319 16
		})
320
	}
321
	for _, child := range root.children {
322 16
		routes = iterate(path, method, routes, child)
323
	}
324 16
	return routes
325
}
326

327
// Run attaches the router to a http.Server and starts listening and serving HTTP requests.
328
// It is a shortcut for http.ListenAndServe(addr, router)
329
// Note: this method will block the calling goroutine indefinitely unless an error happens.
330
func (engine *Engine) Run(addr ...string) (err error) {
331 16
	defer func() { debugPrintError(err) }()
332

333 16
	err = engine.parseTrustedProxies()
334 16
	if err != nil {
335 16
		return err
336
	}
337

338 16
	address := resolveAddress(addr)
339 16
	debugPrint("Listening and serving HTTP on %s\n", address)
340 16
	err = http.ListenAndServe(address, engine)
341 16
	return
342
}
343

344
func (engine *Engine) prepareTrustedCIDRs() ([]*net.IPNet, error) {
345 16
	if engine.TrustedProxies == nil {
346 16
		return nil, nil
347
	}
348

349 16
	cidr := make([]*net.IPNet, 0, len(engine.TrustedProxies))
350
	for _, trustedProxy := range engine.TrustedProxies {
351 16
		if !strings.Contains(trustedProxy, "/") {
352 16
			ip := parseIP(trustedProxy)
353 16
			if ip == nil {
354 16
				return cidr, &net.ParseError{Type: "IP address", Text: trustedProxy}
355
			}
356

357 16
			switch len(ip) {
358 16
			case net.IPv4len:
359 16
				trustedProxy += "/32"
360 16
			case net.IPv6len:
361 16
				trustedProxy += "/128"
362
			}
363
		}
364 16
		_, cidrNet, err := net.ParseCIDR(trustedProxy)
365 16
		if err != nil {
366 16
			return cidr, err
367
		}
368 16
		cidr = append(cidr, cidrNet)
369
	}
370 16
	return cidr, nil
371
}
372

373
// SetTrustedProxies  set Engine.TrustedProxies
374
func (engine *Engine) SetTrustedProxies(trustedProxies []string) error {
375 16
	engine.TrustedProxies = trustedProxies
376 16
	return engine.parseTrustedProxies()
377
}
378

379
// parseTrustedProxies parse Engine.TrustedProxies to Engine.trustedCIDRs
380
func (engine *Engine) parseTrustedProxies() error {
381 16
	trustedCIDRs, err := engine.prepareTrustedCIDRs()
382 16
	engine.trustedCIDRs = trustedCIDRs
383 16
	return err
384
}
385

386
// parseIP parse a string representation of an IP and returns a net.IP with the
387
// minimum byte representation or nil if input is invalid.
388
func parseIP(ip string) net.IP {
389 16
	parsedIP := net.ParseIP(ip)
390

391 16
	if ipv4 := parsedIP.To4(); ipv4 != nil {
392
		// return ip in a 4-byte representation
393 16
		return ipv4
394
	}
395

396
	// return ip in a 16-byte representation or nil
397 16
	return parsedIP
398
}
399

400
// RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests.
401
// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
402
// Note: this method will block the calling goroutine indefinitely unless an error happens.
403
func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) {
404 16
	debugPrint("Listening and serving HTTPS on %s\n", addr)
405 16
	defer func() { debugPrintError(err) }()
406

407 16
	err = engine.parseTrustedProxies()
408 16
	if err != nil {
409 16
		return err
410
	}
411

412 16
	err = http.ListenAndServeTLS(addr, certFile, keyFile, engine)
413 16
	return
414
}
415

416
// RunUnix attaches the router to a http.Server and starts listening and serving HTTP requests
417
// through the specified unix socket (ie. a file).
418
// Note: this method will block the calling goroutine indefinitely unless an error happens.
419
func (engine *Engine) RunUnix(file string) (err error) {
420 16
	debugPrint("Listening and serving HTTP on unix:/%s", file)
421 16
	defer func() { debugPrintError(err) }()
422

423 16
	err = engine.parseTrustedProxies()
424 16
	if err != nil {
425 16
		return err
426
	}
427

428 16
	listener, err := net.Listen("unix", file)
429 16
	if err != nil {
430 16
		return
431
	}
432 16
	defer listener.Close()
433 16
	defer os.Remove(file)
434

435 16
	err = http.Serve(listener, engine)
436 16
	return
437
}
438

439
// RunFd attaches the router to a http.Server and starts listening and serving HTTP requests
440
// through the specified file descriptor.
441
// Note: this method will block the calling goroutine indefinitely unless an error happens.
442
func (engine *Engine) RunFd(fd int) (err error) {
443 16
	debugPrint("Listening and serving HTTP on fd@%d", fd)
444 16
	defer func() { debugPrintError(err) }()
445

446 16
	err = engine.parseTrustedProxies()
447 16
	if err != nil {
448 16
		return err
449
	}
450

451 16
	f := os.NewFile(uintptr(fd), fmt.Sprintf("fd@%d", fd))
452 16
	listener, err := net.FileListener(f)
453 16
	if err != nil {
454 16
		return
455
	}
456 16
	defer listener.Close()
457 16
	err = engine.RunListener(listener)
458 16
	return
459
}
460

461
// RunListener attaches the router to a http.Server and starts listening and serving HTTP requests
462
// through the specified net.Listener
463
func (engine *Engine) RunListener(listener net.Listener) (err error) {
464 16
	debugPrint("Listening and serving HTTP on listener what's bind with address@%s", listener.Addr())
465 16
	defer func() { debugPrintError(err) }()
466

467 16
	err = engine.parseTrustedProxies()
468 16
	if err != nil {
469 16
		return err
470
	}
471

472 16
	err = http.Serve(listener, engine)
473 16
	return
474
}
475

476
// ServeHTTP conforms to the http.Handler interface.
477
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
478 16
	c := engine.pool.Get().(*Context)
479 16
	c.writermem.reset(w)
480 16
	c.Request = req
481 16
	c.reset()
482

483 16
	engine.handleHTTPRequest(c)
484

485 16
	engine.pool.Put(c)
486
}
487

488
// HandleContext re-enter a context that has been rewritten.
489
// This can be done by setting c.Request.URL.Path to your new target.
490
// Disclaimer: You can loop yourself to death with this, use wisely.
491
func (engine *Engine) HandleContext(c *Context) {
492 16
	oldIndexValue := c.index
493 16
	c.reset()
494 16
	engine.handleHTTPRequest(c)
495

496 16
	c.index = oldIndexValue
497
}
498

499
func (engine *Engine) handleHTTPRequest(c *Context) {
500 16
	httpMethod := c.Request.Method
501 16
	rPath := c.Request.URL.Path
502 16
	unescape := false
503 16
	if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
504 16
		rPath = c.Request.URL.RawPath
505 16
		unescape = engine.UnescapePathValues
506
	}
507

508 16
	if engine.RemoveExtraSlash {
509 16
		rPath = cleanPath(rPath)
510
	}
511

512
	// Find root of the tree for the given HTTP method
513 16
	t := engine.trees
514
	for i, tl := 0, len(t); i < tl; i++ {
515 16
		if t[i].method != httpMethod {
516 16
			continue
517
		}
518 16
		root := t[i].root
519
		// Find route in tree
520 16
		value := root.getValue(rPath, c.params, unescape)
521 16
		if value.params != nil {
522 16
			c.Params = *value.params
523
		}
524 16
		if value.handlers != nil {
525 16
			c.handlers = value.handlers
526 16
			c.fullPath = value.fullPath
527 16
			c.Next()
528 16
			c.writermem.WriteHeaderNow()
529 16
			return
530
		}
531 16
		if httpMethod != "CONNECT" && rPath != "/" {
532 16
			if value.tsr && engine.RedirectTrailingSlash {
533 16
				redirectTrailingSlash(c)
534 16
				return
535
			}
536 16
			if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
537 16
				return
538
			}
539
		}
540 16
		break
541
	}
542

543 16
	if engine.HandleMethodNotAllowed {
544
		for _, tree := range engine.trees {
545 16
			if tree.method == httpMethod {
546 16
				continue
547
			}
548 16
			if value := tree.root.getValue(rPath, nil, unescape); value.handlers != nil {
549 16
				c.handlers = engine.allNoMethod
550 16
				serveError(c, http.StatusMethodNotAllowed, default405Body)
551 16
				return
552
			}
553
		}
554
	}
555 16
	c.handlers = engine.allNoRoute
556 16
	serveError(c, http.StatusNotFound, default404Body)
557
}
558

559
var mimePlain = []string{MIMEPlain}
560

561
func serveError(c *Context, code int, defaultMessage []byte) {
562 16
	c.writermem.status = code
563 16
	c.Next()
564 16
	if c.writermem.Written() {
565 16
		return
566
	}
567 16
	if c.writermem.Status() == code {
568 16
		c.writermem.Header()["Content-Type"] = mimePlain
569 16
		_, err := c.Writer.Write(defaultMessage)
570 16
		if err != nil {
571 0
			debugPrint("cannot write message to writer during serve error: %v", err)
572
		}
573 16
		return
574
	}
575 16
	c.writermem.WriteHeaderNow()
576
}
577

578
func redirectTrailingSlash(c *Context) {
579 16
	req := c.Request
580 16
	p := req.URL.Path
581 16
	if prefix := path.Clean(c.Request.Header.Get("X-Forwarded-Prefix")); prefix != "." {
582 16
		p = prefix + "/" + req.URL.Path
583
	}
584 16
	req.URL.Path = p + "/"
585 16
	if length := len(p); length > 1 && p[length-1] == '/' {
586 16
		req.URL.Path = p[:length-1]
587
	}
588 16
	redirectRequest(c)
589
}
590

591
func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool {
592 16
	req := c.Request
593 16
	rPath := req.URL.Path
594

595 16
	if fixedPath, ok := root.findCaseInsensitivePath(cleanPath(rPath), trailingSlash); ok {
596 16
		req.URL.Path = bytesconv.BytesToString(fixedPath)
597 16
		redirectRequest(c)
598 16
		return true
599
	}
600 16
	return false
601
}
602

603
func redirectRequest(c *Context) {
604 16
	req := c.Request
605 16
	rPath := req.URL.Path
606 16
	rURL := req.URL.String()
607

608 16
	code := http.StatusMovedPermanently // Permanent redirect, request with GET method
609 16
	if req.Method != http.MethodGet {
610 16
		code = http.StatusTemporaryRedirect
611
	}
612 16
	debugPrint("redirecting request %d: %s --> %s", code, rPath, rURL)
613 16
	http.Redirect(c.Writer, req, rURL, code)
614 16
	c.writermem.WriteHeaderNow()
615
}

Read our documentation on viewing source code .

Loading