mirror of
https://github.com/gin-gonic/gin.git
synced 2026-07-14 23:21:18 +08:00
Compare commits
11 Commits
60972e8c9f
...
cf805be1e8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf805be1e8 | ||
|
|
e88fc8927a | ||
|
|
5fad976b37 | ||
|
|
93ff771e6d | ||
|
|
58135f06cf | ||
|
|
a85ef5ce4d | ||
|
|
fb27ef26c2 | ||
|
|
19c2d5c0d1 | ||
|
|
a9401cd238 | ||
|
|
d1bcabc7ee | ||
|
|
9eaba78ab6 |
4
.github/workflows/gin.yml
vendored
4
.github/workflows/gin.yml
vendored
@ -24,9 +24,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version: "^1"
|
go-version: "^1"
|
||||||
- name: Setup golangci-lint
|
- name: Setup golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v8
|
uses: golangci/golangci-lint-action@v9
|
||||||
with:
|
with:
|
||||||
version: v2.1.6
|
version: v2.6
|
||||||
args: --verbose
|
args: --verbose
|
||||||
test:
|
test:
|
||||||
needs: lint
|
needs: lint
|
||||||
|
|||||||
5
.github/workflows/trivy-scan.yml
vendored
5
.github/workflows/trivy-scan.yml
vendored
@ -8,9 +8,8 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
schedule:
|
schedule:
|
||||||
# Run every 3 months (quarterly) on the 1st day at 00:00 UTC
|
# Run daily at 00:00 UTC
|
||||||
# Months: January (1), April (4), July (7), October (10)
|
- cron: '0 0 * * *'
|
||||||
- cron: '0 0 1 1,4,7,10 *'
|
|
||||||
workflow_dispatch: # Allow manual trigger
|
workflow_dispatch: # Allow manual trigger
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
|||||||
@ -18,15 +18,8 @@ linters:
|
|||||||
- wastedassign
|
- wastedassign
|
||||||
settings:
|
settings:
|
||||||
gosec:
|
gosec:
|
||||||
includes:
|
excludes:
|
||||||
- G102
|
- G115
|
||||||
- G106
|
|
||||||
- G108
|
|
||||||
- G109
|
|
||||||
- G111
|
|
||||||
- G112
|
|
||||||
- G201
|
|
||||||
- G203
|
|
||||||
perfsprint:
|
perfsprint:
|
||||||
int-conversion: true
|
int-conversion: true
|
||||||
err-error: true
|
err-error: true
|
||||||
@ -68,7 +61,6 @@ linters:
|
|||||||
- examples$
|
- examples$
|
||||||
formatters:
|
formatters:
|
||||||
enable:
|
enable:
|
||||||
- gci
|
|
||||||
- gofmt
|
- gofmt
|
||||||
- gofumpt
|
- gofumpt
|
||||||
- goimports
|
- goimports
|
||||||
@ -80,7 +72,4 @@ formatters:
|
|||||||
exclusions:
|
exclusions:
|
||||||
generated: lax
|
generated: lax
|
||||||
paths:
|
paths:
|
||||||
- third_party$
|
|
||||||
- builtin$
|
|
||||||
- examples$
|
|
||||||
- gin.go
|
- gin.go
|
||||||
|
|||||||
@ -18,9 +18,8 @@ func BenchmarkSliceValidationError(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for b.Loop() {
|
||||||
if len(e.Error()) == 0 {
|
if len(e.Error()) == 0 {
|
||||||
b.Errorf("error")
|
b.Errorf("error")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,7 @@ type structFull struct {
|
|||||||
|
|
||||||
func BenchmarkMapFormFull(b *testing.B) {
|
func BenchmarkMapFormFull(b *testing.B) {
|
||||||
var s structFull
|
var s structFull
|
||||||
for i := 0; i < b.N; i++ {
|
for b.Loop() {
|
||||||
err := mapForm(&s, form)
|
err := mapForm(&s, form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("Error on a form mapping")
|
b.Fatalf("Error on a form mapping")
|
||||||
@ -54,7 +54,7 @@ type structName struct {
|
|||||||
|
|
||||||
func BenchmarkMapFormName(b *testing.B) {
|
func BenchmarkMapFormName(b *testing.B) {
|
||||||
var s structName
|
var s structName
|
||||||
for i := 0; i < b.N; i++ {
|
for b.Loop() {
|
||||||
err := mapForm(&s, form)
|
err := mapForm(&s, form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("Error on a form mapping")
|
b.Fatalf("Error on a form mapping")
|
||||||
|
|||||||
40
context.go
40
context.go
@ -55,6 +55,14 @@ const ContextRequestKey ContextKeyType = 0
|
|||||||
// abortIndex represents a typical value used in abort functions.
|
// abortIndex represents a typical value used in abort functions.
|
||||||
const abortIndex int8 = math.MaxInt8 >> 1
|
const abortIndex int8 = math.MaxInt8 >> 1
|
||||||
|
|
||||||
|
// safeInt8 converts int to int8 safely, capping at math.MaxInt8
|
||||||
|
func safeInt8(n int) int8 {
|
||||||
|
if n > math.MaxInt8 {
|
||||||
|
return math.MaxInt8
|
||||||
|
}
|
||||||
|
return int8(n)
|
||||||
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
@ -186,7 +194,7 @@ func (c *Context) FullPath() string {
|
|||||||
// See example in GitHub.
|
// See example in GitHub.
|
||||||
func (c *Context) Next() {
|
func (c *Context) Next() {
|
||||||
c.index++
|
c.index++
|
||||||
for c.index < int8(len(c.handlers)) {
|
for c.index < safeInt8(len(c.handlers)) {
|
||||||
if c.handlers[c.index] != nil {
|
if c.handlers[c.index] != nil {
|
||||||
c.handlers[c.index](c)
|
c.handlers[c.index](c)
|
||||||
}
|
}
|
||||||
@ -830,41 +838,71 @@ func (c *Context) ShouldBind(obj any) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON).
|
// ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON).
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// POST /user
|
||||||
|
// Content-Type: application/json
|
||||||
|
//
|
||||||
|
// Request Body:
|
||||||
|
// {
|
||||||
|
// "name": "Manu",
|
||||||
|
// "age": 20
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// type User struct {
|
||||||
|
// Name string `json:"name"`
|
||||||
|
// Age int `json:"age"`
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var user User
|
||||||
|
// if err := c.ShouldBindJSON(&user); err != nil {
|
||||||
|
// c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// c.JSON(http.StatusOK, user)
|
||||||
func (c *Context) ShouldBindJSON(obj any) error {
|
func (c *Context) ShouldBindJSON(obj any) error {
|
||||||
return c.ShouldBindWith(obj, binding.JSON)
|
return c.ShouldBindWith(obj, binding.JSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShouldBindXML is a shortcut for c.ShouldBindWith(obj, binding.XML).
|
// ShouldBindXML is a shortcut for c.ShouldBindWith(obj, binding.XML).
|
||||||
|
// It works like ShouldBindJSON but binds the request body as XML data.
|
||||||
func (c *Context) ShouldBindXML(obj any) error {
|
func (c *Context) ShouldBindXML(obj any) error {
|
||||||
return c.ShouldBindWith(obj, binding.XML)
|
return c.ShouldBindWith(obj, binding.XML)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query).
|
// ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query).
|
||||||
|
// It works like ShouldBindJSON but binds query parameters from the URL.
|
||||||
func (c *Context) ShouldBindQuery(obj any) error {
|
func (c *Context) ShouldBindQuery(obj any) error {
|
||||||
return c.ShouldBindWith(obj, binding.Query)
|
return c.ShouldBindWith(obj, binding.Query)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShouldBindYAML is a shortcut for c.ShouldBindWith(obj, binding.YAML).
|
// ShouldBindYAML is a shortcut for c.ShouldBindWith(obj, binding.YAML).
|
||||||
|
// It works like ShouldBindJSON but binds the request body as YAML data.
|
||||||
func (c *Context) ShouldBindYAML(obj any) error {
|
func (c *Context) ShouldBindYAML(obj any) error {
|
||||||
return c.ShouldBindWith(obj, binding.YAML)
|
return c.ShouldBindWith(obj, binding.YAML)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShouldBindTOML is a shortcut for c.ShouldBindWith(obj, binding.TOML).
|
// ShouldBindTOML is a shortcut for c.ShouldBindWith(obj, binding.TOML).
|
||||||
|
// It works like ShouldBindJSON but binds the request body as TOML data.
|
||||||
func (c *Context) ShouldBindTOML(obj any) error {
|
func (c *Context) ShouldBindTOML(obj any) error {
|
||||||
return c.ShouldBindWith(obj, binding.TOML)
|
return c.ShouldBindWith(obj, binding.TOML)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShouldBindPlain is a shortcut for c.ShouldBindWith(obj, binding.Plain).
|
// ShouldBindPlain is a shortcut for c.ShouldBindWith(obj, binding.Plain).
|
||||||
|
// It works like ShouldBindJSON but binds plain text data from the request body.
|
||||||
func (c *Context) ShouldBindPlain(obj any) error {
|
func (c *Context) ShouldBindPlain(obj any) error {
|
||||||
return c.ShouldBindWith(obj, binding.Plain)
|
return c.ShouldBindWith(obj, binding.Plain)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShouldBindHeader is a shortcut for c.ShouldBindWith(obj, binding.Header).
|
// ShouldBindHeader is a shortcut for c.ShouldBindWith(obj, binding.Header).
|
||||||
|
// It works like ShouldBindJSON but binds values from HTTP headers.
|
||||||
func (c *Context) ShouldBindHeader(obj any) error {
|
func (c *Context) ShouldBindHeader(obj any) error {
|
||||||
return c.ShouldBindWith(obj, binding.Header)
|
return c.ShouldBindWith(obj, binding.Header)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShouldBindUri binds the passed struct pointer using the specified binding engine.
|
// ShouldBindUri binds the passed struct pointer using the specified binding engine.
|
||||||
|
// It works like ShouldBindJSON but binds parameters from the URI.
|
||||||
func (c *Context) ShouldBindUri(obj any) error {
|
func (c *Context) ShouldBindUri(obj any) error {
|
||||||
m := make(map[string][]string, len(c.Params))
|
m := make(map[string][]string, len(c.Params))
|
||||||
for _, v := range c.Params {
|
for _, v := range c.Params {
|
||||||
|
|||||||
@ -292,7 +292,7 @@ func TestContextReset(t *testing.T) {
|
|||||||
assert.Empty(t, c.Errors.Errors())
|
assert.Empty(t, c.Errors.Errors())
|
||||||
assert.Empty(t, c.Errors.ByType(ErrorTypeAny))
|
assert.Empty(t, c.Errors.ByType(ErrorTypeAny))
|
||||||
assert.Empty(t, c.Params)
|
assert.Empty(t, c.Params)
|
||||||
assert.EqualValues(t, c.index, -1)
|
assert.EqualValues(t, -1, c.index)
|
||||||
assert.Equal(t, c.Writer.(*responseWriter), &c.writermem)
|
assert.Equal(t, c.Writer.(*responseWriter), &c.writermem)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,7 +384,7 @@ func TestContextSetGetValues(t *testing.T) {
|
|||||||
c.Set("intInterface", a)
|
c.Set("intInterface", a)
|
||||||
|
|
||||||
assert.Exactly(t, "this is a string", c.MustGet("string").(string))
|
assert.Exactly(t, "this is a string", c.MustGet("string").(string))
|
||||||
assert.Exactly(t, c.MustGet("int32").(int32), int32(-42))
|
assert.Exactly(t, int32(-42), c.MustGet("int32").(int32))
|
||||||
assert.Exactly(t, int64(42424242424242), c.MustGet("int64").(int64))
|
assert.Exactly(t, int64(42424242424242), c.MustGet("int64").(int64))
|
||||||
assert.Exactly(t, uint64(42), c.MustGet("uint64").(uint64))
|
assert.Exactly(t, uint64(42), c.MustGet("uint64").(uint64))
|
||||||
assert.InDelta(t, float32(4.2), c.MustGet("float32").(float32), 0.01)
|
assert.InDelta(t, float32(4.2), c.MustGet("float32").(float32), 0.01)
|
||||||
|
|||||||
@ -1071,6 +1071,7 @@ func main() {
|
|||||||
|
|
||||||
if err := c.ShouldBindHeader(&h); err != nil {
|
if err := c.ShouldBindHeader(&h); err != nil {
|
||||||
c.JSON(http.StatusOK, err)
|
c.JSON(http.StatusOK, err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%#v\n", h)
|
fmt.Printf("%#v\n", h)
|
||||||
|
|||||||
46
gin.go
46
gin.go
@ -23,10 +23,12 @@ import (
|
|||||||
"golang.org/x/net/http2/h2c"
|
"golang.org/x/net/http2/h2c"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultMultipartMemory = 32 << 20 // 32 MB
|
const (
|
||||||
const escapedColon = "\\:"
|
defaultMultipartMemory = 32 << 20 // 32 MB
|
||||||
const colon = ":"
|
escapedColon = "\\:"
|
||||||
const backslash = "\\"
|
colon = ":"
|
||||||
|
backslash = "\\"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
default404Body = []byte("404 page not found")
|
default404Body = []byte("404 page not found")
|
||||||
@ -46,8 +48,10 @@ var defaultTrustedCIDRs = []*net.IPNet{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var regSafePrefix = regexp.MustCompile("[^a-zA-Z0-9/-]+")
|
var (
|
||||||
var regRemoveRepeatedChar = regexp.MustCompile("/{2,}")
|
regSafePrefix = regexp.MustCompile("[^a-zA-Z0-9/-]+")
|
||||||
|
regRemoveRepeatedChar = regexp.MustCompile("/{2,}")
|
||||||
|
)
|
||||||
|
|
||||||
// HandlerFunc defines the handler used by gin middleware as return value.
|
// HandlerFunc defines the handler used by gin middleware as return value.
|
||||||
type HandlerFunc func(*Context)
|
type HandlerFunc func(*Context)
|
||||||
@ -94,6 +98,10 @@ const (
|
|||||||
type Engine struct {
|
type Engine struct {
|
||||||
RouterGroup
|
RouterGroup
|
||||||
|
|
||||||
|
// routeTreesUpdated ensures that the initialization or update of the route trees
|
||||||
|
// (used for routing HTTP requests) happens only once, even if called multiple times concurrently.
|
||||||
|
routeTreesUpdated sync.Once
|
||||||
|
|
||||||
// RedirectTrailingSlash enables automatic redirection if the current route can't be matched but a
|
// RedirectTrailingSlash enables automatic redirection if the current route can't be matched but a
|
||||||
// handler for the path with (without) the trailing slash exists.
|
// handler for the path with (without) the trailing slash exists.
|
||||||
// For example if /foo/ is requested but a route only exists for /foo, the
|
// For example if /foo/ is requested but a route only exists for /foo, the
|
||||||
@ -537,7 +545,11 @@ func (engine *Engine) Run(addr ...string) (err error) {
|
|||||||
engine.updateRouteTrees()
|
engine.updateRouteTrees()
|
||||||
address := resolveAddress(addr)
|
address := resolveAddress(addr)
|
||||||
debugPrint("Listening and serving HTTP on %s\n", address)
|
debugPrint("Listening and serving HTTP on %s\n", address)
|
||||||
err = http.ListenAndServe(address, engine.Handler())
|
server := &http.Server{ // #nosec G112
|
||||||
|
Addr: address,
|
||||||
|
Handler: engine.Handler(),
|
||||||
|
}
|
||||||
|
err = server.ListenAndServe()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -553,7 +565,11 @@ func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) {
|
|||||||
"Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.")
|
"Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = http.ListenAndServeTLS(addr, certFile, keyFile, engine.Handler())
|
server := &http.Server{ // #nosec G112
|
||||||
|
Addr: addr,
|
||||||
|
Handler: engine.Handler(),
|
||||||
|
}
|
||||||
|
err = server.ListenAndServeTLS(certFile, keyFile)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -576,7 +592,10 @@ func (engine *Engine) RunUnix(file string) (err error) {
|
|||||||
defer listener.Close()
|
defer listener.Close()
|
||||||
defer os.Remove(file)
|
defer os.Remove(file)
|
||||||
|
|
||||||
err = http.Serve(listener, engine.Handler())
|
server := &http.Server{ // #nosec G112
|
||||||
|
Handler: engine.Handler(),
|
||||||
|
}
|
||||||
|
err = server.Serve(listener)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -630,12 +649,19 @@ func (engine *Engine) RunListener(listener net.Listener) (err error) {
|
|||||||
"Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.")
|
"Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = http.Serve(listener, engine.Handler())
|
server := &http.Server{ // #nosec G112
|
||||||
|
Handler: engine.Handler(),
|
||||||
|
}
|
||||||
|
err = server.Serve(listener)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeHTTP conforms to the http.Handler interface.
|
// ServeHTTP conforms to the http.Handler interface.
|
||||||
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
|
engine.routeTreesUpdated.Do(func() {
|
||||||
|
engine.updateRouteTrees()
|
||||||
|
})
|
||||||
|
|
||||||
c := engine.pool.Get().(*Context)
|
c := engine.pool.Get().(*Context)
|
||||||
c.writermem.reset(w)
|
c.writermem.reset(w)
|
||||||
c.Request = req
|
c.Request = req
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -261,10 +262,11 @@ func TestUnixSocket(t *testing.T) {
|
|||||||
|
|
||||||
fmt.Fprint(c, "GET /example HTTP/1.0\r\n\r\n")
|
fmt.Fprint(c, "GET /example HTTP/1.0\r\n\r\n")
|
||||||
scanner := bufio.NewScanner(c)
|
scanner := bufio.NewScanner(c)
|
||||||
var response string
|
var responseBuilder strings.Builder
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
response += scanner.Text()
|
responseBuilder.WriteString(scanner.Text())
|
||||||
}
|
}
|
||||||
|
response := responseBuilder.String()
|
||||||
assert.Contains(t, response, "HTTP/1.0 200", "should get a 200")
|
assert.Contains(t, response, "HTTP/1.0 200", "should get a 200")
|
||||||
assert.Contains(t, response, "it worked", "resp body should match")
|
assert.Contains(t, response, "it worked", "resp body should match")
|
||||||
}
|
}
|
||||||
@ -322,10 +324,11 @@ func TestFileDescriptor(t *testing.T) {
|
|||||||
|
|
||||||
fmt.Fprintf(c, "GET /example HTTP/1.0\r\n\r\n")
|
fmt.Fprintf(c, "GET /example HTTP/1.0\r\n\r\n")
|
||||||
scanner := bufio.NewScanner(c)
|
scanner := bufio.NewScanner(c)
|
||||||
var response string
|
var responseBuilder strings.Builder
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
response += scanner.Text()
|
responseBuilder.WriteString(scanner.Text())
|
||||||
}
|
}
|
||||||
|
response := responseBuilder.String()
|
||||||
assert.Contains(t, response, "HTTP/1.0 200", "should get a 200")
|
assert.Contains(t, response, "HTTP/1.0 200", "should get a 200")
|
||||||
assert.Contains(t, response, "it worked", "resp body should match")
|
assert.Contains(t, response, "it worked", "resp body should match")
|
||||||
}
|
}
|
||||||
@ -354,10 +357,11 @@ func TestListener(t *testing.T) {
|
|||||||
|
|
||||||
fmt.Fprintf(c, "GET /example HTTP/1.0\r\n\r\n")
|
fmt.Fprintf(c, "GET /example HTTP/1.0\r\n\r\n")
|
||||||
scanner := bufio.NewScanner(c)
|
scanner := bufio.NewScanner(c)
|
||||||
var response string
|
var responseBuilder strings.Builder
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
response += scanner.Text()
|
responseBuilder.WriteString(scanner.Text())
|
||||||
}
|
}
|
||||||
|
response := responseBuilder.String()
|
||||||
assert.Contains(t, response, "HTTP/1.0 200", "should get a 200")
|
assert.Contains(t, response, "HTTP/1.0 200", "should get a 200")
|
||||||
assert.Contains(t, response, "it worked", "resp body should match")
|
assert.Contains(t, response, "it worked", "resp body should match")
|
||||||
}
|
}
|
||||||
|
|||||||
99
gin_test.go
99
gin_test.go
@ -913,3 +913,102 @@ func TestMethodNotAllowedNoRoute(t *testing.T) {
|
|||||||
assert.NotPanics(t, func() { g.ServeHTTP(resp, req) })
|
assert.NotPanics(t, func() { g.ServeHTTP(resp, req) })
|
||||||
assert.Equal(t, http.StatusNotFound, resp.Code)
|
assert.Equal(t, http.StatusNotFound, resp.Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test the fix for https://github.com/gin-gonic/gin/pull/4415
|
||||||
|
func TestLiteralColonWithRun(t *testing.T) {
|
||||||
|
SetMode(TestMode)
|
||||||
|
router := New()
|
||||||
|
|
||||||
|
router.GET(`/test\:action`, func(c *Context) {
|
||||||
|
c.JSON(http.StatusOK, H{"path": "literal_colon"})
|
||||||
|
})
|
||||||
|
|
||||||
|
router.updateRouteTrees()
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
req, _ := http.NewRequest(http.MethodGet, "/test:action", nil)
|
||||||
|
router.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Contains(t, w.Body.String(), "literal_colon")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLiteralColonWithDirectServeHTTP(t *testing.T) {
|
||||||
|
SetMode(TestMode)
|
||||||
|
router := New()
|
||||||
|
|
||||||
|
router.GET(`/test\:action`, func(c *Context) {
|
||||||
|
c.JSON(http.StatusOK, H{"path": "literal_colon"})
|
||||||
|
})
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
req, _ := http.NewRequest(http.MethodGet, "/test:action", nil)
|
||||||
|
router.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Contains(t, w.Body.String(), "literal_colon")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLiteralColonWithHandler(t *testing.T) {
|
||||||
|
SetMode(TestMode)
|
||||||
|
router := New()
|
||||||
|
|
||||||
|
router.GET(`/test\:action`, func(c *Context) {
|
||||||
|
c.JSON(http.StatusOK, H{"path": "literal_colon"})
|
||||||
|
})
|
||||||
|
|
||||||
|
handler := router.Handler()
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
req, _ := http.NewRequest(http.MethodGet, "/test:action", nil)
|
||||||
|
handler.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Contains(t, w.Body.String(), "literal_colon")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLiteralColonWithHTTPServer(t *testing.T) {
|
||||||
|
SetMode(TestMode)
|
||||||
|
router := New()
|
||||||
|
|
||||||
|
router.GET(`/test\:action`, func(c *Context) {
|
||||||
|
c.JSON(http.StatusOK, H{"path": "literal_colon"})
|
||||||
|
})
|
||||||
|
|
||||||
|
router.GET("/test/:param", func(c *Context) {
|
||||||
|
c.JSON(http.StatusOK, H{"param": c.Param("param")})
|
||||||
|
})
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
req, _ := http.NewRequest(http.MethodGet, "/test:action", nil)
|
||||||
|
router.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Contains(t, w.Body.String(), "literal_colon")
|
||||||
|
|
||||||
|
w2 := httptest.NewRecorder()
|
||||||
|
req2, _ := http.NewRequest(http.MethodGet, "/test/foo", nil)
|
||||||
|
router.ServeHTTP(w2, req2)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w2.Code)
|
||||||
|
assert.Contains(t, w2.Body.String(), "foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that updateRouteTrees is called only once
|
||||||
|
func TestUpdateRouteTreesCalledOnce(t *testing.T) {
|
||||||
|
SetMode(TestMode)
|
||||||
|
router := New()
|
||||||
|
|
||||||
|
router.GET(`/test\:action`, func(c *Context) {
|
||||||
|
c.String(http.StatusOK, "ok")
|
||||||
|
})
|
||||||
|
|
||||||
|
for range 5 {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
req, _ := http.NewRequest(http.MethodGet, "/test:action", nil)
|
||||||
|
router.ServeHTTP(w, req)
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, "ok", w.Body.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
14
go.mod
14
go.mod
@ -12,10 +12,10 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20
|
github.com/mattn/go-isatty v0.0.20
|
||||||
github.com/modern-go/reflect2 v1.0.2
|
github.com/modern-go/reflect2 v1.0.2
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4
|
github.com/pelletier/go-toml/v2 v2.2.4
|
||||||
github.com/quic-go/quic-go v0.55.0
|
github.com/quic-go/quic-go v0.56.0
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/ugorji/go/codec v1.3.1
|
github.com/ugorji/go/codec v1.3.1
|
||||||
golang.org/x/net v0.46.0
|
golang.org/x/net v0.47.0
|
||||||
google.golang.org/protobuf v1.36.10
|
google.golang.org/protobuf v1.36.10
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -28,17 +28,15 @@ require (
|
|||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||||
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/quic-go/qpack v0.5.1 // indirect
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
golang.org/x/arch v0.20.0 // indirect
|
golang.org/x/arch v0.20.0 // indirect
|
||||||
golang.org/x/crypto v0.43.0 // indirect
|
golang.org/x/crypto v0.44.0 // indirect
|
||||||
golang.org/x/mod v0.28.0 // indirect
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
golang.org/x/sync v0.17.0 // indirect
|
golang.org/x/text v0.31.0 // indirect
|
||||||
golang.org/x/sys v0.37.0 // indirect
|
|
||||||
golang.org/x/text v0.30.0 // indirect
|
|
||||||
golang.org/x/tools v0.37.0 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
38
go.sum
38
go.sum
@ -6,6 +6,7 @@ github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2N
|
|||||||
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||||
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@ -32,6 +33,10 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
|
|||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
@ -46,8 +51,10 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk=
|
github.com/quic-go/quic-go v0.56.0 h1:q/TW+OLismmXAehgFLczhCDTYB3bFmua4D9lsNBWxvY=
|
||||||
github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U=
|
github.com/quic-go/quic-go v0.56.0/go.mod h1:9gx5KsFQtw2oZ6GZTyh+7YEvOxWCL9WZAepnHxgAo6c=
|
||||||
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
@ -67,25 +74,22 @@ go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
|||||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||||
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
|
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
|
||||||
golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||||
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
|
||||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
|
||||||
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
|
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||||
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
|
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||||
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
|
||||||
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
|
||||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
|
||||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||||
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
|
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
@ -94,7 +94,7 @@ func TestPathCleanMallocs(t *testing.T) {
|
|||||||
func BenchmarkPathClean(b *testing.B) {
|
func BenchmarkPathClean(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for b.Loop() {
|
||||||
for _, test := range cleanTests {
|
for _, test := range cleanTests {
|
||||||
cleanPath(test.path)
|
cleanPath(test.path)
|
||||||
}
|
}
|
||||||
@ -134,10 +134,10 @@ func TestPathCleanLong(t *testing.T) {
|
|||||||
|
|
||||||
func BenchmarkPathCleanLong(b *testing.B) {
|
func BenchmarkPathCleanLong(b *testing.B) {
|
||||||
cleanTests := genLongPaths()
|
cleanTests := genLongPaths()
|
||||||
b.ResetTimer()
|
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for b.Loop() {
|
||||||
for _, test := range cleanTests {
|
for _, test := range cleanTests {
|
||||||
cleanPath(test.path)
|
cleanPath(test.path)
|
||||||
}
|
}
|
||||||
|
|||||||
13
tree.go
13
tree.go
@ -5,6 +5,7 @@
|
|||||||
package gin
|
package gin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
@ -77,14 +78,22 @@ func (n *node) addChild(child *node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// safeUint16 converts int to uint16 safely, capping at math.MaxUint16
|
||||||
|
func safeUint16(n int) uint16 {
|
||||||
|
if n > math.MaxUint16 {
|
||||||
|
return math.MaxUint16
|
||||||
|
}
|
||||||
|
return uint16(n)
|
||||||
|
}
|
||||||
|
|
||||||
func countParams(path string) uint16 {
|
func countParams(path string) uint16 {
|
||||||
colons := strings.Count(path, ":")
|
colons := strings.Count(path, ":")
|
||||||
stars := strings.Count(path, "*")
|
stars := strings.Count(path, "*")
|
||||||
return uint16(colons + stars)
|
return safeUint16(colons + stars)
|
||||||
}
|
}
|
||||||
|
|
||||||
func countSections(path string) uint16 {
|
func countSections(path string) uint16 {
|
||||||
return uint16(strings.Count(path, "/"))
|
return safeUint16(strings.Count(path, "/"))
|
||||||
}
|
}
|
||||||
|
|
||||||
type nodeType uint8
|
type nodeType uint8
|
||||||
|
|||||||
@ -19,7 +19,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkParseAccept(b *testing.B) {
|
func BenchmarkParseAccept(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for b.Loop() {
|
||||||
parseAccept("text/html , application/xhtml+xml,application/xml;q=0.9, */* ;q=0.8")
|
parseAccept("text/html , application/xhtml+xml,application/xml;q=0.9, */* ;q=0.8")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user