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
	"net/http"
9
	"path"
10
	"regexp"
11
	"strings"
12
)
13

14
var (
15
	// reg match english letters for http method name
16
	regEnLetter = regexp.MustCompile("^[A-Z]+$")
17
)
18

19
// IRouter defines all router handle interface includes single and group router.
20
type IRouter interface {
21
	IRoutes
22
	Group(string, ...HandlerFunc) *RouterGroup
23
}
24

25
// IRoutes defines all router handle interface.
26
type IRoutes interface {
27
	Use(...HandlerFunc) IRoutes
28

29
	Handle(string, string, ...HandlerFunc) IRoutes
30
	Any(string, ...HandlerFunc) IRoutes
31
	GET(string, ...HandlerFunc) IRoutes
32
	POST(string, ...HandlerFunc) IRoutes
33
	DELETE(string, ...HandlerFunc) IRoutes
34
	PATCH(string, ...HandlerFunc) IRoutes
35
	PUT(string, ...HandlerFunc) IRoutes
36
	OPTIONS(string, ...HandlerFunc) IRoutes
37
	HEAD(string, ...HandlerFunc) IRoutes
38

39
	StaticFile(string, string) IRoutes
40
	Static(string, string) IRoutes
41
	StaticFS(string, http.FileSystem) IRoutes
42
}
43

44
// RouterGroup is used internally to configure router, a RouterGroup is associated with
45
// a prefix and an array of handlers (middleware).
46
type RouterGroup struct {
47
	Handlers HandlersChain
48
	basePath string
49
	engine   *Engine
50
	root     bool
51
}
52

53
var _ IRouter = &RouterGroup{}
54

55
// Use adds middleware to the group, see example code in GitHub.
56
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
57 18
	group.Handlers = append(group.Handlers, middleware...)
58 18
	return group.returnObj()
59
}
60

61
// Group creates a new router group. You should add all the routes that have common middlewares or the same path prefix.
62
// For example, all the routes that use a common middleware for authorization could be grouped.
63
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
64 18
	return &RouterGroup{
65 18
		Handlers: group.combineHandlers(handlers),
66 18
		basePath: group.calculateAbsolutePath(relativePath),
67 18
		engine:   group.engine,
68
	}
69
}
70

71
// BasePath returns the base path of router group.
72
// For example, if v := router.Group("/rest/n/v1/api"), v.BasePath() is "/rest/n/v1/api".
73
func (group *RouterGroup) BasePath() string {
74 18
	return group.basePath
75
}
76

77
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
78 18
	absolutePath := group.calculateAbsolutePath(relativePath)
79 18
	handlers = group.combineHandlers(handlers)
80 18
	group.engine.addRoute(httpMethod, absolutePath, handlers)
81 18
	return group.returnObj()
82
}
83

84
// Handle registers a new request handle and middleware with the given path and method.
85
// The last handler should be the real handler, the other ones should be middleware that can and should be shared among different routes.
86
// See the example code in GitHub.
87
//
88
// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
89
// functions can be used.
90
//
91
// This function is intended for bulk loading and to allow the usage of less
92
// frequently used, non-standardized or custom methods (e.g. for internal
93
// communication with a proxy).
94
func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes {
95 18
	if matched := regEnLetter.MatchString(httpMethod); !matched {
96 18
		panic("http method " + httpMethod + " is not valid")
97
	}
98 18
	return group.handle(httpMethod, relativePath, handlers)
99
}
100

101
// POST is a shortcut for router.Handle("POST", path, handle).
102
func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes {
103 18
	return group.handle(http.MethodPost, relativePath, handlers)
104
}
105

106
// GET is a shortcut for router.Handle("GET", path, handle).
107
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
108 18
	return group.handle(http.MethodGet, relativePath, handlers)
109
}
110

111
// DELETE is a shortcut for router.Handle("DELETE", path, handle).
112
func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes {
113 18
	return group.handle(http.MethodDelete, relativePath, handlers)
114
}
115

116
// PATCH is a shortcut for router.Handle("PATCH", path, handle).
117
func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes {
118 18
	return group.handle(http.MethodPatch, relativePath, handlers)
119
}
120

121
// PUT is a shortcut for router.Handle("PUT", path, handle).
122
func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes {
123 18
	return group.handle(http.MethodPut, relativePath, handlers)
124
}
125

126
// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle).
127
func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes {
128 18
	return group.handle(http.MethodOptions, relativePath, handlers)
129
}
130

131
// HEAD is a shortcut for router.Handle("HEAD", path, handle).
132
func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes {
133 18
	return group.handle(http.MethodHead, relativePath, handlers)
134
}
135

136
// Any registers a route that matches all the HTTP methods.
137
// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE.
138
func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes {
139 18
	group.handle(http.MethodGet, relativePath, handlers)
140 18
	group.handle(http.MethodPost, relativePath, handlers)
141 18
	group.handle(http.MethodPut, relativePath, handlers)
142 18
	group.handle(http.MethodPatch, relativePath, handlers)
143 18
	group.handle(http.MethodHead, relativePath, handlers)
144 18
	group.handle(http.MethodOptions, relativePath, handlers)
145 18
	group.handle(http.MethodDelete, relativePath, handlers)
146 18
	group.handle(http.MethodConnect, relativePath, handlers)
147 18
	group.handle(http.MethodTrace, relativePath, handlers)
148 18
	return group.returnObj()
149
}
150

151
// StaticFile registers a single route in order to serve a single file of the local filesystem.
152
// router.StaticFile("favicon.ico", "./resources/favicon.ico")
153
func (group *RouterGroup) StaticFile(relativePath, filepath string) IRoutes {
154 18
	if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
155 18
		panic("URL parameters can not be used when serving a static file")
156
	}
157 18
	handler := func(c *Context) {
158 18
		c.File(filepath)
159
	}
160 18
	group.GET(relativePath, handler)
161 18
	group.HEAD(relativePath, handler)
162 18
	return group.returnObj()
163
}
164

165
// Static serves files from the given file system root.
166
// Internally a http.FileServer is used, therefore http.NotFound is used instead
167
// of the Router's NotFound handler.
168
// To use the operating system's file system implementation,
169
// use :
170
//     router.Static("/static", "/var/www")
171
func (group *RouterGroup) Static(relativePath, root string) IRoutes {
172 18
	return group.StaticFS(relativePath, Dir(root, false))
173
}
174

175
// StaticFS works just like `Static()` but a custom `http.FileSystem` can be used instead.
176
// Gin by default user: gin.Dir()
177
func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes {
178 18
	if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
179 18
		panic("URL parameters can not be used when serving a static folder")
180
	}
181 18
	handler := group.createStaticHandler(relativePath, fs)
182 18
	urlPattern := path.Join(relativePath, "/*filepath")
183

184
	// Register GET and HEAD handlers
185 18
	group.GET(urlPattern, handler)
186 18
	group.HEAD(urlPattern, handler)
187 18
	return group.returnObj()
188
}
189

190
func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileSystem) HandlerFunc {
191 18
	absolutePath := group.calculateAbsolutePath(relativePath)
192 18
	fileServer := http.StripPrefix(absolutePath, http.FileServer(fs))
193

194 18
	return func(c *Context) {
195 18
		if _, noListing := fs.(*onlyFilesFS); noListing {
196 18
			c.Writer.WriteHeader(http.StatusNotFound)
197
		}
198

199 18
		file := c.Param("filepath")
200
		// Check if file exists and/or if we have permission to access it
201 18
		f, err := fs.Open(file)
202 18
		if err != nil {
203 18
			c.Writer.WriteHeader(http.StatusNotFound)
204 18
			c.handlers = group.engine.noRoute
205
			// Reset index
206 18
			c.index = -1
207 18
			return
208
		}
209 18
		f.Close()
210

211 18
		fileServer.ServeHTTP(c.Writer, c.Request)
212
	}
213
}
214

215
func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
216 18
	finalSize := len(group.Handlers) + len(handlers)
217 18
	if finalSize >= int(abortIndex) {
218 18
		panic("too many handlers")
219
	}
220 18
	mergedHandlers := make(HandlersChain, finalSize)
221 18
	copy(mergedHandlers, group.Handlers)
222 18
	copy(mergedHandlers[len(group.Handlers):], handlers)
223 18
	return mergedHandlers
224
}
225

226
func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
227 18
	return joinPaths(group.basePath, relativePath)
228
}
229

230
func (group *RouterGroup) returnObj() IRoutes {
231 18
	if group.root {
232 18
		return group.engine
233
	}
234 18
	return group
235
}

Read our documentation on viewing source code .

Loading