Cleaning up performance branch
This commit is contained in:
		| @ -6,7 +6,6 @@ package binding | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" |  | ||||||
| 	"log" | 	"log" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| @ -27,8 +26,6 @@ func mapForm(ptr interface{}, form map[string][]string) error { | |||||||
| 			inputFieldName = typeField.Name | 			inputFieldName = typeField.Name | ||||||
| 		} | 		} | ||||||
| 		inputValue, exists := form[inputFieldName] | 		inputValue, exists := form[inputFieldName] | ||||||
| 		fmt.Println("Field: "+inputFieldName+" Value: ", inputValue) |  | ||||||
|  |  | ||||||
| 		if !exists { | 		if !exists { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								context.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								context.go
									
									
									
									
									
								
							| @ -20,7 +20,6 @@ const AbortIndex = math.MaxInt8 / 2 | |||||||
| // Context is the most important part of gin. It allows us to pass variables between middleware, | // Context is the most important part of gin. It allows us to pass variables between middleware, | ||||||
| // manage the flow, validate the JSON of a request and render a JSON response for example. | // manage the flow, validate the JSON of a request and render a JSON response for example. | ||||||
| type Context struct { | type Context struct { | ||||||
| 	Engine    *Engine |  | ||||||
| 	writermem responseWriter | 	writermem responseWriter | ||||||
| 	Request   *http.Request | 	Request   *http.Request | ||||||
| 	Writer    ResponseWriter | 	Writer    ResponseWriter | ||||||
| @ -30,6 +29,7 @@ type Context struct { | |||||||
| 	handlers []HandlerFunc | 	handlers []HandlerFunc | ||||||
| 	index    int8 | 	index    int8 | ||||||
|  |  | ||||||
|  | 	Engine   *Engine | ||||||
| 	Keys     map[string]interface{} | 	Keys     map[string]interface{} | ||||||
| 	Errors   errorMsgs | 	Errors   errorMsgs | ||||||
| 	accepted []string | 	accepted []string | ||||||
| @ -40,10 +40,13 @@ type Context struct { | |||||||
| /************************************/ | /************************************/ | ||||||
|  |  | ||||||
| func (c *Context) reset() { | func (c *Context) reset() { | ||||||
| 	c.Keys = nil | 	c.Writer = &c.writermem | ||||||
|  | 	c.Params = c.Params[0:0] | ||||||
|  | 	c.handlers = nil | ||||||
| 	c.index = -1 | 	c.index = -1 | ||||||
| 	c.accepted = nil | 	c.Keys = nil | ||||||
| 	c.Errors = c.Errors[0:0] | 	c.Errors = c.Errors[0:0] | ||||||
|  | 	c.accepted = nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *Context) Copy() *Context { | func (c *Context) Copy() *Context { | ||||||
| @ -114,9 +117,8 @@ func (c *Context) LastError() error { | |||||||
| 	nuErrors := len(c.Errors) | 	nuErrors := len(c.Errors) | ||||||
| 	if nuErrors > 0 { | 	if nuErrors > 0 { | ||||||
| 		return errors.New(c.Errors[nuErrors-1].Err) | 		return errors.New(c.Errors[nuErrors-1].Err) | ||||||
| 	} else { |  | ||||||
| 		return nil |  | ||||||
| 	} | 	} | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| /************************************/ | /************************************/ | ||||||
|  | |||||||
							
								
								
									
										177
									
								
								gin.go
									
									
									
									
									
								
							
							
						
						
									
										177
									
								
								gin.go
									
									
									
									
									
								
							| @ -27,9 +27,9 @@ type Params []Param | |||||||
| // ByName returns the value of the first Param which key matches the given name. | // ByName returns the value of the first Param which key matches the given name. | ||||||
| // If no matching Param is found, an empty string is returned. | // If no matching Param is found, an empty string is returned. | ||||||
| func (ps Params) ByName(name string) string { | func (ps Params) ByName(name string) string { | ||||||
| 	for i := range ps { | 	for _, entry := range ps { | ||||||
| 		if ps[i].Key == name { | 		if entry.Key == name { | ||||||
| 			return ps[i].Value | 			return entry.Value | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return "" | 	return "" | ||||||
| @ -43,7 +43,7 @@ type ( | |||||||
|  |  | ||||||
| 	// Represents the web framework, it wraps the blazing fast httprouter multiplexer and a list of global middlewares. | 	// Represents the web framework, it wraps the blazing fast httprouter multiplexer and a list of global middlewares. | ||||||
| 	Engine struct { | 	Engine struct { | ||||||
| 		*RouterGroup | 		RouterGroup | ||||||
| 		HTMLRender  render.Render | 		HTMLRender  render.Render | ||||||
| 		pool        sync.Pool | 		pool        sync.Pool | ||||||
| 		allNoRoute  []HandlerFunc | 		allNoRoute  []HandlerFunc | ||||||
| @ -84,16 +84,16 @@ type ( | |||||||
| // The most basic configuration | // The most basic configuration | ||||||
| func New() *Engine { | func New() *Engine { | ||||||
| 	engine := &Engine{ | 	engine := &Engine{ | ||||||
|  | 		RouterGroup: RouterGroup{ | ||||||
|  | 			Handlers:     nil, | ||||||
|  | 			absolutePath: "/", | ||||||
|  | 		}, | ||||||
| 		RedirectTrailingSlash:  true, | 		RedirectTrailingSlash:  true, | ||||||
| 		RedirectFixedPath:      true, | 		RedirectFixedPath:      true, | ||||||
| 		HandleMethodNotAllowed: true, | 		HandleMethodNotAllowed: true, | ||||||
| 		trees: make(map[string]*node), | 		trees: make(map[string]*node), | ||||||
| 	} | 	} | ||||||
| 	engine.RouterGroup = &RouterGroup{ | 	engine.RouterGroup.engine = engine | ||||||
| 		Handlers:     nil, |  | ||||||
| 		absolutePath: "/", |  | ||||||
| 		engine:       engine, |  | ||||||
| 	} |  | ||||||
| 	engine.pool.New = func() interface{} { | 	engine.pool.New = func() interface{} { | ||||||
| 		return engine.allocateContext() | 		return engine.allocateContext() | ||||||
| 	} | 	} | ||||||
| @ -109,23 +109,10 @@ func Default() *Engine { | |||||||
|  |  | ||||||
| func (engine *Engine) allocateContext() (context *Context) { | func (engine *Engine) allocateContext() (context *Context) { | ||||||
| 	context = &Context{Engine: engine} | 	context = &Context{Engine: engine} | ||||||
| 	context.Writer = &context.writermem |  | ||||||
| 	context.Input = inputHolder{context: context} | 	context.Input = inputHolder{context: context} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
| func (engine *Engine) createContext(w http.ResponseWriter, req *http.Request) *Context { |  | ||||||
| 	c := engine.pool.Get().(*Context) |  | ||||||
| 	c.reset() |  | ||||||
| 	c.writermem.reset(w) |  | ||||||
| 	c.Request = req |  | ||||||
| 	return c |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (engine *Engine) reuseContext(c *Context) { |  | ||||||
| 	engine.pool.Put(c) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (engine *Engine) LoadHTMLGlob(pattern string) { | func (engine *Engine) LoadHTMLGlob(pattern string) { | ||||||
| 	if IsDebugging() { | 	if IsDebugging() { | ||||||
| 		r := &render.HTMLDebugRender{Glob: pattern} | 		r := &render.HTMLDebugRender{Glob: pattern} | ||||||
| @ -177,40 +164,10 @@ func (engine *Engine) rebuild405Handlers() { | |||||||
| 	engine.allNoMethod = engine.combineHandlers(engine.noMethod) | 	engine.allNoMethod = engine.combineHandlers(engine.noMethod) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (engine *Engine) handle404(c *Context) { |  | ||||||
| 	// set 404 by default, useful for logging |  | ||||||
| 	c.handlers = engine.allNoRoute |  | ||||||
| 	c.Writer.WriteHeader(404) |  | ||||||
| 	c.Next() |  | ||||||
| 	if !c.Writer.Written() { |  | ||||||
| 		if c.Writer.Status() == 404 { |  | ||||||
| 			c.Data(-1, binding.MIMEPlain, default404Body) |  | ||||||
| 		} else { |  | ||||||
| 			c.Writer.WriteHeaderNow() |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (engine *Engine) handle405(c *Context) { |  | ||||||
| 	// set 405 by default, useful for logging |  | ||||||
| 	c.handlers = engine.allNoMethod |  | ||||||
| 	c.Writer.WriteHeader(405) |  | ||||||
| 	c.Next() |  | ||||||
| 	if !c.Writer.Written() { |  | ||||||
| 		if c.Writer.Status() == 405 { |  | ||||||
| 			c.Data(-1, binding.MIMEPlain, default405Body) |  | ||||||
| 		} else { |  | ||||||
| 			c.Writer.WriteHeaderNow() |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (engine *Engine) handle(method, path string, handlers []HandlerFunc) { | func (engine *Engine) handle(method, path string, handlers []HandlerFunc) { | ||||||
| 	if path[0] != '/' { | 	if path[0] != '/' { | ||||||
| 		panic("path must begin with '/'") | 		panic("path must begin with '/'") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	//methodCode := codeForHTTPMethod(method) |  | ||||||
| 	root := engine.trees[method] | 	root := engine.trees[method] | ||||||
| 	if root == nil { | 	if root == nil { | ||||||
| 		root = new(node) | 		root = new(node) | ||||||
| @ -219,27 +176,6 @@ func (engine *Engine) handle(method, path string, handlers []HandlerFunc) { | |||||||
| 	root.addRoute(path, handlers) | 	root.addRoute(path, handlers) | ||||||
| } | } | ||||||
|  |  | ||||||
| // ServeHTTP makes the router implement the http.Handler interface. |  | ||||||
| func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { |  | ||||||
| 	c := engine.createContext(w, req) |  | ||||||
| 	//methodCode := codeForHTTPMethod(req.Method) |  | ||||||
| 	if root := engine.trees[req.Method]; root != nil { |  | ||||||
| 		path := req.URL.Path |  | ||||||
| 		if handlers, params, _ := root.getValue(path, c.Params); handlers != nil { |  | ||||||
| 			c.handlers = handlers |  | ||||||
| 			c.Params = params |  | ||||||
| 			c.Next() |  | ||||||
| 			c.Writer.WriteHeaderNow() |  | ||||||
| 			engine.reuseContext(c) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Handle 404 |  | ||||||
| 	engine.handle404(c) |  | ||||||
| 	engine.reuseContext(c) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (engine *Engine) Run(addr string) error { | func (engine *Engine) Run(addr string) error { | ||||||
| 	debugPrint("Listening and serving HTTP on %s\n", addr) | 	debugPrint("Listening and serving HTTP on %s\n", addr) | ||||||
| 	return http.ListenAndServe(addr, engine) | 	return http.ListenAndServe(addr, engine) | ||||||
| @ -249,3 +185,98 @@ func (engine *Engine) RunTLS(addr string, cert string, key string) error { | |||||||
| 	debugPrint("Listening and serving HTTPS on %s\n", addr) | 	debugPrint("Listening and serving HTTPS on %s\n", addr) | ||||||
| 	return http.ListenAndServeTLS(addr, cert, key, engine) | 	return http.ListenAndServeTLS(addr, cert, key, engine) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ServeHTTP makes the router implement the http.Handler interface. | ||||||
|  | func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { | ||||||
|  | 	context := engine.pool.Get().(*Context) | ||||||
|  | 	context.writermem.reset(w) | ||||||
|  | 	context.Request = req | ||||||
|  | 	context.reset() | ||||||
|  |  | ||||||
|  | 	engine.serveHTTPRequest(context) | ||||||
|  |  | ||||||
|  | 	engine.pool.Put(context) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (engine *Engine) serveHTTPRequest(context *Context) { | ||||||
|  | 	httpMethod := context.Request.Method | ||||||
|  | 	path := context.Request.URL.Path | ||||||
|  |  | ||||||
|  | 	// Find root of the tree for the given HTTP method | ||||||
|  | 	if root := engine.trees[httpMethod]; root != nil { | ||||||
|  | 		// Find route in tree | ||||||
|  | 		handlers, params, tsr := root.getValue(path, context.Params) | ||||||
|  | 		// Dispatch if we found any handlers | ||||||
|  | 		if handlers != nil { | ||||||
|  | 			context.handlers = handlers | ||||||
|  | 			context.Params = params | ||||||
|  | 			context.Next() | ||||||
|  | 			context.writermem.WriteHeaderNow() | ||||||
|  | 			return | ||||||
|  |  | ||||||
|  | 		} else if httpMethod != "CONNECT" && path != "/" { | ||||||
|  | 			if engine.serveAutoRedirect(context, root, tsr) { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if engine.HandleMethodNotAllowed { | ||||||
|  | 		for method, root := range engine.trees { | ||||||
|  | 			if method != httpMethod { | ||||||
|  | 				if handlers, _, _ := root.getValue(path, nil); handlers != nil { | ||||||
|  | 					context.handlers = engine.allNoMethod | ||||||
|  | 					serveError(context, 405, default405Body) | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	context.handlers = engine.allNoMethod | ||||||
|  | 	serveError(context, 404, default404Body) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (engine *Engine) serveAutoRedirect(c *Context, root *node, tsr bool) bool { | ||||||
|  | 	req := c.Request | ||||||
|  | 	path := req.URL.Path | ||||||
|  | 	code := 301 // Permanent redirect, request with GET method | ||||||
|  | 	if req.Method != "GET" { | ||||||
|  | 		code = 307 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if tsr && engine.RedirectTrailingSlash { | ||||||
|  | 		if len(path) > 1 && path[len(path)-1] == '/' { | ||||||
|  | 			req.URL.Path = path[:len(path)-1] | ||||||
|  | 		} else { | ||||||
|  | 			req.URL.Path = path + "/" | ||||||
|  | 		} | ||||||
|  | 		http.Redirect(c.Writer, req, req.URL.String(), code) | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Try to fix the request path | ||||||
|  | 	if engine.RedirectFixedPath { | ||||||
|  | 		fixedPath, found := root.findCaseInsensitivePath( | ||||||
|  | 			CleanPath(path), | ||||||
|  | 			engine.RedirectTrailingSlash, | ||||||
|  | 		) | ||||||
|  | 		if found { | ||||||
|  | 			req.URL.Path = string(fixedPath) | ||||||
|  | 			http.Redirect(c.Writer, req, req.URL.String(), code) | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func serveError(c *Context, code int, defaultMessage []byte) { | ||||||
|  | 	c.writermem.status = code | ||||||
|  | 	c.Next() | ||||||
|  | 	if !c.Writer.Written() { | ||||||
|  | 		if c.Writer.Status() == code { | ||||||
|  | 			c.Data(-1, binding.MIMEPlain, defaultMessage) | ||||||
|  | 		} else { | ||||||
|  | 			c.Writer.WriteHeaderNow() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | |||||||
| @ -125,13 +125,5 @@ func (group *RouterGroup) combineHandlers(handlers []HandlerFunc) []HandlerFunc | |||||||
| } | } | ||||||
|  |  | ||||||
| func (group *RouterGroup) calculateAbsolutePath(relativePath string) string { | func (group *RouterGroup) calculateAbsolutePath(relativePath string) string { | ||||||
| 	if len(relativePath) == 0 { | 	return joinPaths(group.absolutePath, relativePath) | ||||||
| 		return group.absolutePath |  | ||||||
| 	} |  | ||||||
| 	absolutePath := path.Join(group.absolutePath, relativePath) |  | ||||||
| 	appendSlash := lastChar(relativePath) == '/' && lastChar(absolutePath) != '/' |  | ||||||
| 	if appendSlash { |  | ||||||
| 		return absolutePath + "/" |  | ||||||
| 	} |  | ||||||
| 	return absolutePath |  | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										14
									
								
								tree.go
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								tree.go
									
									
									
									
									
								
							| @ -312,6 +312,7 @@ func (n *node) insertChild(numParams uint8, path string, handlers []HandlerFunc) | |||||||
| // made if a handle exists with an extra (without the) trailing slash for the | // made if a handle exists with an extra (without the) trailing slash for the | ||||||
| // given path. | // given path. | ||||||
| func (n *node) getValue(path string, po Params) (handlers []HandlerFunc, p Params, tsr bool) { | func (n *node) getValue(path string, po Params) (handlers []HandlerFunc, p Params, tsr bool) { | ||||||
|  | 	p = po | ||||||
| walk: // Outer loop for walking the tree | walk: // Outer loop for walking the tree | ||||||
| 	for { | 	for { | ||||||
| 		if len(path) > len(n.path) { | 		if len(path) > len(n.path) { | ||||||
| @ -334,7 +335,6 @@ walk: // Outer loop for walking the tree | |||||||
| 					// trailing slash if a leaf exists for that path. | 					// trailing slash if a leaf exists for that path. | ||||||
| 					tsr = (path == "/" && n.handlers != nil) | 					tsr = (path == "/" && n.handlers != nil) | ||||||
| 					return | 					return | ||||||
|  |  | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				// handle wildcard child | 				// handle wildcard child | ||||||
| @ -348,12 +348,8 @@ walk: // Outer loop for walking the tree | |||||||
| 					} | 					} | ||||||
|  |  | ||||||
| 					// save param value | 					// save param value | ||||||
| 					if p == nil { | 					if cap(p) < int(n.maxParams) { | ||||||
| 						if cap(po) < int(n.maxParams) { |  | ||||||
| 						p = make(Params, 0, n.maxParams) | 						p = make(Params, 0, n.maxParams) | ||||||
| 						} else { |  | ||||||
| 							p = po[0:0] |  | ||||||
| 						} |  | ||||||
| 					} | 					} | ||||||
| 					i := len(p) | 					i := len(p) | ||||||
| 					p = p[:i+1] // expand slice within preallocated capacity | 					p = p[:i+1] // expand slice within preallocated capacity | ||||||
| @ -386,12 +382,8 @@ walk: // Outer loop for walking the tree | |||||||
|  |  | ||||||
| 				case catchAll: | 				case catchAll: | ||||||
| 					// save param value | 					// save param value | ||||||
| 					if p == nil { | 					if cap(p) < int(n.maxParams) { | ||||||
| 						if cap(po) < int(n.maxParams) { |  | ||||||
| 						p = make(Params, 0, n.maxParams) | 						p = make(Params, 0, n.maxParams) | ||||||
| 						} else { |  | ||||||
| 							p = po[0:0] |  | ||||||
| 						} |  | ||||||
| 					} | 					} | ||||||
| 					i := len(p) | 					i := len(p) | ||||||
| 					p = p[:i+1] // expand slice within preallocated capacity | 					p = p[:i+1] // expand slice within preallocated capacity | ||||||
|  | |||||||
							
								
								
									
										42
									
								
								utils.go
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								utils.go
									
									
									
									
									
								
							| @ -7,23 +7,12 @@ package gin | |||||||
| import ( | import ( | ||||||
| 	"encoding/xml" | 	"encoding/xml" | ||||||
| 	"log" | 	"log" | ||||||
|  | 	"path" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"runtime" | 	"runtime" | ||||||
| 	"strings" | 	"strings" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( |  | ||||||
| 	methodGET     = iota |  | ||||||
| 	methodPOST    = iota |  | ||||||
| 	methodPUT     = iota |  | ||||||
| 	methodAHEAD   = iota |  | ||||||
| 	methodOPTIONS = iota |  | ||||||
| 	methodDELETE  = iota |  | ||||||
| 	methodCONNECT = iota |  | ||||||
| 	methodTRACE   = iota |  | ||||||
| 	methodUnknown = iota |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type H map[string]interface{} | type H map[string]interface{} | ||||||
|  |  | ||||||
| // Allows type H to be used with xml.Marshal | // Allows type H to be used with xml.Marshal | ||||||
| @ -93,25 +82,14 @@ func nameOfFunction(f interface{}) string { | |||||||
| 	return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() | 	return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() | ||||||
| } | } | ||||||
|  |  | ||||||
| func codeForHTTPMethod(method string) int { | func joinPaths(absolutePath, relativePath string) string { | ||||||
| 	switch method { | 	if len(relativePath) == 0 { | ||||||
| 	case "GET": | 		return absolutePath | ||||||
| 		return methodGET |  | ||||||
| 	case "POST": |  | ||||||
| 		return methodPOST |  | ||||||
| 	case "PUT": |  | ||||||
| 		return methodPUT |  | ||||||
| 	case "AHEAD": |  | ||||||
| 		return methodAHEAD |  | ||||||
| 	case "OPTIONS": |  | ||||||
| 		return methodOPTIONS |  | ||||||
| 	case "DELETE": |  | ||||||
| 		return methodDELETE |  | ||||||
| 	case "TRACE": |  | ||||||
| 		return methodTRACE |  | ||||||
| 	case "CONNECT": |  | ||||||
| 		return methodCONNECT |  | ||||||
| 	default: |  | ||||||
| 		return methodUnknown |  | ||||||
| 	} | 	} | ||||||
|  | 	absolutePath = path.Join(absolutePath, relativePath) | ||||||
|  | 	appendSlash := lastChar(relativePath) == '/' && lastChar(absolutePath) != '/' | ||||||
|  | 	if appendSlash { | ||||||
|  | 		return absolutePath + "/" | ||||||
|  | 	} | ||||||
|  | 	return absolutePath | ||||||
| } | } | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user