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

16
	"github.com/gin-gonic/gin/internal/bytesconv"
17
	"github.com/gin-gonic/gin/render"
18
)
19

20
const defaultMultipartMemory = 32 << 20 // 32 MB
21

22
var (
23
	default404Body = []byte("404 page not found")
24
	default405Body = []byte("405 method not allowed")
25
)
26

27
var defaultAppEngine bool
28

29
// HandlerFunc defines the handler used by gin middleware as return value.
30
type HandlerFunc func(*Context)
31

32
// HandlersChain defines a HandlerFunc array.
33
type HandlersChain []HandlerFunc
34

35
// Last returns the last handler in the chain. ie. the last handler is the main one.
36
func (c HandlersChain) Last() HandlerFunc {
37 16
	if length := len(c); length > 0 {
38 16
		return c[length-1]
39
	}
40 16
	return nil
41
}
42

43
// RouteInfo represents a request route's specification which contains method and path and its handler.
44
type RouteInfo struct {
45
	Method      string
46
	Path        string
47
	Handler     string
48
	HandlerFunc HandlerFunc
49
}
50

51
// RoutesInfo defines a RouteInfo array.
52
type RoutesInfo []RouteInfo
53

54
// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
55
// Create an instance of Engine, by using New() or Default()
56
type Engine struct {
57
	RouterGroup
58

59
	// Enables automatic redirection if the current route can't be matched but a
60
	// handler for the path with (without) the trailing slash exists.
61
	// For example if /foo/ is requested but a route only exists for /foo, the
62
	// client is redirected to /foo with http status code 301 for GET requests
63
	// and 307 for all other request methods.
64
	RedirectTrailingSlash bool
65

66
	// If enabled, the router tries to fix the current request path, if no
67
	// handle is registered for it.
68
	// First superfluous path elements like ../ or // are removed.
69
	// Afterwards the router does a case-insensitive lookup of the cleaned path.
70
	// If a handle can be found for this route, the router makes a redirection
71
	// to the corrected path with status code 301 for GET requests and 307 for
72
	// all other request methods.
73
	// For example /FOO and /..//Foo could be redirected to /foo.
74
	// RedirectTrailingSlash is independent of this option.
75
	RedirectFixedPath bool
76

77
	// If enabled, the router checks if another method is allowed for the
78
	// current route, if the current request can not be routed.
79
	// If this is the case, the request is answered with 'Method Not Allowed'
80
	// and HTTP status code 405.
81
	// If no other Method is allowed, the request is delegated to the NotFound
82
	// handler.
83
	HandleMethodNotAllowed bool
84
	ForwardedByClientIP    bool
85

86
	// #726 #755 If enabled, it will thrust some headers starting with
87
	// 'X-AppEngine...' for better integration with that PaaS.
88
	AppEngine bool
89

90
	// If enabled, the url.RawPath will be used to find parameters.
91
	UseRawPath bool
92

93
	// If true, the path value will be unescaped.
94
	// If UseRawPath is false (by default), the UnescapePathValues effectively is true,
95
	// as url.Path gonna be used, which is already unescaped.
96
	UnescapePathValues bool
97

98
	// Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
99
	// method call.
100
	MaxMultipartMemory int64
101

102
	// RemoveExtraSlash a parameter can be parsed from the URL even with extra slashes.
103
	// See the PR #1817 and issue #1644
104
	RemoveExtraSlash bool
105

106
	delims           render.Delims
107
	secureJSONPrefix string
108
	HTMLRender       render.HTMLRender
109
	FuncMap          template.FuncMap
110
	allNoRoute       HandlersChain
111
	allNoMethod      HandlersChain
112
	noRoute          HandlersChain
113
	noMethod         HandlersChain
114
	pool             sync.Pool
115
	trees            methodTrees
116
	maxParams        uint16
117
}
118

119
var _ IRouter = &Engine{}
120

121
// New returns a new blank Engine instance without any middleware attached.
122
// By default the configuration is:
123
// - RedirectTrailingSlash:  true
124
// - RedirectFixedPath:      false
125
// - HandleMethodNotAllowed: false
126
// - ForwardedByClientIP:    true
127
// - UseRawPath:             false
128
// - UnescapePathValues:     true
129
func New() *Engine {
130 16
	debugPrintWARNINGNew()
131 16
	engine := &Engine{
132 16
		RouterGroup: RouterGroup{
133 16
			Handlers: nil,
134 16
			basePath: "/",
135 16
			root:     true,
136 16
		},
137 16
		FuncMap:                template.FuncMap{},
138 16
		RedirectTrailingSlash:  true,
139 16
		RedirectFixedPath:      false,
140 16
		HandleMethodNotAllowed: false,
141 16
		ForwardedByClientIP:    true,
142 16
		AppEngine:              defaultAppEngine,
143 16
		UseRawPath:             false,
144 16
		RemoveExtraSlash:       false,
145 16
		UnescapePathValues:     true,
146 16
		MaxMultipartMemory:     defaultMultipartMemory,
147 16
		trees:                  make(methodTrees, 0, 9),
148 16
		delims:                 render.Delims{Left: "{{", Right: "}}"},
149 16
		secureJSONPrefix:       "while(1);",
150
	}
151 16
	engine.RouterGroup.engine = engine
152 16
	engine.pool.New = func() interface{} {
153 16
		return engine.allocateContext()
154
	}
155 16
	return engine
156
}
157

158
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
159
func Default() *Engine {
160 16
	debugPrintWARNINGDefault()
161 16
	engine := New()
162 16
	engine.Use(Logger(), Recovery())
163 16
	return engine
164
}
165

166
func (engine *Engine) allocateContext() *Context {
167 16
	v := make(Params, 0, engine.maxParams)
168 16
	return &Context{engine: engine, params: &v}
169
}
170

171
// Delims sets template left and right delims and returns a Engine instance.
172
func (engine *Engine) Delims(left, right string) *Engine {
173 16
	engine.delims = render.Delims{Left: left, Right: right}
174 16
	return engine
175
}
176

177
// SecureJsonPrefix sets the secureJSONPrefix used in Context.SecureJSON.
178
func (engine *Engine) SecureJsonPrefix(prefix string) *Engine {
179 16
	engine.secureJSONPrefix = prefix
180 16
	return engine
181
}
182

183
// LoadHTMLGlob loads HTML files identified by glob pattern
184
// and associates the result with HTML renderer.
185
func (engine *Engine) LoadHTMLGlob(pattern string) {
186 16
	left := engine.delims.Left
187 16
	right := engine.delims.Right
188 16
	templ := template.Must(template.New("").Delims(left, right).Funcs(engine.FuncMap).ParseGlob(pattern))
189

190 16
	if IsDebugging() {
191 16
		debugPrintLoadTemplate(templ)
192 16
		engine.HTMLRender = render.HTMLDebug{Glob: pattern, FuncMap: engine.FuncMap, Delims: engine.delims}
193 16
		return
194
	}
195

196 16
	engine.SetHTMLTemplate(templ)
197
}
198

199
// LoadHTMLFiles loads a slice of HTML files
200
// and associates the result with HTML renderer.
201
func (engine *Engine) LoadHTMLFiles(files ...string) {
202 16
	if IsDebugging() {
203 16
		engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims}
204 16
		return
205
	}
206

207 16
	templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFiles(files...))
208 16
	engine.SetHTMLTemplate(templ)
209
}
210

211
// SetHTMLTemplate associate a template with HTML renderer.
212
func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
213 16
	if len(engine.trees) > 0 {
214 16
		debugPrintWARNINGSetHTMLTemplate()
215
	}
216

217 16
	engine.HTMLRender = render.HTMLProduction{Template: templ.Funcs(engine.FuncMap)}
218
}
219

220
// SetFuncMap sets the FuncMap used for template.FuncMap.
221
func (engine *Engine) SetFuncMap(funcMap template.FuncMap) {
222 16
	engine.FuncMap = funcMap
223
}
224

225
// NoRoute adds handlers for NoRoute. It return a 404 code by default.
226
func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
227 16
	engine.noRoute = handlers
228 16
	engine.rebuild404Handlers()
229
}
230

231
// NoMethod sets the handlers called when... TODO.
232
func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
233 16
	engine.noMethod = handlers
234 16
	engine.rebuild405Handlers()
235
}
236

237
// Use attaches a global middleware to the router. ie. the middleware attached though Use() will be
238
// included in the handlers chain for every single request. Even 404, 405, static files...
239
// For example, this is the right place for a logger or error management middleware.
240
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
241 16
	engine.RouterGroup.Use(middleware...)
242 16
	engine.rebuild404Handlers()
243 16
	engine.rebuild405Handlers()
244 16
	return engine
245
}
246

247
func (engine *Engine) rebuild404Handlers() {
248 16
	engine.allNoRoute = engine.combineHandlers(engine.noRoute)
249
}
250

251
func (engine *Engine) rebuild405Handlers() {
252 16
	engine.allNoMethod = engine.combineHandlers(engine.noMethod)
253
}
254

255
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
256 16
	assert1(path[0] == '/', "path must begin with '/'")
257 16
	assert1(method != "", "HTTP method can not be empty")
258 16
	assert1(len(handlers) > 0, "there must be at least one handler")
259

260 16
	debugPrintRoute(method, path, handlers)
261

262 16
	root := engine.trees.get(method)
263 16
	if root == nil {
264 16
		root = new(node)
265 16
		root.fullPath = "/"
266 16
		engine.trees = append(engine.trees, methodTree{method: method, root: root})
267
	}
268 16
	root.addRoute(path, handlers)
269

270
	// Update maxParams
271 16
	if paramsCount := countParams(path); paramsCount > engine.maxParams {
272 16
		engine.maxParams = paramsCount
273
	}
274
}
275

276
// Routes returns a slice of registered routes, including some useful information, such as:
277
// the http method, path and the handler name.
278
func (engine *Engine) Routes() (routes RoutesInfo) {
279
	for _, tree := range engine.trees {
280 16
		routes = iterate("", tree.method, routes, tree.root)
281
	}
282 16
	return routes
283
}
284

285
func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
286 16
	path += root.path
287 16
	if len(root.handlers) > 0 {
288 16
		handlerFunc := root.handlers.Last()
289 16
		routes = append(routes, RouteInfo{
290 16
			Method:      method,
291 16
			Path:        path,
292 16
			Handler:     nameOfFunction(handlerFunc),
293 16
			HandlerFunc: handlerFunc,
294 16
		})
295
	}
296
	for _, child := range root.children {
297 16
		routes = iterate(path, method, routes, child)
298
	}
299 16
	return routes
300
}
301

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

308 16
	address := resolveAddress(addr)
309 16
	debugPrint("Listening and serving HTTP on %s\n", address)
310 16
	err = http.ListenAndServe(address, engine)
311 16
	return
312
}
313

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

321 16
	err = http.ListenAndServeTLS(addr, certFile, keyFile, engine)
322 16
	return
323
}
324

325
// RunUnix attaches the router to a http.Server and starts listening and serving HTTP requests
326
// through the specified unix socket (ie. a file).
327
// Note: this method will block the calling goroutine indefinitely unless an error happens.
328
func (engine *Engine) RunUnix(file string) (err error) {
329 16
	debugPrint("Listening and serving HTTP on unix:/%s", file)
330 16
	defer func() { debugPrintError(err) }()
331

332 16
	listener, err := net.Listen("unix", file)
333 16
	if err != nil {
334 16
		return
335
	}
336 16
	defer listener.Close()
337 16
	defer os.Remove(file)
338

339 16
	err = http.Serve(listener, engine)
340 16
	return
341
}
342

343
// RunFd attaches the router to a http.Server and starts listening and serving HTTP requests
344
// through the specified file descriptor.
345
// Note: this method will block the calling goroutine indefinitely unless an error happens.
346
func (engine *Engine) RunFd(fd int) (err error) {
347 16
	debugPrint("Listening and serving HTTP on fd@%d", fd)
348 16
	defer func() { debugPrintError(err) }()
349

350 16
	f := os.NewFile(uintptr(fd), fmt.Sprintf("fd@%d", fd))
351 16
	listener, err := net.FileListener(f)
352 16
	if err != nil {
353 16
		return
354
	}
355 16
	defer listener.Close()
356 16
	err = engine.RunListener(listener)
357 16
	return
358
}
359

360
// RunListener attaches the router to a http.Server and starts listening and serving HTTP requests
361
// through the specified net.Listener
362
func (engine *Engine) RunListener(listener net.Listener) (err error) {
363 16
	debugPrint("Listening and serving HTTP on listener what's bind with address@%s", listener.Addr())
364 16
	defer func() { debugPrintError(err) }()
365 16
	err = http.Serve(listener, engine)
366 16
	return
367
}
368

369
// ServeHTTP conforms to the http.Handler interface.
370
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
371 16
	c := engine.pool.Get().(*Context)
372 16
	c.writermem.reset(w)
373 16
	c.Request = req
374 16
	c.reset()
375

376 16
	engine.handleHTTPRequest(c)
377

378 16
	engine.pool.Put(c)
379
}
380

381
// HandleContext re-enter a context that has been rewritten.
382
// This can be done by setting c.Request.URL.Path to your new target.
383
// Disclaimer: You can loop yourself to death with this, use wisely.
384
func (engine *Engine) HandleContext(c *Context) {
385 16
	oldIndexValue := c.index
386 16
	c.reset()
387 16
	engine.handleHTTPRequest(c)
388

389 16
	c.index = oldIndexValue
390
}
391

392
func (engine *Engine) handleHTTPRequest(c *Context) {
393 16
	httpMethod := c.Request.Method
394 16
	rPath := c.Request.URL.Path
395 16
	unescape := false
396 16
	if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
397 16
		rPath = c.Request.URL.RawPath
398 16
		unescape = engine.UnescapePathValues
399
	}
400

401 16
	if engine.RemoveExtraSlash {
402 16
		rPath = cleanPath(rPath)
403
	}
404

405
	// Find root of the tree for the given HTTP method
406 16
	t := engine.trees
407
	for i, tl := 0, len(t); i < tl; i++ {
408 16
		if t[i].method != httpMethod {
409 16
			continue
410
		}
411 16
		root := t[i].root
412
		// Find route in tree
413 16
		value := root.getValue(rPath, c.params, unescape)
414 16
		if value.params != nil {
415 16
			c.Params = *value.params
416
		}
417 16
		if value.handlers != nil {
418 16
			c.handlers = value.handlers
419 16
			c.fullPath = value.fullPath
420 16
			c.Next()
421 16
			c.writermem.WriteHeaderNow()
422 16
			return
423
		}
424 16
		if httpMethod != "CONNECT" && rPath != "/" {
425 16
			if value.tsr && engine.RedirectTrailingSlash {
426 16
				redirectTrailingSlash(c)
427 16
				return
428
			}
429 16
			if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
430 16
				return
431
			}
432
		}
433 16
		break
434
	}
435

436 16
	if engine.HandleMethodNotAllowed {
437
		for _, tree := range engine.trees {
438 16
			if tree.method == httpMethod {
439 16
				continue
440
			}
441 16
			if value := tree.root.getValue(rPath, nil, unescape); value.handlers != nil {
442 16
				c.handlers = engine.allNoMethod
443 16
				serveError(c, http.StatusMethodNotAllowed, default405Body)
444 16
				return
445
			}
446
		}
447
	}
448 16
	c.handlers = engine.allNoRoute
449 16
	serveError(c, http.StatusNotFound, default404Body)
450
}
451

452
var mimePlain = []string{MIMEPlain}
453

454
func serveError(c *Context, code int, defaultMessage []byte) {
455 16
	c.writermem.status = code
456 16
	c.Next()
457 16
	if c.writermem.Written() {
458 16
		return
459
	}
460 16
	if c.writermem.Status() == code {
461 16
		c.writermem.Header()["Content-Type"] = mimePlain
462 16
		_, err := c.Writer.Write(defaultMessage)
463 16
		if err != nil {
464 0
			debugPrint("cannot write message to writer during serve error: %v", err)
465
		}
466 16
		return
467
	}
468 16
	c.writermem.WriteHeaderNow()
469
}
470

471
func redirectTrailingSlash(c *Context) {
472 16
	req := c.Request
473 16
	p := req.URL.Path
474 16
	if prefix := path.Clean(c.Request.Header.Get("X-Forwarded-Prefix")); prefix != "." {
475 16
		p = prefix + "/" + req.URL.Path
476
	}
477 16
	req.URL.Path = p + "/"
478 16
	if length := len(p); length > 1 && p[length-1] == '/' {
479 16
		req.URL.Path = p[:length-1]
480
	}
481 16
	redirectRequest(c)
482
}
483

484
func redirectFixedPath(c *Context, root *node, trailingSlash bool) bool {
485 16
	req := c.Request
486 16
	rPath := req.URL.Path
487

488 16
	if fixedPath, ok := root.findCaseInsensitivePath(cleanPath(rPath), trailingSlash); ok {
489 16
		req.URL.Path = bytesconv.BytesToString(fixedPath)
490 16
		redirectRequest(c)
491 16
		return true
492
	}
493 16
	return false
494
}
495

496
func redirectRequest(c *Context) {
497 16
	req := c.Request
498 16
	rPath := req.URL.Path
499 16
	rURL := req.URL.String()
500

501 16
	code := http.StatusMovedPermanently // Permanent redirect, request with GET method
502 16
	if req.Method != http.MethodGet {
503 16
		code = http.StatusTemporaryRedirect
504
	}
505 16
	debugPrint("redirecting request %d: %s --> %s", code, rPath, rURL)
506 16
	http.Redirect(c.Writer, req, rURL, code)
507 16
	c.writermem.WriteHeaderNow()
508
}

Read our documentation on viewing source code .

Loading