Cleaning up performance branch
This commit is contained in:
parent
3faa81a464
commit
1f6304ca25
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user