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 14
	if length := len(c); length > 0 {
38 14
		return c[length-1]
39
	}
40 14
	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 14
	debugPrintWARNINGNew()
131 14
	engine := &Engine{
132 14
		RouterGroup: RouterGroup{
133 14
			Handlers: nil,
134 14
			basePath: "/",
135 14
			root:     true,
136 14
		},
137 14
		FuncMap:                template.FuncMap{},
138 14
		RedirectTrailingSlash:  true,
139 14
		RedirectFixedPath:      false,
140 14
		HandleMethodNotAllowed: false,
141 14
		ForwardedByClientIP:    true,
142 14
		AppEngine:              defaultAppEngine,
143 14
		UseRawPath:             false,
144 14
		RemoveExtraSlash:       false,
145 14
		UnescapePathValues:     true,
146 14
		MaxMultipartMemory:     defaultMultipartMemory,
147 14
		trees:                  make(methodTrees, 0, 9),
148 14
		delims:                 render.Delims{Left: "{{", Right: "}}"},
149 14
		secureJSONPrefix:       "while(1);",
150
	}
151 14
	engine.RouterGroup.engine = engine
152 14
	engine.pool.New = func() interface{} {
153 14
		return engine.allocateContext()
154
	}
155 14
	return engine
156
}
157

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

166
func (engine *Engine) allocateContext() *Context {
167 14
	v := make(Params, 0, engine.maxParams)
168 14
	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 14
	engine.delims = render.Delims{Left: left, Right: right}
174 14
	return engine
175
}
176

177
// SecureJsonPrefix sets the secureJSONPrefix used in Context.SecureJSON.
178
func (engine *Engine) SecureJsonPrefix(prefix string) *Engine {
179 14
	engine.secureJSONPrefix = prefix
180 14
	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 14
	left := engine.delims.Left
187 14
	right := engine.delims.Right
188 14
	templ := template.Must(template.New("").Delims(left, right).Funcs(engine.FuncMap).ParseGlob(pattern))
189

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

196 14
	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 14
	if IsDebugging() {
203 14
		engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims}
204 14
		return
205
	}
206

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

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

217 14
	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 14
	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 14
	engine.noRoute = handlers
228 14
	engine.rebuild404Handlers()
229
}
230

231
// NoMethod sets the handlers called when... TODO.
232
func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
233 14
	engine.noMethod = handlers
234 14
	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 14
	engine.RouterGroup.Use(middleware...)
242 14
	engine.rebuild404Handlers()
243 14
	engine.rebuild405Handlers()
244 14
	return engine
245
}
246

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

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

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

260 14
	debugPrintRoute(method, path, handlers)
261

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

270
	// Update maxParams
271 14
	if paramsCount := countParams(path); paramsCount > engine.maxParams {
272 14
		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 14
		routes = iterate("", tree.method, routes, tree.root)
281
	}
282 14
	return routes
283
}
284

285
func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
286 14
	path += root.path
287 14
	if len(root.handlers) > 0 {
288 14
		handlerFunc := root.handlers.Last()
289 14
		routes = append(routes, RouteInfo{
290 14
			Method:      method,
291 14
			Path:        path,
292 14
			Handler:     nameOfFunction(handlerFunc),
293 14
			HandlerFunc: handlerFunc,
294 14
		})
295
	}
296
	for _, child := range root.children {
297 14
		routes = iterate(path, method, routes, child)
298
	}
299 14
	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 14
	defer func() { debugPrintError(err) }()
307

308 14
	address := resolveAddress(addr)
309 14
	debugPrint("Listening and serving HTTP on %s\n", address)
310 14
	err = http.ListenAndServe(address, engine)
311 14
	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 14
	debugPrint("Listening and serving HTTPS on %s\n", addr)
319 14
	defer func() { debugPrintError(err) }()
320

321 14
	err = http.ListenAndServeTLS(addr, certFile, keyFile, engine)
322 14
	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 14
	debugPrint("Listening and serving HTTP on unix:/%s", file)
330 14
	defer func() { debugPrintError(err) }()
331

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

339 14
	err = http.Serve(listener, engine)
340 14
	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 14
	debugPrint("Listening and serving HTTP on fd@%d", fd)
348 14
	defer func() { debugPrintError(err) }()
349

350 14
	f := os.NewFile(uintptr(fd), fmt.Sprintf("fd@%d", fd))
351 14
	listener, err := net.FileListener(f)
352 14
	if err != nil {
353 14
		return
354
	}
355 14
	defer listener.Close()
356 14
	err = engine.RunListener(listener)
357 14
	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 14
	debugPrint("Listening and serving HTTP on listener what's bind with address@%s", listener.Addr())
364 14
	defer func() { debugPrintError(err) }()
365 14
	err = http.Serve(listener, engine)
366 14
	return
367
}
368

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

376 14
	engine.handleHTTPRequest(c)
377

378 14
	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 14
	oldIndexValue := c.index
386 14
	c.reset()
387 14
	engine.handleHTTPRequest(c)
388

389 14
	c.index = oldIndexValue
390
}
391

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

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

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

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

452
var mimePlain = []string{MIMEPlain}
453

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

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

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

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

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

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

Read our documentation on viewing source code .

Loading