404 not found performance improvements
benchmark old ns/op new ns/op delta Benchmark404 737 249 -66.21% Benchmark404Many 2330 454 -80.52% benchmark old allocs new allocs delta Benchmark404 3 0 -100.00% Benchmark404Many 10 0 -100.00% benchmark old bytes new bytes delta Benchmark404 115 68 -40.87% Benchmark404Many 235 57 -75.74%
This commit is contained in:
		| @ -283,7 +283,7 @@ func (c *Context) Header(key, value string) { | ||||
| } | ||||
|  | ||||
| func (c *Context) Render(code int, r render.Render) { | ||||
| 	c.Writer.WriteHeader(code) | ||||
| 	c.writermem.WriteHeader(code) | ||||
| 	if err := r.Write(c.Writer); err != nil { | ||||
| 		debugPrintError(err) | ||||
| 		c.AbortWithError(500, err).SetType(ErrorTypeRender) | ||||
|  | ||||
| @ -111,7 +111,7 @@ func (a errorMsgs) Last() *Error { | ||||
| // `` | ||||
| func (a errorMsgs) Errors() []string { | ||||
| 	if len(a) == 0 { | ||||
| 		return []string{} | ||||
| 		return nil | ||||
| 	} | ||||
| 	errorStrings := make([]string, len(a)) | ||||
| 	for i, err := range a { | ||||
|  | ||||
							
								
								
									
										18
									
								
								gin.go
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								gin.go
									
									
									
									
									
								
							| @ -11,7 +11,6 @@ import ( | ||||
| 	"os" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/gin-gonic/gin/binding" | ||||
| 	"github.com/gin-gonic/gin/render" | ||||
| ) | ||||
|  | ||||
| @ -73,8 +72,8 @@ func New() *Engine { | ||||
| 			BasePath: "/", | ||||
| 		}, | ||||
| 		RedirectTrailingSlash:  true, | ||||
| 		RedirectFixedPath:      true, | ||||
| 		HandleMethodNotAllowed: true, | ||||
| 		RedirectFixedPath:      false, | ||||
| 		HandleMethodNotAllowed: false, | ||||
| 		trees: make(methodTrees, 0, 6), | ||||
| 	} | ||||
| 	engine.RouterGroup.engine = engine | ||||
| @ -285,7 +284,7 @@ func (engine *Engine) serveAutoRedirect(c *Context, root *node, tsr bool) bool { | ||||
| 	// Try to fix the request path | ||||
| 	if engine.RedirectFixedPath { | ||||
| 		fixedPath, found := root.findCaseInsensitivePath( | ||||
| 			CleanPath(path), | ||||
| 			cleanPath(path), | ||||
| 			engine.RedirectTrailingSlash, | ||||
| 		) | ||||
| 		if found { | ||||
| @ -299,14 +298,17 @@ func (engine *Engine) serveAutoRedirect(c *Context, root *node, tsr bool) bool { | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| var mimePlain = []string{MIMEPlain} | ||||
|  | ||||
| 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) | ||||
| 	if !c.writermem.Written() { | ||||
| 		if c.writermem.Status() == code { | ||||
| 			c.writermem.Header()["Content-Type"] = mimePlain | ||||
| 			c.Writer.Write(defaultMessage) | ||||
| 		} else { | ||||
| 			c.Writer.WriteHeaderNow() | ||||
| 			c.writermem.WriteHeaderNow() | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -25,9 +25,6 @@ func TestCreateEngine(t *testing.T) { | ||||
| 	assert.Equal(t, "/", router.BasePath) | ||||
| 	assert.Equal(t, router.engine, router) | ||||
| 	assert.Empty(t, router.Handlers) | ||||
| 	assert.True(t, router.RedirectTrailingSlash) | ||||
| 	assert.True(t, router.RedirectFixedPath) | ||||
| 	assert.True(t, router.HandleMethodNotAllowed) | ||||
|  | ||||
| 	assert.Panics(t, func() { router.addRoute("", "/", HandlersChain{func(_ *Context) {}}) }) | ||||
| 	assert.Panics(t, func() { router.addRoute("GET", "a", HandlersChain{func(_ *Context) {}}) }) | ||||
|  | ||||
| @ -77,9 +77,10 @@ func TestMiddlewareNoRoute(t *testing.T) { | ||||
| 	assert.Equal(t, signature, "ACEGHFDB") | ||||
| } | ||||
|  | ||||
| func TestMiddlewareNoMethod(t *testing.T) { | ||||
| func TestMiddlewareNoMethodEnabled(t *testing.T) { | ||||
| 	signature := "" | ||||
| 	router := New() | ||||
| 	router.HandleMethodNotAllowed = true | ||||
| 	router.Use(func(c *Context) { | ||||
| 		signature += "A" | ||||
| 		c.Next() | ||||
| @ -113,6 +114,43 @@ func TestMiddlewareNoMethod(t *testing.T) { | ||||
| 	assert.Equal(t, signature, "ACEGHFDB") | ||||
| } | ||||
|  | ||||
| func TestMiddlewareNoMethodDisabled(t *testing.T) { | ||||
| 	signature := "" | ||||
| 	router := New() | ||||
| 	router.HandleMethodNotAllowed = false | ||||
| 	router.Use(func(c *Context) { | ||||
| 		signature += "A" | ||||
| 		c.Next() | ||||
| 		signature += "B" | ||||
| 	}) | ||||
| 	router.Use(func(c *Context) { | ||||
| 		signature += "C" | ||||
| 		c.Next() | ||||
| 		signature += "D" | ||||
| 	}) | ||||
| 	router.NoMethod(func(c *Context) { | ||||
| 		signature += "E" | ||||
| 		c.Next() | ||||
| 		signature += "F" | ||||
| 	}, func(c *Context) { | ||||
| 		signature += "G" | ||||
| 		c.Next() | ||||
| 		signature += "H" | ||||
| 	}) | ||||
| 	router.NoRoute(func(c *Context) { | ||||
| 		signature += " X " | ||||
| 	}) | ||||
| 	router.POST("/", func(c *Context) { | ||||
| 		signature += " XX " | ||||
| 	}) | ||||
| 	// RUN | ||||
| 	w := performRequest(router, "GET", "/") | ||||
|  | ||||
| 	// TEST | ||||
| 	assert.Equal(t, w.Code, 404) | ||||
| 	assert.Equal(t, signature, "AC X DB") | ||||
| } | ||||
|  | ||||
| func TestMiddlewareAbort(t *testing.T) { | ||||
| 	signature := "" | ||||
| 	router := New() | ||||
|  | ||||
							
								
								
									
										2
									
								
								path.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								path.go
									
									
									
									
									
								
							| @ -18,7 +18,7 @@ package gin | ||||
| //	   that is, replace "/.." by "/" at the beginning of a path. | ||||
| // | ||||
| // If the result of this process is an empty string, "/" is returned | ||||
| func CleanPath(p string) string { | ||||
| func cleanPath(p string) string { | ||||
| 	// Turn empty string into "/" | ||||
| 	if p == "" { | ||||
| 		return "/" | ||||
|  | ||||
| @ -67,8 +67,8 @@ var cleanTests = []struct { | ||||
|  | ||||
| func TestPathClean(t *testing.T) { | ||||
| 	for _, test := range cleanTests { | ||||
| 		assert.Equal(t, CleanPath(test.path), test.result) | ||||
| 		assert.Equal(t, CleanPath(test.result), test.result) | ||||
| 		assert.Equal(t, cleanPath(test.path), test.result) | ||||
| 		assert.Equal(t, cleanPath(test.result), test.result) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -82,7 +82,7 @@ func TestPathCleanMallocs(t *testing.T) { | ||||
| 	} | ||||
|  | ||||
| 	for _, test := range cleanTests { | ||||
| 		allocs := testing.AllocsPerRun(100, func() { CleanPath(test.result) }) | ||||
| 		allocs := testing.AllocsPerRun(100, func() { cleanPath(test.result) }) | ||||
| 		assert.Equal(t, allocs, 0) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -13,7 +13,7 @@ type Data struct { | ||||
|  | ||||
| func (r Data) Write(w http.ResponseWriter) error { | ||||
| 	if len(r.ContentType) > 0 { | ||||
| 		w.Header().Set("Content-Type", r.ContentType) | ||||
| 		w.Header()["Content-Type"] = []string{r.ContentType} | ||||
| 	} | ||||
| 	w.Write(r.Data) | ||||
| 	return nil | ||||
|  | ||||
| @ -6,6 +6,7 @@ package render | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| ) | ||||
|  | ||||
| @ -24,7 +25,7 @@ func (r String) Write(w http.ResponseWriter) error { | ||||
| 	if len(r.Data) > 0 { | ||||
| 		fmt.Fprintf(w, r.Format, r.Data...) | ||||
| 	} else { | ||||
| 		w.Write([]byte(r.Format)) | ||||
| 		io.WriteString(w, r.Format) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @ -6,6 +6,7 @@ package gin | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"io" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| ) | ||||
| @ -24,6 +25,7 @@ type ( | ||||
|  | ||||
| 		Status() int | ||||
| 		Size() int | ||||
| 		WriteString(string) (int, error) | ||||
| 		Written() bool | ||||
| 		WriteHeaderNow() | ||||
| 	} | ||||
| @ -35,6 +37,8 @@ type ( | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| var _ ResponseWriter = &responseWriter{} | ||||
|  | ||||
| func (w *responseWriter) reset(writer http.ResponseWriter) { | ||||
| 	w.ResponseWriter = writer | ||||
| 	w.size = noWritten | ||||
| @ -64,6 +68,13 @@ func (w *responseWriter) Write(data []byte) (n int, err error) { | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (w *responseWriter) WriteString(s string) (n int, err error) { | ||||
| 	w.WriteHeaderNow() | ||||
| 	n, err = io.WriteString(w.ResponseWriter, s) | ||||
| 	w.size += n | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (w *responseWriter) Status() int { | ||||
| 	return w.status | ||||
| } | ||||
|  | ||||
| @ -61,6 +61,7 @@ func testRouteNotOK(method string, t *testing.T) { | ||||
| func testRouteNotOK2(method string, t *testing.T) { | ||||
| 	passed := false | ||||
| 	router := New() | ||||
| 	router.HandleMethodNotAllowed = true | ||||
| 	var methodRoute string | ||||
| 	if method == "POST" { | ||||
| 		methodRoute = "GET" | ||||
| @ -224,9 +225,9 @@ func TestRouterMiddlewareAndStatic(t *testing.T) { | ||||
| 	assert.Equal(t, w.HeaderMap.Get("x-GIN"), "Gin Framework") | ||||
| } | ||||
|  | ||||
| func TestRouteNotAllowed(t *testing.T) { | ||||
| func TestRouteNotAllowedEnabled(t *testing.T) { | ||||
| 	router := New() | ||||
|  | ||||
| 	router.HandleMethodNotAllowed = true | ||||
| 	router.POST("/path", func(c *Context) {}) | ||||
| 	w := performRequest(router, "GET", "/path") | ||||
| 	assert.Equal(t, w.Code, http.StatusMethodNotAllowed) | ||||
| @ -239,8 +240,24 @@ func TestRouteNotAllowed(t *testing.T) { | ||||
| 	assert.Equal(t, w.Code, http.StatusTeapot) | ||||
| } | ||||
|  | ||||
| func TestRouteNotAllowedDisabled(t *testing.T) { | ||||
| 	router := New() | ||||
| 	router.HandleMethodNotAllowed = false | ||||
| 	router.POST("/path", func(c *Context) {}) | ||||
| 	w := performRequest(router, "GET", "/path") | ||||
| 	assert.Equal(t, w.Code, 404) | ||||
|  | ||||
| 	router.NoMethod(func(c *Context) { | ||||
| 		c.String(http.StatusTeapot, "responseText") | ||||
| 	}) | ||||
| 	w = performRequest(router, "GET", "/path") | ||||
| 	assert.Equal(t, w.Body.String(), "404 page not found") | ||||
| 	assert.Equal(t, w.Code, 404) | ||||
| } | ||||
|  | ||||
| func TestRouterNotFound(t *testing.T) { | ||||
| 	router := New() | ||||
| 	router.RedirectFixedPath = true | ||||
| 	router.GET("/path", func(c *Context) {}) | ||||
| 	router.GET("/dir/", func(c *Context) {}) | ||||
| 	router.GET("/", func(c *Context) {}) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user