go.sdls.io/sin@v0.0.9/pkg/sin/gin.go (about)

     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 sin
     6  
     7  import (
     8  	"net"
     9  	"net/http"
    10  	"sync"
    11  )
    12  
    13  const defaultMultipartMemory = 32 << 20 // 32 MB
    14  
    15  var (
    16  	default404Body = []byte("404 page not found")
    17  	default405Body = []byte("405 method not allowed")
    18  )
    19  
    20  // HandlerFunc defines the handler used by gin middleware as return value.
    21  type HandlerFunc func(*Context)
    22  
    23  // HandlersChain defines a HandlerFunc array.
    24  type HandlersChain []HandlerFunc
    25  
    26  // Last returns the last handler in the chain. ie. the last handler is the main one.
    27  func (c HandlersChain) Last() HandlerFunc {
    28  	if length := len(c); length > 0 {
    29  		return c[length-1]
    30  	}
    31  	return nil
    32  }
    33  
    34  // RouteInfo represents a request route's specification which contains method and path and its handler.
    35  type RouteInfo struct {
    36  	HandlerFunc HandlerFunc
    37  	Method      string
    38  	Path        string
    39  	Handler     string
    40  }
    41  
    42  // RoutesInfo defines a RouteInfo array.
    43  type RoutesInfo []RouteInfo
    44  
    45  // Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
    46  // Create an instance of Engine, by using New() or Default()
    47  type Engine struct {
    48  	pool             sync.Pool
    49  	secureJSONPrefix string
    50  	allNoRoute       HandlersChain
    51  	trees            methodTrees
    52  	noMethod         HandlersChain
    53  	noRoute          HandlersChain
    54  	allNoMethod      HandlersChain
    55  	RouterGroup
    56  	MaxMultipartMemory     int64
    57  	maxParams              uint16
    58  	UnescapePathValues     bool
    59  	UseRawPath             bool
    60  	HandleMethodNotAllowed bool
    61  }
    62  
    63  var _ IRouter = &Engine{}
    64  
    65  // New returns a new blank Engine instance without any middleware attached.
    66  // By default the configuration is:
    67  // - RedirectTrailingSlash:  true
    68  // - RedirectFixedPath:      false
    69  // - HandleMethodNotAllowed: false
    70  // - ForwardedByClientIP:    true
    71  // - UseRawPath:             false
    72  // - UnescapePathValues:     true
    73  func New() *Engine {
    74  	engine := &Engine{
    75  		RouterGroup: RouterGroup{
    76  			Handlers: nil,
    77  			basePath: "/",
    78  			root:     true,
    79  		},
    80  		HandleMethodNotAllowed: false,
    81  		UseRawPath:             false,
    82  		UnescapePathValues:     true,
    83  		MaxMultipartMemory:     defaultMultipartMemory,
    84  		trees:                  make(methodTrees, 0, 9),
    85  		secureJSONPrefix:       "while(1);",
    86  	}
    87  	engine.RouterGroup.engine = engine
    88  	engine.pool.New = func() interface{} {
    89  		return engine.allocateContext()
    90  	}
    91  	return engine
    92  }
    93  
    94  func (engine *Engine) allocateContext() *Context {
    95  	v := make(Params, 0, engine.maxParams)
    96  	return &Context{engine: engine, params: &v}
    97  }
    98  
    99  // NoRoute adds handlers for NoRoute. It return a 404 code by default.
   100  func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
   101  	engine.noRoute = handlers
   102  	engine.rebuild404Handlers()
   103  }
   104  
   105  // NoMethod sets the handlers called when... TODO.
   106  func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
   107  	engine.noMethod = handlers
   108  	engine.rebuild405Handlers()
   109  }
   110  
   111  // Use attaches a global middleware to the router. ie. the middleware attached though Use() will be
   112  // included in the handlers chain for every single request. Even 404, 405, static files...
   113  // For example, this is the right place for a logger or error management middleware.
   114  func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
   115  	engine.RouterGroup.Use(middleware...)
   116  	engine.rebuild404Handlers()
   117  	engine.rebuild405Handlers()
   118  	return engine
   119  }
   120  
   121  func (engine *Engine) rebuild404Handlers() {
   122  	engine.allNoRoute = engine.combineHandlers(engine.noRoute)
   123  }
   124  
   125  func (engine *Engine) rebuild405Handlers() {
   126  	engine.allNoMethod = engine.combineHandlers(engine.noMethod)
   127  }
   128  
   129  func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
   130  	assert1(path[0] == '/', "path must begin with '/'")
   131  	assert1(method != "", "HTTP method can not be empty")
   132  	assert1(len(handlers) > 0, "there must be at least one handler")
   133  
   134  	root := engine.trees.get(method)
   135  	if root == nil {
   136  		root = new(node)
   137  		root.fullPath = "/"
   138  		engine.trees = append(engine.trees, methodTree{method: method, root: root})
   139  	}
   140  	root.addRoute(path, handlers)
   141  
   142  	// Update maxParams
   143  	if paramsCount := countParams(path); paramsCount > engine.maxParams {
   144  		engine.maxParams = paramsCount
   145  	}
   146  }
   147  
   148  // Routes returns a slice of registered routes, including some useful information, such as:
   149  // the http method, path and the handler name.
   150  func (engine *Engine) Routes() (routes RoutesInfo) {
   151  	for _, tree := range engine.trees {
   152  		routes = iterate("", tree.method, routes, tree.root)
   153  	}
   154  	return routes
   155  }
   156  
   157  func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
   158  	path += root.path
   159  	if len(root.handlers) > 0 {
   160  		handlerFunc := root.handlers.Last()
   161  		routes = append(routes, RouteInfo{
   162  			Method:      method,
   163  			Path:        path,
   164  			Handler:     nameOfFunction(handlerFunc),
   165  			HandlerFunc: handlerFunc,
   166  		})
   167  	}
   168  	for _, child := range root.children {
   169  		routes = iterate(path, method, routes, child)
   170  	}
   171  	return routes
   172  }
   173  
   174  // Run attaches the router to a http.Server and starts listening and serving HTTP requests.
   175  // It is a shortcut for http.ListenAndServe(addr, router)
   176  // Note: this method will block the calling goroutine indefinitely unless an error happens.
   177  func (engine *Engine) Run(address string) error {
   178  	return http.ListenAndServe(address, engine)
   179  }
   180  
   181  // RunListener attaches the router to a http.Server and starts listening and serving HTTP requests
   182  // through the specified net.Listener
   183  func (engine *Engine) RunListener(listener net.Listener) error {
   184  	return http.Serve(listener, engine)
   185  }
   186  
   187  // ServeHTTP conforms to the http.Handler interface.
   188  func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
   189  	c := engine.pool.Get().(*Context)
   190  	c.writermem.reset(w)
   191  	c.Request = req
   192  	c.reset()
   193  
   194  	engine.handleHTTPRequest(c)
   195  
   196  	engine.pool.Put(c)
   197  }
   198  
   199  // HandleContext re-enter a context that has been rewritten.
   200  // This can be done by setting c.Request.URL.Path to your new target.
   201  // Disclaimer: You can loop yourself to death with this, use wisely.
   202  func (engine *Engine) HandleContext(c *Context) {
   203  	oldIndexValue := c.index
   204  	c.reset()
   205  	engine.handleHTTPRequest(c)
   206  
   207  	c.index = oldIndexValue
   208  }
   209  
   210  func (engine *Engine) handleHTTPRequest(c *Context) {
   211  	httpMethod := c.Request.Method
   212  	rPath := c.Request.URL.Path
   213  	unescape := false
   214  	if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
   215  		rPath = c.Request.URL.RawPath
   216  		unescape = engine.UnescapePathValues
   217  	}
   218  
   219  	// Find root of the tree for the given HTTP method
   220  	t := engine.trees
   221  	for i, tl := 0, len(t); i < tl; i++ {
   222  		if t[i].method != httpMethod {
   223  			continue
   224  		}
   225  		root := t[i].root
   226  		// Find route in tree
   227  		value := root.getValue(rPath, c.params, unescape)
   228  		if value.params != nil {
   229  			c.Params = *value.params
   230  		}
   231  		if value.handlers != nil {
   232  			c.handlers = value.handlers
   233  			c.fullPath = value.fullPath
   234  			c.Next()
   235  			c.writermem.WriteHeaderNow()
   236  			return
   237  		}
   238  		break
   239  	}
   240  
   241  	if engine.HandleMethodNotAllowed {
   242  		for _, tree := range engine.trees {
   243  			if tree.method == httpMethod {
   244  				continue
   245  			}
   246  			if value := tree.root.getValue(rPath, nil, unescape); value.handlers != nil {
   247  				c.handlers = engine.allNoMethod
   248  				serveError(c, http.StatusMethodNotAllowed, default405Body)
   249  				return
   250  			}
   251  		}
   252  	}
   253  	c.handlers = engine.allNoRoute
   254  	serveError(c, http.StatusNotFound, default404Body)
   255  }
   256  
   257  var mimePlain = []string{"text/plain"}
   258  
   259  func serveError(c *Context, code int, defaultMessage []byte) {
   260  	c.writermem.status = code
   261  	c.Next()
   262  	if c.writermem.Written() {
   263  		return
   264  	}
   265  	if c.writermem.Status() == code {
   266  		c.writermem.Header()["Content-Type"] = mimePlain
   267  		_, _ = c.Writer.Write(defaultMessage)
   268  		return
   269  	}
   270  	c.writermem.WriteHeaderNow()
   271  }