mirror of https://github.com/gin-gonic/gin.git
Cleaning up performance branch
This commit is contained in:
parent
3faa81a464
commit
1f6304ca25
|
@ -6,7 +6,6 @@ package binding
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
@ -27,8 +26,6 @@ func mapForm(ptr interface{}, form map[string][]string) error {
|
|||
inputFieldName = typeField.Name
|
||||
}
|
||||
inputValue, exists := form[inputFieldName]
|
||||
fmt.Println("Field: "+inputFieldName+" Value: ", inputValue)
|
||||
|
||||
if !exists {
|
||||
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,
|
||||
// manage the flow, validate the JSON of a request and render a JSON response for example.
|
||||
type Context struct {
|
||||
Engine *Engine
|
||||
writermem responseWriter
|
||||
Request *http.Request
|
||||
Writer ResponseWriter
|
||||
|
@ -30,6 +29,7 @@ type Context struct {
|
|||
handlers []HandlerFunc
|
||||
index int8
|
||||
|
||||
Engine *Engine
|
||||
Keys map[string]interface{}
|
||||
Errors errorMsgs
|
||||
accepted []string
|
||||
|
@ -40,10 +40,13 @@ type Context struct {
|
|||
/************************************/
|
||||
|
||||
func (c *Context) reset() {
|
||||
c.Keys = nil
|
||||
c.Writer = &c.writermem
|
||||
c.Params = c.Params[0:0]
|
||||
c.handlers = nil
|
||||
c.index = -1
|
||||
c.accepted = nil
|
||||
c.Keys = nil
|
||||
c.Errors = c.Errors[0:0]
|
||||
c.accepted = nil
|
||||
}
|
||||
|
||||
func (c *Context) Copy() *Context {
|
||||
|
@ -114,9 +117,8 @@ func (c *Context) LastError() error {
|
|||
nuErrors := len(c.Errors)
|
||||
if nuErrors > 0 {
|
||||
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.
|
||||
// If no matching Param is found, an empty string is returned.
|
||||
func (ps Params) ByName(name string) string {
|
||||
for i := range ps {
|
||||
if ps[i].Key == name {
|
||||
return ps[i].Value
|
||||
for _, entry := range ps {
|
||||
if entry.Key == name {
|
||||
return entry.Value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
|
@ -43,7 +43,7 @@ type (
|
|||
|
||||
// Represents the web framework, it wraps the blazing fast httprouter multiplexer and a list of global middlewares.
|
||||
Engine struct {
|
||||
*RouterGroup
|
||||
RouterGroup
|
||||
HTMLRender render.Render
|
||||
pool sync.Pool
|
||||
allNoRoute []HandlerFunc
|
||||
|
@ -84,16 +84,16 @@ type (
|
|||
// The most basic configuration
|
||||
func New() *Engine {
|
||||
engine := &Engine{
|
||||
RouterGroup: RouterGroup{
|
||||
Handlers: nil,
|
||||
absolutePath: "/",
|
||||
},
|
||||
RedirectTrailingSlash: true,
|
||||
RedirectFixedPath: true,
|
||||
HandleMethodNotAllowed: true,
|
||||
trees: make(map[string]*node),
|
||||
}
|
||||
engine.RouterGroup = &RouterGroup{
|
||||
Handlers: nil,
|
||||
absolutePath: "/",
|
||||
engine: engine,
|
||||
}
|
||||
engine.RouterGroup.engine = engine
|
||||
engine.pool.New = func() interface{} {
|
||||
return engine.allocateContext()
|
||||
}
|
||||
|
@ -109,23 +109,10 @@ func Default() *Engine {
|
|||
|
||||
func (engine *Engine) allocateContext() (context *Context) {
|
||||
context = &Context{Engine: engine}
|
||||
context.Writer = &context.writermem
|
||||
context.Input = inputHolder{context: context}
|
||||
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) {
|
||||
if IsDebugging() {
|
||||
r := &render.HTMLDebugRender{Glob: pattern}
|
||||
|
@ -177,40 +164,10 @@ func (engine *Engine) rebuild405Handlers() {
|
|||
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) {
|
||||
if path[0] != '/' {
|
||||
panic("path must begin with '/'")
|
||||
}
|
||||
|
||||
//methodCode := codeForHTTPMethod(method)
|
||||
root := engine.trees[method]
|
||||
if root == nil {
|
||||
root = new(node)
|
||||
|
@ -219,27 +176,6 @@ func (engine *Engine) handle(method, path string, handlers []HandlerFunc) {
|
|||
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 {
|
||||
debugPrint("Listening and serving HTTP on %s\n", addr)
|
||||
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)
|
||||
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 {
|
||||
if len(relativePath) == 0 {
|
||||
return group.absolutePath
|
||||
}
|
||||
absolutePath := path.Join(group.absolutePath, relativePath)
|
||||
appendSlash := lastChar(relativePath) == '/' && lastChar(absolutePath) != '/'
|
||||
if appendSlash {
|
||||
return absolutePath + "/"
|
||||
}
|
||||
return absolutePath
|
||||
return joinPaths(group.absolutePath, relativePath)
|
||||
}
|
||||
|
|
18
tree.go
18
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
|
||||
// given path.
|
||||
func (n *node) getValue(path string, po Params) (handlers []HandlerFunc, p Params, tsr bool) {
|
||||
p = po
|
||||
walk: // Outer loop for walking the tree
|
||||
for {
|
||||
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.
|
||||
tsr = (path == "/" && n.handlers != nil)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// handle wildcard child
|
||||
|
@ -348,12 +348,8 @@ walk: // Outer loop for walking the tree
|
|||
}
|
||||
|
||||
// save param value
|
||||
if p == nil {
|
||||
if cap(po) < int(n.maxParams) {
|
||||
p = make(Params, 0, n.maxParams)
|
||||
} else {
|
||||
p = po[0:0]
|
||||
}
|
||||
if cap(p) < int(n.maxParams) {
|
||||
p = make(Params, 0, n.maxParams)
|
||||
}
|
||||
i := len(p)
|
||||
p = p[:i+1] // expand slice within preallocated capacity
|
||||
|
@ -386,12 +382,8 @@ walk: // Outer loop for walking the tree
|
|||
|
||||
case catchAll:
|
||||
// save param value
|
||||
if p == nil {
|
||||
if cap(po) < int(n.maxParams) {
|
||||
p = make(Params, 0, n.maxParams)
|
||||
} else {
|
||||
p = po[0:0]
|
||||
}
|
||||
if cap(p) < int(n.maxParams) {
|
||||
p = make(Params, 0, n.maxParams)
|
||||
}
|
||||
i := len(p)
|
||||
p = p[:i+1] // expand slice within preallocated capacity
|
||||
|
|
42
utils.go
42
utils.go
|
@ -7,23 +7,12 @@ package gin
|
|||
import (
|
||||
"encoding/xml"
|
||||
"log"
|
||||
"path"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"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{}
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
func codeForHTTPMethod(method string) int {
|
||||
switch method {
|
||||
case "GET":
|
||||
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
|
||||
func joinPaths(absolutePath, relativePath string) string {
|
||||
if len(relativePath) == 0 {
|
||||
return absolutePath
|
||||
}
|
||||
absolutePath = path.Join(absolutePath, relativePath)
|
||||
appendSlash := lastChar(relativePath) == '/' && lastChar(absolutePath) != '/'
|
||||
if appendSlash {
|
||||
return absolutePath + "/"
|
||||
}
|
||||
return absolutePath
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue