mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-15 21:06:39 +08:00
Merge branch 'master' into Dadudidas-patch-1
This commit is contained in:
commit
56ef453da2
4
.github/ISSUE_TEMPLATE.md
vendored
4
.github/ISSUE_TEMPLATE.md
vendored
@ -30,7 +30,7 @@ func main() {
|
||||
|
||||
<!-- Your expectation result of 'curl' command, like -->
|
||||
```
|
||||
$ curl http://localhost:8201/hello/world
|
||||
$ curl http://localhost:9000/hello/world
|
||||
Hello world
|
||||
```
|
||||
|
||||
@ -38,7 +38,7 @@ Hello world
|
||||
|
||||
<!-- Actual result showing the problem -->
|
||||
```
|
||||
$ curl -i http://localhost:8201/hello/world
|
||||
$ curl -i http://localhost:9000/hello/world
|
||||
<YOUR RESULT>
|
||||
```
|
||||
|
||||
|
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
10
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Custom issue template
|
||||
about: Describe this issue template's purpose here.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
14
.github/workflows/codeql.yml
vendored
14
.github/workflows/codeql.yml
vendored
@ -7,12 +7,12 @@ name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [master]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
branches: [master]
|
||||
schedule:
|
||||
- cron: '0 17 * * 5'
|
||||
- cron: "0 17 * * 5"
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
@ -29,15 +29,15 @@ jobs:
|
||||
# Override automatic language detection by changing the below list
|
||||
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
|
||||
# TODO: Enable for javascript later
|
||||
language: [ 'go']
|
||||
language: ["go"]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@ -46,4 +46,4 @@ jobs:
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
23
.github/workflows/gin.yml
vendored
23
.github/workflows/gin.yml
vendored
@ -16,23 +16,23 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '^1.18'
|
||||
go-version: "^1.18"
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3.4.0
|
||||
uses: golangci/golangci-lint-action@v3.7.0
|
||||
with:
|
||||
version: v1.52.2
|
||||
version: v1.55.2
|
||||
args: --verbose
|
||||
test:
|
||||
needs: lint
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
go: ['1.18', '1.19', '1.20']
|
||||
test-tags: ['', '-tags nomsgpack', '-tags "sonic avx"', '-tags go_json']
|
||||
go: ["1.18", "1.19", "1.20", "1.21"]
|
||||
test-tags: ["", "-tags nomsgpack", '-tags "sonic avx"', "-tags go_json"]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
go-build: ~/.cache/go-build
|
||||
@ -46,16 +46,17 @@ jobs:
|
||||
GOPROXY: https://proxy.golang.org
|
||||
steps:
|
||||
- name: Set up Go ${{ matrix.go }}
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
cache: false
|
||||
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
${{ matrix.go-build }}
|
||||
@ -73,5 +74,5 @@ jobs:
|
||||
flags: ${{ matrix.os }},go-${{ matrix.go }},${{ matrix.test-tags }}
|
||||
|
||||
- name: Format
|
||||
if: matrix.go-version == '1.20.x'
|
||||
if: matrix.go-version == '1.21.x'
|
||||
run: diff -u <(echo -n) <(gofmt -d .)
|
||||
|
15
.github/workflows/goreleaser.yml
vendored
15
.github/workflows/goreleaser.yml
vendored
@ -3,7 +3,7 @@ name: Goreleaser
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
- "*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@ -12,23 +12,22 @@ jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.20
|
||||
go-version: "^1"
|
||||
-
|
||||
name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
# either 'goreleaser' (default) or 'goreleaser-pro'
|
||||
distribution: goreleaser
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
@ -3,7 +3,6 @@ run:
|
||||
linters:
|
||||
enable:
|
||||
- asciicheck
|
||||
- depguard
|
||||
- dogsled
|
||||
- durationcheck
|
||||
- errcheck
|
||||
|
21
CHANGELOG.md
21
CHANGELOG.md
@ -1,5 +1,26 @@
|
||||
# Gin ChangeLog
|
||||
|
||||
## Gin v1.9.1
|
||||
|
||||
### BUG FIXES
|
||||
|
||||
* fix Request.Context() checks [#3512](https://github.com/gin-gonic/gin/pull/3512)
|
||||
|
||||
### SECURITY
|
||||
|
||||
* fix lack of escaping of filename in Content-Disposition [#3556](https://github.com/gin-gonic/gin/pull/3556)
|
||||
|
||||
### ENHANCEMENTS
|
||||
|
||||
* refactor: use bytes.ReplaceAll directly [#3455](https://github.com/gin-gonic/gin/pull/3455)
|
||||
* convert strings and slices using the officially recommended way [#3344](https://github.com/gin-gonic/gin/pull/3344)
|
||||
* improve render code coverage [#3525](https://github.com/gin-gonic/gin/pull/3525)
|
||||
|
||||
### DOCS
|
||||
|
||||
* docs: changed documentation link for trusted proxies [#3575](https://github.com/gin-gonic/gin/pull/3575)
|
||||
* chore: improve linting, testing, and GitHub Actions setup [#3583](https://github.com/gin-gonic/gin/pull/3583)
|
||||
|
||||
## Gin v1.9.0
|
||||
|
||||
### BREAK CHANGES
|
||||
|
@ -7,6 +7,7 @@ package binding
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -235,10 +236,17 @@ func setWithProperType(val string, value reflect.Value, field reflect.StructFiel
|
||||
switch value.Interface().(type) {
|
||||
case time.Time:
|
||||
return setTimeField(val, field, value)
|
||||
case multipart.FileHeader:
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
|
||||
case reflect.Map:
|
||||
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
|
||||
case reflect.Ptr:
|
||||
if !value.Elem().IsValid() {
|
||||
value.Set(reflect.New(value.Type().Elem()))
|
||||
}
|
||||
return setWithProperType(val, value.Elem(), field)
|
||||
default:
|
||||
return errUnknownType
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"mime/multipart"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
@ -43,6 +44,7 @@ func TestMappingBaseTypes(t *testing.T) {
|
||||
{"zero value", struct{ F uint }{}, "", uint(0)},
|
||||
{"zero value", struct{ F bool }{}, "", false},
|
||||
{"zero value", struct{ F float32 }{}, "", float32(0)},
|
||||
{"file value", struct{ F *multipart.FileHeader }{}, "", &multipart.FileHeader{}},
|
||||
} {
|
||||
tp := reflect.TypeOf(tt.value)
|
||||
testName := tt.name + ":" + tp.Field(0).Type.String()
|
||||
@ -269,6 +271,39 @@ func TestMappingStructField(t *testing.T) {
|
||||
assert.Equal(t, 9, s.J.I)
|
||||
}
|
||||
|
||||
func TestMappingPtrField(t *testing.T) {
|
||||
type ptrStruct struct {
|
||||
Key int64 `json:"key"`
|
||||
}
|
||||
|
||||
type ptrRequest struct {
|
||||
Items []*ptrStruct `json:"items" form:"items"`
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
// With 0 items.
|
||||
var req0 ptrRequest
|
||||
err = mappingByPtr(&req0, formSource{}, "form")
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, req0.Items)
|
||||
|
||||
// With 1 item.
|
||||
var req1 ptrRequest
|
||||
err = mappingByPtr(&req1, formSource{"items": {`{"key": 1}`}}, "form")
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, req1.Items, 1)
|
||||
assert.EqualValues(t, 1, req1.Items[0].Key)
|
||||
|
||||
// With 2 items.
|
||||
var req2 ptrRequest
|
||||
err = mappingByPtr(&req2, formSource{"items": {`{"key": 1}`, `{"key": 2}`}}, "form")
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, req2.Items, 2)
|
||||
assert.EqualValues(t, 1, req2.Items[0].Key)
|
||||
assert.EqualValues(t, 2, req2.Items[1].Key)
|
||||
}
|
||||
|
||||
func TestMappingMapField(t *testing.T) {
|
||||
var s struct {
|
||||
M map[string]int
|
||||
|
23
context.go
23
context.go
@ -1052,11 +1052,17 @@ func (c *Context) FileFromFS(filepath string, fs http.FileSystem) {
|
||||
http.FileServer(fs).ServeHTTP(c.Writer, c.Request)
|
||||
}
|
||||
|
||||
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
|
||||
|
||||
func escapeQuotes(s string) string {
|
||||
return quoteEscaper.Replace(s)
|
||||
}
|
||||
|
||||
// FileAttachment writes the specified file into the body stream in an efficient way
|
||||
// On the client side, the file will typically be downloaded with the given filename
|
||||
func (c *Context) FileAttachment(filepath, filename string) {
|
||||
if isASCII(filename) {
|
||||
c.Writer.Header().Set("Content-Disposition", `attachment; filename="`+filename+`"`)
|
||||
c.Writer.Header().Set("Content-Disposition", `attachment; filename="`+escapeQuotes(filename)+`"`)
|
||||
} else {
|
||||
c.Writer.Header().Set("Content-Disposition", `attachment; filename*=UTF-8''`+url.QueryEscape(filename))
|
||||
}
|
||||
@ -1174,9 +1180,16 @@ func (c *Context) SetAccepted(formats ...string) {
|
||||
/***** GOLANG.ORG/X/NET/CONTEXT *****/
|
||||
/************************************/
|
||||
|
||||
// hasRequestContext returns whether c.Request has Context and fallback.
|
||||
func (c *Context) hasRequestContext() bool {
|
||||
hasFallback := c.engine != nil && c.engine.ContextWithFallback
|
||||
hasRequestContext := c.Request != nil && c.Request.Context() != nil
|
||||
return hasFallback && hasRequestContext
|
||||
}
|
||||
|
||||
// Deadline returns that there is no deadline (ok==false) when c.Request has no Context.
|
||||
func (c *Context) Deadline() (deadline time.Time, ok bool) {
|
||||
if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil {
|
||||
if !c.hasRequestContext() {
|
||||
return
|
||||
}
|
||||
return c.Request.Context().Deadline()
|
||||
@ -1184,7 +1197,7 @@ func (c *Context) Deadline() (deadline time.Time, ok bool) {
|
||||
|
||||
// Done returns nil (chan which will wait forever) when c.Request has no Context.
|
||||
func (c *Context) Done() <-chan struct{} {
|
||||
if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil {
|
||||
if !c.hasRequestContext() {
|
||||
return nil
|
||||
}
|
||||
return c.Request.Context().Done()
|
||||
@ -1192,7 +1205,7 @@ func (c *Context) Done() <-chan struct{} {
|
||||
|
||||
// Err returns nil when c.Request has no Context.
|
||||
func (c *Context) Err() error {
|
||||
if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil {
|
||||
if !c.hasRequestContext() {
|
||||
return nil
|
||||
}
|
||||
return c.Request.Context().Err()
|
||||
@ -1213,7 +1226,7 @@ func (c *Context) Value(key any) any {
|
||||
return val
|
||||
}
|
||||
}
|
||||
if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil {
|
||||
if !c.hasRequestContext() {
|
||||
return nil
|
||||
}
|
||||
return c.Request.Context().Value(key)
|
||||
|
@ -1032,6 +1032,20 @@ func TestContextRenderAttachment(t *testing.T) {
|
||||
assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", newFilename), w.Header().Get("Content-Disposition"))
|
||||
}
|
||||
|
||||
func TestContextRenderAndEscapeAttachment(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := CreateTestContext(w)
|
||||
maliciousFilename := "tampering_field.sh\"; \\\"; dummy=.go"
|
||||
actualEscapedResponseFilename := "tampering_field.sh\\\"; \\\\\\\"; dummy=.go"
|
||||
|
||||
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||
c.FileAttachment("./gin.go", maliciousFilename)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Contains(t, w.Body.String(), "func New() *Engine {")
|
||||
assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", actualEscapedResponseFilename), w.Header().Get("Content-Disposition"))
|
||||
}
|
||||
|
||||
func TestContextRenderUTF8Attachment(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := CreateTestContext(w)
|
||||
@ -2162,6 +2176,24 @@ func TestRemoteIPFail(t *testing.T) {
|
||||
assert.False(t, trust)
|
||||
}
|
||||
|
||||
func TestHasRequestContext(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
assert.False(t, c.hasRequestContext(), "no request, no fallback")
|
||||
c.engine.ContextWithFallback = true
|
||||
assert.False(t, c.hasRequestContext(), "no request, has fallback")
|
||||
c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
|
||||
assert.True(t, c.hasRequestContext(), "has request, has fallback")
|
||||
c.Request, _ = http.NewRequestWithContext(nil, "", "", nil) //nolint:staticcheck
|
||||
assert.False(t, c.hasRequestContext(), "has request with nil ctx, has fallback")
|
||||
c.engine.ContextWithFallback = false
|
||||
assert.False(t, c.hasRequestContext(), "has request, no fallback")
|
||||
|
||||
c = &Context{}
|
||||
assert.False(t, c.hasRequestContext(), "no request, no engine")
|
||||
c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
|
||||
assert.False(t, c.hasRequestContext(), "has request, no engine")
|
||||
}
|
||||
|
||||
func TestContextWithFallbackDeadlineFromRequestContext(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
// enable ContextWithFallback feature flag
|
||||
|
38
docs/doc.md
38
docs/doc.md
@ -508,6 +508,44 @@ Sample Output
|
||||
::1 - [Fri, 07 Dec 2018 17:04:38 JST] "GET /ping HTTP/1.1 200 122.767µs "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36" "
|
||||
```
|
||||
|
||||
### Skip logging
|
||||
|
||||
```go
|
||||
func main() {
|
||||
router := gin.New()
|
||||
|
||||
// skip logging for desired paths by setting SkipPaths in LoggerConfig
|
||||
loggerConfig := gin.LoggerConfig{SkipPaths: []string{"/metrics"}}
|
||||
|
||||
// skip logging based on your logic by setting Skip func in LoggerConfig
|
||||
loggerConfig.Skip = func(c *gin.Context) bool {
|
||||
// as an example skip non server side errors
|
||||
return c.Writer.Status() < http.StatusInternalServerError
|
||||
}
|
||||
|
||||
engine.Use(gin.LoggerWithConfig(loggerConfig))
|
||||
router.Use(gin.Recovery())
|
||||
|
||||
// skipped
|
||||
router.GET("/metrics", func(c *gin.Context) {
|
||||
c.Status(http.StatusNotImplemented)
|
||||
})
|
||||
|
||||
// skipped
|
||||
router.GET("/ping", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "pong")
|
||||
})
|
||||
|
||||
// not skipped
|
||||
router.GET("/data", func(c *gin.Context) {
|
||||
c.Status(http.StatusNotImplemented)
|
||||
})
|
||||
|
||||
router.Run(":8080")
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Controlling Log output coloring
|
||||
|
||||
By default, logs output on console should be colorized depending on the detected TTY.
|
||||
|
1
gin.go
1
gin.go
@ -334,7 +334,6 @@ func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
|
||||
}
|
||||
root.addRoute(path, handlers)
|
||||
|
||||
// Update maxParams
|
||||
if paramsCount := countParams(path); paramsCount > engine.maxParams {
|
||||
engine.maxParams = paramsCount
|
||||
}
|
||||
|
32
go.mod
32
go.mod
@ -3,33 +3,35 @@ module github.com/gin-gonic/gin
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.8.8
|
||||
github.com/bytedance/sonic v1.10.2
|
||||
github.com/gin-contrib/sse v0.1.0
|
||||
github.com/go-playground/validator/v10 v10.12.0
|
||||
github.com/go-playground/validator/v10 v10.17.0
|
||||
github.com/goccy/go-json v0.10.2
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/mattn/go-isatty v0.0.18
|
||||
github.com/pelletier/go-toml/v2 v2.0.7
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/ugorji/go/codec v1.2.11
|
||||
golang.org/x/net v0.9.0
|
||||
google.golang.org/protobuf v1.30.0
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/pelletier/go-toml/v2 v2.1.1
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/ugorji/go/codec v1.2.12
|
||||
golang.org/x/net v0.20.0
|
||||
google.golang.org/protobuf v1.32.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/leodido/go-urn v1.2.3 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||
github.com/leodido/go-urn v1.3.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.8.0 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/arch v0.7.0 // indirect
|
||||
golang.org/x/crypto v0.18.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
)
|
||||
|
73
go.sum
73
go.sum
@ -1,12 +1,19 @@
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.8.8 h1:Kj4AYbZSeENfyXicsYppYKO0K2YWab+i2UTSY7Ukz9Q=
|
||||
github.com/bytedance/sonic v1.8.8/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
||||
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
|
||||
github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
|
||||
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
|
||||
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
@ -14,30 +21,29 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI=
|
||||
github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA=
|
||||
github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74=
|
||||
github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA=
|
||||
github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/leodido/go-urn v1.3.0 h1:jX8FDLfW4ThVXctBNZ+3cIWnCSnrACDV73r76dy0aQQ=
|
||||
github.com/leodido/go-urn v1.3.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/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us=
|
||||
github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@ -48,33 +54,32 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
|
||||
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
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/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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
63
logger.go
63
logger.go
@ -47,8 +47,15 @@ type LoggerConfig struct {
|
||||
// SkipPaths is an url path array which logs are not written.
|
||||
// Optional.
|
||||
SkipPaths []string
|
||||
|
||||
// Skip is a Skipper that indicates which logs should not be written.
|
||||
// Optional.
|
||||
Skip Skipper
|
||||
}
|
||||
|
||||
// Skipper is a function to skip logs based on provided Context
|
||||
type Skipper func(c *Context) bool
|
||||
|
||||
// LogFormatter gives the signature of the formatter function passed to LoggerWithFormatter
|
||||
type LogFormatter func(params LogFormatterParams) string
|
||||
|
||||
@ -83,6 +90,8 @@ func (p *LogFormatterParams) StatusCodeColor() string {
|
||||
code := p.StatusCode
|
||||
|
||||
switch {
|
||||
case code >= http.StatusContinue && code < http.StatusOK:
|
||||
return white
|
||||
case code >= http.StatusOK && code < http.StatusMultipleChoices:
|
||||
return green
|
||||
case code >= http.StatusMultipleChoices && code < http.StatusBadRequest:
|
||||
@ -239,32 +248,34 @@ func LoggerWithConfig(conf LoggerConfig) HandlerFunc {
|
||||
// Process request
|
||||
c.Next()
|
||||
|
||||
// Log only when path is not being skipped
|
||||
if _, ok := skip[path]; !ok {
|
||||
param := LogFormatterParams{
|
||||
Request: c.Request,
|
||||
isTerm: isTerm,
|
||||
Keys: c.Keys,
|
||||
}
|
||||
|
||||
// Stop timer
|
||||
param.TimeStamp = time.Now()
|
||||
param.Latency = param.TimeStamp.Sub(start)
|
||||
|
||||
param.ClientIP = c.ClientIP()
|
||||
param.Method = c.Request.Method
|
||||
param.StatusCode = c.Writer.Status()
|
||||
param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String()
|
||||
|
||||
param.BodySize = c.Writer.Size()
|
||||
|
||||
if raw != "" {
|
||||
path = path + "?" + raw
|
||||
}
|
||||
|
||||
param.Path = path
|
||||
|
||||
fmt.Fprint(out, formatter(param))
|
||||
// Log only when it is not being skipped
|
||||
if _, ok := skip[path]; ok || (conf.Skip != nil && conf.Skip(c)) {
|
||||
return
|
||||
}
|
||||
|
||||
param := LogFormatterParams{
|
||||
Request: c.Request,
|
||||
isTerm: isTerm,
|
||||
Keys: c.Keys,
|
||||
}
|
||||
|
||||
// Stop timer
|
||||
param.TimeStamp = time.Now()
|
||||
param.Latency = param.TimeStamp.Sub(start)
|
||||
|
||||
param.ClientIP = c.ClientIP()
|
||||
param.Method = c.Request.Method
|
||||
param.StatusCode = c.Writer.Status()
|
||||
param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String()
|
||||
|
||||
param.BodySize = c.Writer.Size()
|
||||
|
||||
if raw != "" {
|
||||
path = path + "?" + raw
|
||||
}
|
||||
|
||||
param.Path = path
|
||||
|
||||
fmt.Fprint(out, formatter(param))
|
||||
}
|
||||
}
|
||||
|
@ -310,6 +310,7 @@ func TestColorForStatus(t *testing.T) {
|
||||
return p.StatusCodeColor()
|
||||
}
|
||||
|
||||
assert.Equal(t, white, colorForStatus(http.StatusContinue), "1xx should be white")
|
||||
assert.Equal(t, green, colorForStatus(http.StatusOK), "2xx should be green")
|
||||
assert.Equal(t, white, colorForStatus(http.StatusMovedPermanently), "3xx should be white")
|
||||
assert.Equal(t, yellow, colorForStatus(http.StatusNotFound), "4xx should be yellow")
|
||||
@ -414,6 +415,26 @@ func TestLoggerWithConfigSkippingPaths(t *testing.T) {
|
||||
assert.Contains(t, buffer.String(), "")
|
||||
}
|
||||
|
||||
func TestLoggerWithConfigSkipper(t *testing.T) {
|
||||
buffer := new(strings.Builder)
|
||||
router := New()
|
||||
router.Use(LoggerWithConfig(LoggerConfig{
|
||||
Output: buffer,
|
||||
Skip: func(c *Context) bool {
|
||||
return c.Writer.Status() == http.StatusNoContent
|
||||
},
|
||||
}))
|
||||
router.GET("/logged", func(c *Context) { c.Status(http.StatusOK) })
|
||||
router.GET("/skipped", func(c *Context) { c.Status(http.StatusNoContent) })
|
||||
|
||||
PerformRequest(router, "GET", "/logged")
|
||||
assert.Contains(t, buffer.String(), "200")
|
||||
|
||||
buffer.Reset()
|
||||
PerformRequest(router, "GET", "/skipped")
|
||||
assert.Contains(t, buffer.String(), "")
|
||||
}
|
||||
|
||||
func TestDisableConsoleColor(t *testing.T) {
|
||||
New()
|
||||
assert.Equal(t, autoColor, consoleColorMode)
|
||||
|
@ -578,3 +578,16 @@ func TestRenderReaderNoContentLength(t *testing.T) {
|
||||
assert.Equal(t, headers["Content-Disposition"], w.Header().Get("Content-Disposition"))
|
||||
assert.Equal(t, headers["x-request-id"], w.Header().Get("x-request-id"))
|
||||
}
|
||||
|
||||
func TestRenderWriteError(t *testing.T) {
|
||||
data := []interface{}{"value1", "value2"}
|
||||
prefix := "my-prefix:"
|
||||
r := SecureJSON{Data: data, Prefix: prefix}
|
||||
ew := &errorWriter{
|
||||
bufString: prefix,
|
||||
ResponseRecorder: httptest.NewRecorder(),
|
||||
}
|
||||
err := r.Render(ew)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, `write "my-prefix:" error`, err.Error())
|
||||
}
|
||||
|
@ -156,3 +156,33 @@ func TestResponseWriterStatusCode(t *testing.T) {
|
||||
// status must be 200 although we tried to change it
|
||||
assert.Equal(t, http.StatusOK, w.Status())
|
||||
}
|
||||
|
||||
// mockPusherResponseWriter is an http.ResponseWriter that implements http.Pusher.
|
||||
type mockPusherResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
}
|
||||
|
||||
func (m *mockPusherResponseWriter) Push(target string, opts *http.PushOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// nonPusherResponseWriter is an http.ResponseWriter that does not implement http.Pusher.
|
||||
type nonPusherResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
}
|
||||
|
||||
func TestPusherWithPusher(t *testing.T) {
|
||||
rw := &mockPusherResponseWriter{}
|
||||
w := &responseWriter{ResponseWriter: rw}
|
||||
|
||||
pusher := w.Pusher()
|
||||
assert.NotNil(t, pusher, "Expected pusher to be non-nil")
|
||||
}
|
||||
|
||||
func TestPusherWithoutPusher(t *testing.T) {
|
||||
rw := &nonPusherResponseWriter{}
|
||||
w := &responseWriter{ResponseWriter: rw}
|
||||
|
||||
pusher := w.Pusher()
|
||||
assert.Nil(t, pusher, "Expected pusher to be nil")
|
||||
}
|
||||
|
@ -337,6 +337,45 @@ func TestRouteParamsByNameWithExtraSlash(t *testing.T) {
|
||||
assert.Equal(t, "/is/super/great", wild)
|
||||
}
|
||||
|
||||
// TestRouteParamsNotEmpty tests that context parameters will be set
|
||||
// even if a route with params/wildcards is registered after the context
|
||||
// initialisation (which happened in a previous requets).
|
||||
func TestRouteParamsNotEmpty(t *testing.T) {
|
||||
name := ""
|
||||
lastName := ""
|
||||
wild := ""
|
||||
router := New()
|
||||
|
||||
w := PerformRequest(router, http.MethodGet, "/test/john/smith/is/super/great")
|
||||
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
|
||||
router.GET("/test/:name/:last_name/*wild", func(c *Context) {
|
||||
name = c.Params.ByName("name")
|
||||
lastName = c.Params.ByName("last_name")
|
||||
var ok bool
|
||||
wild, ok = c.Params.Get("wild")
|
||||
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, name, c.Param("name"))
|
||||
assert.Equal(t, lastName, c.Param("last_name"))
|
||||
|
||||
assert.Empty(t, c.Param("wtf"))
|
||||
assert.Empty(t, c.Params.ByName("wtf"))
|
||||
|
||||
wtf, ok := c.Params.Get("wtf")
|
||||
assert.Empty(t, wtf)
|
||||
assert.False(t, ok)
|
||||
})
|
||||
|
||||
w = PerformRequest(router, http.MethodGet, "/test/john/smith/is/super/great")
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, "john", name)
|
||||
assert.Equal(t, "smith", lastName)
|
||||
assert.Equal(t, "/is/super/great", wild)
|
||||
}
|
||||
|
||||
// TestHandleStaticFile - ensure the static file handles properly
|
||||
func TestRouteStaticFile(t *testing.T) {
|
||||
// SETUP file
|
||||
|
43
tree.go
43
tree.go
@ -351,7 +351,10 @@ func (n *node) insertChild(path string, fullPath string, handlers HandlersChain)
|
||||
}
|
||||
|
||||
if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
|
||||
pathSeg := strings.SplitN(n.children[0].path, "/", 2)[0]
|
||||
pathSeg := ""
|
||||
if len(n.children) != 0 {
|
||||
pathSeg = strings.SplitN(n.children[0].path, "/", 2)[0]
|
||||
}
|
||||
panic("catch-all wildcard '" + path +
|
||||
"' in new path '" + fullPath +
|
||||
"' conflicts with existing path segment '" + pathSeg +
|
||||
@ -478,7 +481,7 @@ walk: // Outer loop for walking the tree
|
||||
// We can recommend to redirect to the same URL without a
|
||||
// trailing slash if a leaf exists for that path.
|
||||
value.tsr = path == "/" && n.handlers != nil
|
||||
return
|
||||
return value
|
||||
}
|
||||
|
||||
// Handle wildcard child, which is always at the end of the array
|
||||
@ -497,7 +500,14 @@ walk: // Outer loop for walking the tree
|
||||
}
|
||||
|
||||
// Save param value
|
||||
if params != nil && cap(*params) > 0 {
|
||||
if params != nil {
|
||||
// Preallocate capacity if necessary
|
||||
if cap(*params) < int(globalParamsCount) {
|
||||
newParams := make(Params, len(*params), globalParamsCount)
|
||||
copy(newParams, *params)
|
||||
*params = newParams
|
||||
}
|
||||
|
||||
if value.params == nil {
|
||||
value.params = params
|
||||
}
|
||||
@ -526,12 +536,12 @@ walk: // Outer loop for walking the tree
|
||||
|
||||
// ... but we can't
|
||||
value.tsr = len(path) == end+1
|
||||
return
|
||||
return value
|
||||
}
|
||||
|
||||
if value.handlers = n.handlers; value.handlers != nil {
|
||||
value.fullPath = n.fullPath
|
||||
return
|
||||
return value
|
||||
}
|
||||
if len(n.children) == 1 {
|
||||
// No handle found. Check if a handle for this path + a
|
||||
@ -539,11 +549,18 @@ walk: // Outer loop for walking the tree
|
||||
n = n.children[0]
|
||||
value.tsr = (n.path == "/" && n.handlers != nil) || (n.path == "" && n.indices == "/")
|
||||
}
|
||||
return
|
||||
return value
|
||||
|
||||
case catchAll:
|
||||
// Save param value
|
||||
if params != nil {
|
||||
// Preallocate capacity if necessary
|
||||
if cap(*params) < int(globalParamsCount) {
|
||||
newParams := make(Params, len(*params), globalParamsCount)
|
||||
copy(newParams, *params)
|
||||
*params = newParams
|
||||
}
|
||||
|
||||
if value.params == nil {
|
||||
value.params = params
|
||||
}
|
||||
@ -564,7 +581,7 @@ walk: // Outer loop for walking the tree
|
||||
|
||||
value.handlers = n.handlers
|
||||
value.fullPath = n.fullPath
|
||||
return
|
||||
return value
|
||||
|
||||
default:
|
||||
panic("invalid node type")
|
||||
@ -595,7 +612,7 @@ walk: // Outer loop for walking the tree
|
||||
// Check if this node has a handle registered.
|
||||
if value.handlers = n.handlers; value.handlers != nil {
|
||||
value.fullPath = n.fullPath
|
||||
return
|
||||
return value
|
||||
}
|
||||
|
||||
// If there is no handle for this route, but this route has a
|
||||
@ -603,12 +620,12 @@ walk: // Outer loop for walking the tree
|
||||
// additional trailing slash
|
||||
if path == "/" && n.wildChild && n.nType != root {
|
||||
value.tsr = true
|
||||
return
|
||||
return value
|
||||
}
|
||||
|
||||
if path == "/" && n.nType == static {
|
||||
value.tsr = true
|
||||
return
|
||||
return value
|
||||
}
|
||||
|
||||
// No handle found. Check if a handle for this path + a
|
||||
@ -618,11 +635,11 @@ walk: // Outer loop for walking the tree
|
||||
n = n.children[i]
|
||||
value.tsr = (len(n.path) == 1 && n.handlers != nil) ||
|
||||
(n.nType == catchAll && n.children[0].handlers != nil)
|
||||
return
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return value
|
||||
}
|
||||
|
||||
// Nothing found. We can recommend to redirect to the same URL with an
|
||||
@ -648,7 +665,7 @@ walk: // Outer loop for walking the tree
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
|
36
tree_test.go
36
tree_test.go
@ -417,6 +417,8 @@ func TestTreeWildcardConflict(t *testing.T) {
|
||||
{"/user_:name", false},
|
||||
{"/id:id", false},
|
||||
{"/id/:id", false},
|
||||
{"/static/*file", false},
|
||||
{"/static/", true},
|
||||
}
|
||||
testRoutes(t, routes)
|
||||
}
|
||||
@ -893,9 +895,9 @@ func TestTreeInvalidNodeType(t *testing.T) {
|
||||
|
||||
func TestTreeInvalidParamsType(t *testing.T) {
|
||||
tree := &node{}
|
||||
tree.wildChild = true
|
||||
tree.children = append(tree.children, &node{})
|
||||
tree.children[0].nType = 2
|
||||
// add a child with wildcard
|
||||
route := "/:path"
|
||||
tree.addRoute(route, fakeHandler(route))
|
||||
|
||||
// set invalid Params type
|
||||
params := make(Params, 0)
|
||||
@ -904,6 +906,34 @@ func TestTreeInvalidParamsType(t *testing.T) {
|
||||
tree.getValue("/test", ¶ms, getSkippedNodes(), false)
|
||||
}
|
||||
|
||||
func TestTreeExpandParamsCapacity(t *testing.T) {
|
||||
data := []struct {
|
||||
path string
|
||||
}{
|
||||
{"/:path"},
|
||||
{"/*path"},
|
||||
}
|
||||
|
||||
for _, item := range data {
|
||||
tree := &node{}
|
||||
tree.addRoute(item.path, fakeHandler(item.path))
|
||||
params := make(Params, 0)
|
||||
|
||||
value := tree.getValue("/test", ¶ms, getSkippedNodes(), false)
|
||||
|
||||
if value.params == nil {
|
||||
t.Errorf("Expected %s params to be set, but they weren't", item.path)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(*value.params) != 1 {
|
||||
t.Errorf("Wrong number of %s params: got %d, want %d",
|
||||
item.path, len(*value.params), 1)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeWildcardConflictEx(t *testing.T) {
|
||||
conflicts := [...]struct {
|
||||
route string
|
||||
|
@ -5,4 +5,4 @@
|
||||
package gin
|
||||
|
||||
// Version is the current gin framework's version.
|
||||
const Version = "v1.9.0"
|
||||
const Version = "v1.9.1"
|
||||
|
Loading…
x
Reference in New Issue
Block a user