gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

routergroup.go (9280B)


      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 	// regEnLetter matches english letters for http method name
     16 	regEnLetter = regexp.MustCompile("^[A-Z]+$")
     17 
     18 	// anyMethods for RouterGroup Any method
     19 	anyMethods = []string{
     20 		http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch,
     21 		http.MethodHead, http.MethodOptions, http.MethodDelete, http.MethodConnect,
     22 		http.MethodTrace,
     23 	}
     24 )
     25 
     26 // IRouter defines all router handle interface includes single and group router.
     27 type IRouter interface {
     28 	IRoutes
     29 	Group(string, ...HandlerFunc) *RouterGroup
     30 }
     31 
     32 // IRoutes defines all router handle interface.
     33 type IRoutes interface {
     34 	Use(...HandlerFunc) IRoutes
     35 
     36 	Handle(string, string, ...HandlerFunc) IRoutes
     37 	Any(string, ...HandlerFunc) IRoutes
     38 	GET(string, ...HandlerFunc) IRoutes
     39 	POST(string, ...HandlerFunc) IRoutes
     40 	DELETE(string, ...HandlerFunc) IRoutes
     41 	PATCH(string, ...HandlerFunc) IRoutes
     42 	PUT(string, ...HandlerFunc) IRoutes
     43 	OPTIONS(string, ...HandlerFunc) IRoutes
     44 	HEAD(string, ...HandlerFunc) IRoutes
     45 	Match([]string, string, ...HandlerFunc) IRoutes
     46 
     47 	StaticFile(string, string) IRoutes
     48 	StaticFileFS(string, string, http.FileSystem) IRoutes
     49 	Static(string, string) IRoutes
     50 	StaticFS(string, http.FileSystem) IRoutes
     51 }
     52 
     53 // RouterGroup is used internally to configure router, a RouterGroup is associated with
     54 // a prefix and an array of handlers (middleware).
     55 type RouterGroup struct {
     56 	Handlers HandlersChain
     57 	basePath string
     58 	engine   *Engine
     59 	root     bool
     60 }
     61 
     62 var _ IRouter = (*RouterGroup)(nil)
     63 
     64 // Use adds middleware to the group, see example code in GitHub.
     65 func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
     66 	group.Handlers = append(group.Handlers, middleware...)
     67 	return group.returnObj()
     68 }
     69 
     70 // Group creates a new router group. You should add all the routes that have common middlewares or the same path prefix.
     71 // For example, all the routes that use a common middleware for authorization could be grouped.
     72 func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
     73 	return &RouterGroup{
     74 		Handlers: group.combineHandlers(handlers),
     75 		basePath: group.calculateAbsolutePath(relativePath),
     76 		engine:   group.engine,
     77 	}
     78 }
     79 
     80 // BasePath returns the base path of router group.
     81 // For example, if v := router.Group("/rest/n/v1/api"), v.BasePath() is "/rest/n/v1/api".
     82 func (group *RouterGroup) BasePath() string {
     83 	return group.basePath
     84 }
     85 
     86 func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
     87 	absolutePath := group.calculateAbsolutePath(relativePath)
     88 	handlers = group.combineHandlers(handlers)
     89 	group.engine.addRoute(httpMethod, absolutePath, handlers)
     90 	return group.returnObj()
     91 }
     92 
     93 // Handle registers a new request handle and middleware with the given path and method.
     94 // The last handler should be the real handler, the other ones should be middleware that can and should be shared among different routes.
     95 // See the example code in GitHub.
     96 //
     97 // For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
     98 // functions can be used.
     99 //
    100 // This function is intended for bulk loading and to allow the usage of less
    101 // frequently used, non-standardized or custom methods (e.g. for internal
    102 // communication with a proxy).
    103 func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes {
    104 	if matched := regEnLetter.MatchString(httpMethod); !matched {
    105 		panic("http method " + httpMethod + " is not valid")
    106 	}
    107 	return group.handle(httpMethod, relativePath, handlers)
    108 }
    109 
    110 // POST is a shortcut for router.Handle("POST", path, handlers).
    111 func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes {
    112 	return group.handle(http.MethodPost, relativePath, handlers)
    113 }
    114 
    115 // GET is a shortcut for router.Handle("GET", path, handlers).
    116 func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
    117 	return group.handle(http.MethodGet, relativePath, handlers)
    118 }
    119 
    120 // DELETE is a shortcut for router.Handle("DELETE", path, handlers).
    121 func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes {
    122 	return group.handle(http.MethodDelete, relativePath, handlers)
    123 }
    124 
    125 // PATCH is a shortcut for router.Handle("PATCH", path, handlers).
    126 func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes {
    127 	return group.handle(http.MethodPatch, relativePath, handlers)
    128 }
    129 
    130 // PUT is a shortcut for router.Handle("PUT", path, handlers).
    131 func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes {
    132 	return group.handle(http.MethodPut, relativePath, handlers)
    133 }
    134 
    135 // OPTIONS is a shortcut for router.Handle("OPTIONS", path, handlers).
    136 func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes {
    137 	return group.handle(http.MethodOptions, relativePath, handlers)
    138 }
    139 
    140 // HEAD is a shortcut for router.Handle("HEAD", path, handlers).
    141 func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes {
    142 	return group.handle(http.MethodHead, relativePath, handlers)
    143 }
    144 
    145 // Any registers a route that matches all the HTTP methods.
    146 // GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE.
    147 func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes {
    148 	for _, method := range anyMethods {
    149 		group.handle(method, relativePath, handlers)
    150 	}
    151 
    152 	return group.returnObj()
    153 }
    154 
    155 // Match registers a route that matches the specified methods that you declared.
    156 func (group *RouterGroup) Match(methods []string, relativePath string, handlers ...HandlerFunc) IRoutes {
    157 	for _, method := range methods {
    158 		group.handle(method, relativePath, handlers)
    159 	}
    160 
    161 	return group.returnObj()
    162 }
    163 
    164 // StaticFile registers a single route in order to serve a single file of the local filesystem.
    165 // router.StaticFile("favicon.ico", "./resources/favicon.ico")
    166 func (group *RouterGroup) StaticFile(relativePath, filepath string) IRoutes {
    167 	return group.staticFileHandler(relativePath, func(c *Context) {
    168 		c.File(filepath)
    169 	})
    170 }
    171 
    172 // StaticFileFS works just like `StaticFile` but a custom `http.FileSystem` can be used instead..
    173 // router.StaticFileFS("favicon.ico", "./resources/favicon.ico", Dir{".", false})
    174 // Gin by default uses: gin.Dir()
    175 func (group *RouterGroup) StaticFileFS(relativePath, filepath string, fs http.FileSystem) IRoutes {
    176 	return group.staticFileHandler(relativePath, func(c *Context) {
    177 		c.FileFromFS(filepath, fs)
    178 	})
    179 }
    180 
    181 func (group *RouterGroup) staticFileHandler(relativePath string, handler HandlerFunc) IRoutes {
    182 	if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
    183 		panic("URL parameters can not be used when serving a static file")
    184 	}
    185 	group.GET(relativePath, handler)
    186 	group.HEAD(relativePath, handler)
    187 	return group.returnObj()
    188 }
    189 
    190 // Static serves files from the given file system root.
    191 // Internally a http.FileServer is used, therefore http.NotFound is used instead
    192 // of the Router's NotFound handler.
    193 // To use the operating system's file system implementation,
    194 // use :
    195 //
    196 //	router.Static("/static", "/var/www")
    197 func (group *RouterGroup) Static(relativePath, root string) IRoutes {
    198 	return group.StaticFS(relativePath, Dir(root, false))
    199 }
    200 
    201 // StaticFS works just like `Static()` but a custom `http.FileSystem` can be used instead.
    202 // Gin by default uses: gin.Dir()
    203 func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes {
    204 	if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
    205 		panic("URL parameters can not be used when serving a static folder")
    206 	}
    207 	handler := group.createStaticHandler(relativePath, fs)
    208 	urlPattern := path.Join(relativePath, "/*filepath")
    209 
    210 	// Register GET and HEAD handlers
    211 	group.GET(urlPattern, handler)
    212 	group.HEAD(urlPattern, handler)
    213 	return group.returnObj()
    214 }
    215 
    216 func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileSystem) HandlerFunc {
    217 	absolutePath := group.calculateAbsolutePath(relativePath)
    218 	fileServer := http.StripPrefix(absolutePath, http.FileServer(fs))
    219 
    220 	return func(c *Context) {
    221 		if _, noListing := fs.(*onlyFilesFS); noListing {
    222 			c.Writer.WriteHeader(http.StatusNotFound)
    223 		}
    224 
    225 		file := c.Param("filepath")
    226 		// Check if file exists and/or if we have permission to access it
    227 		f, err := fs.Open(file)
    228 		if err != nil {
    229 			c.Writer.WriteHeader(http.StatusNotFound)
    230 			c.handlers = group.engine.noRoute
    231 			// Reset index
    232 			c.index = -1
    233 			return
    234 		}
    235 		f.Close()
    236 
    237 		fileServer.ServeHTTP(c.Writer, c.Request)
    238 	}
    239 }
    240 
    241 func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
    242 	finalSize := len(group.Handlers) + len(handlers)
    243 	assert1(finalSize < int(abortIndex), "too many handlers")
    244 	mergedHandlers := make(HandlersChain, finalSize)
    245 	copy(mergedHandlers, group.Handlers)
    246 	copy(mergedHandlers[len(group.Handlers):], handlers)
    247 	return mergedHandlers
    248 }
    249 
    250 func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
    251 	return joinPaths(group.basePath, relativePath)
    252 }
    253 
    254 func (group *RouterGroup) returnObj() IRoutes {
    255 	if group.root {
    256 		return group.engine
    257 	}
    258 	return group
    259 }