mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-16 13:22:09 +08:00
Merge branch 'gin-gonic:master' into fix-missing-params
This commit is contained in:
commit
bb52068591
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 -->
|
<!-- Your expectation result of 'curl' command, like -->
|
||||||
```
|
```
|
||||||
$ curl http://localhost:8201/hello/world
|
$ curl http://localhost:9000/hello/world
|
||||||
Hello world
|
Hello world
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ Hello world
|
|||||||
|
|
||||||
<!-- Actual result showing the problem -->
|
<!-- Actual result showing the problem -->
|
||||||
```
|
```
|
||||||
$ curl -i http://localhost:8201/hello/world
|
$ curl -i http://localhost:9000/hello/world
|
||||||
<YOUR RESULT>
|
<YOUR RESULT>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
|
32
.github/workflows/gin.yml
vendored
32
.github/workflows/gin.yml
vendored
@ -16,22 +16,22 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: '^1.16'
|
go-version: '^1.18'
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Setup golangci-lint
|
- name: Setup golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v3.4.0
|
uses: golangci/golangci-lint-action@v3.7.0
|
||||||
with:
|
with:
|
||||||
version: v1.48.0
|
version: v1.52.2
|
||||||
args: --verbose
|
args: --verbose
|
||||||
test:
|
test:
|
||||||
needs: lint
|
needs: lint
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest]
|
os: [ubuntu-latest, macos-latest]
|
||||||
go: ['1.16', '1.17', '1.18', '1.19', '1.20']
|
go: ['1.18', '1.19', '1.20']
|
||||||
test-tags: ['', '-tags nomsgpack', '-tags "sonic avx"', '-tags go_json']
|
test-tags: ['', '-tags nomsgpack', '-tags "sonic avx"', '-tags go_json']
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
@ -46,12 +46,12 @@ jobs:
|
|||||||
GOPROXY: https://proxy.golang.org
|
GOPROXY: https://proxy.golang.org
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go ${{ matrix.go }}
|
- name: Set up Go ${{ matrix.go }}
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go }}
|
go-version: ${{ matrix.go }}
|
||||||
|
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.ref }}
|
ref: ${{ github.ref }}
|
||||||
|
|
||||||
@ -73,19 +73,5 @@ jobs:
|
|||||||
flags: ${{ matrix.os }},go-${{ matrix.go }},${{ matrix.test-tags }}
|
flags: ${{ matrix.os }},go-${{ matrix.go }},${{ matrix.test-tags }}
|
||||||
|
|
||||||
- name: Format
|
- name: Format
|
||||||
if: matrix.go-version == '1.19.x'
|
if: matrix.go-version == '1.20.x'
|
||||||
run: diff -u <(echo -n) <(gofmt -d .)
|
run: diff -u <(echo -n) <(gofmt -d .)
|
||||||
notification-gitter:
|
|
||||||
needs: test
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Notification failure message
|
|
||||||
if: failure()
|
|
||||||
run: |
|
|
||||||
PR_OR_COMPARE="$(if [ "${{ github.event.pull_request }}" != "" ]; then echo "${{ github.event.pull_request.html_url }}"; else echo "${{ github.event.compare }}"; fi)"
|
|
||||||
curl -d message="GitHub Actions [$GITHUB_REPOSITORY]($PR_OR_COMPARE) ($GITHUB_REF) [normal]($GITHUB_API_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID) ($GITHUB_RUN_NUMBER)" -d level=error https://webhooks.gitter.im/e/7f95bf605c4d356372f4
|
|
||||||
- name: Notification success message
|
|
||||||
if: success()
|
|
||||||
run: |
|
|
||||||
PR_OR_COMPARE="$(if [ "${{ github.event.pull_request }}" != "" ]; then echo "${{ github.event.pull_request.html_url }}"; else echo "${{ github.event.compare }}"; fi)"
|
|
||||||
curl -d message="GitHub Actions [$GITHUB_REPOSITORY]($PR_OR_COMPARE) ($GITHUB_REF) [normal]($GITHUB_API_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID) ($GITHUB_RUN_NUMBER)" https://webhooks.gitter.im/e/7f95bf605c4d356372f4
|
|
||||||
|
8
.github/workflows/goreleaser.yml
vendored
8
.github/workflows/goreleaser.yml
vendored
@ -14,14 +14,14 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
-
|
-
|
||||||
name: Set up Go
|
name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: 1.17
|
go-version: 1.20
|
||||||
-
|
-
|
||||||
name: Run GoReleaser
|
name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v4
|
uses: goreleaser/goreleaser-action@v4
|
||||||
@ -29,6 +29,6 @@ jobs:
|
|||||||
# either 'goreleaser' (default) or 'goreleaser-pro'
|
# either 'goreleaser' (default) or 'goreleaser-pro'
|
||||||
distribution: goreleaser
|
distribution: goreleaser
|
||||||
version: latest
|
version: latest
|
||||||
args: release --rm-dist
|
args: release --clean
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
@ -19,6 +19,22 @@ linters:
|
|||||||
- nolintlint
|
- nolintlint
|
||||||
- revive
|
- revive
|
||||||
- wastedassign
|
- wastedassign
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
gosec:
|
||||||
|
# To select a subset of rules to run.
|
||||||
|
# Available rules: https://github.com/securego/gosec#available-rules
|
||||||
|
# Default: [] - means include all rules
|
||||||
|
includes:
|
||||||
|
- G102
|
||||||
|
- G106
|
||||||
|
- G108
|
||||||
|
- G109
|
||||||
|
- G111
|
||||||
|
- G112
|
||||||
|
- G201
|
||||||
|
- G203
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
exclude-rules:
|
exclude-rules:
|
||||||
- linters:
|
- linters:
|
||||||
@ -37,3 +53,6 @@ issues:
|
|||||||
- path: _test\.go
|
- path: _test\.go
|
||||||
linters:
|
linters:
|
||||||
- gosec # security is not make sense in tests
|
- gosec # security is not make sense in tests
|
||||||
|
- linters:
|
||||||
|
- revive
|
||||||
|
path: _test\.go
|
||||||
|
78
CHANGELOG.md
78
CHANGELOG.md
@ -1,13 +1,70 @@
|
|||||||
# Gin ChangeLog
|
# 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
|
||||||
|
|
||||||
|
* Stop useless panicking in context and render [#2150](https://github.com/gin-gonic/gin/pull/2150)
|
||||||
|
|
||||||
|
### BUG FIXES
|
||||||
|
|
||||||
|
* fix(router): tree bug where loop index is not decremented. [#3460](https://github.com/gin-gonic/gin/pull/3460)
|
||||||
|
* fix(context): panic on NegotiateFormat - index out of range [#3397](https://github.com/gin-gonic/gin/pull/3397)
|
||||||
|
* Add escape logic for header [#3500](https://github.com/gin-gonic/gin/pull/3500) and [#3503](https://github.com/gin-gonic/gin/pull/3503)
|
||||||
|
|
||||||
|
### SECURITY
|
||||||
|
|
||||||
|
* Fix the GO-2022-0969 and GO-2022-0288 vulnerabilities [#3333](https://github.com/gin-gonic/gin/pull/3333)
|
||||||
|
* fix(security): vulnerability GO-2023-1571 [#3505](https://github.com/gin-gonic/gin/pull/3505)
|
||||||
|
|
||||||
|
### ENHANCEMENTS
|
||||||
|
|
||||||
|
* feat: add sonic json support [#3184](https://github.com/gin-gonic/gin/pull/3184)
|
||||||
|
* chore(file): Creates a directory named path [#3316](https://github.com/gin-gonic/gin/pull/3316)
|
||||||
|
* fix: modify interface check way [#3327](https://github.com/gin-gonic/gin/pull/3327)
|
||||||
|
* remove deprecated of package io/ioutil [#3395](https://github.com/gin-gonic/gin/pull/3395)
|
||||||
|
* refactor: avoid calling strings.ToLower twice [#3343](https://github.com/gin-gonic/gin/pull/3433)
|
||||||
|
* console logger HTTP status code bug fixed [#3453](https://github.com/gin-gonic/gin/pull/3453)
|
||||||
|
* chore(yaml): upgrade dependency to v3 version [#3456](https://github.com/gin-gonic/gin/pull/3456)
|
||||||
|
* chore(router): match method added to routergroup for multiple HTTP methods supporting [#3464](https://github.com/gin-gonic/gin/pull/3464)
|
||||||
|
* chore(http): add support for go1.20 http.rwUnwrapper to gin.responseWriter [#3489](https://github.com/gin-gonic/gin/pull/3489)
|
||||||
|
|
||||||
|
### DOCS
|
||||||
|
|
||||||
|
* docs: update markdown format [#3260](https://github.com/gin-gonic/gin/pull/3260)
|
||||||
|
* docs(readme): Add the TOML rendering example [#3400](https://github.com/gin-gonic/gin/pull/3400)
|
||||||
|
* docs(readme): move more example to docs/doc.md [#3449](https://github.com/gin-gonic/gin/pull/3449)
|
||||||
|
* docs: update markdown format [#3446](https://github.com/gin-gonic/gin/pull/3446)
|
||||||
|
|
||||||
## Gin v1.8.2
|
## Gin v1.8.2
|
||||||
|
|
||||||
### Bugs
|
### BUG FIXES
|
||||||
|
|
||||||
* fix(route): redirectSlash bug ([#3227]((https://github.com/gin-gonic/gin/pull/3227)))
|
* fix(route): redirectSlash bug ([#3227]((https://github.com/gin-gonic/gin/pull/3227)))
|
||||||
* fix(engine): missing route params for CreateTestContext ([#2778]((https://github.com/gin-gonic/gin/pull/2778))) ([#2803]((https://github.com/gin-gonic/gin/pull/2803)))
|
* fix(engine): missing route params for CreateTestContext ([#2778]((https://github.com/gin-gonic/gin/pull/2778))) ([#2803]((https://github.com/gin-gonic/gin/pull/2803)))
|
||||||
|
|
||||||
### Security
|
### SECURITY
|
||||||
|
|
||||||
* Fix the GO-2022-1144 vulnerability ([#3432]((https://github.com/gin-gonic/gin/pull/3432)))
|
* Fix the GO-2022-1144 vulnerability ([#3432]((https://github.com/gin-gonic/gin/pull/3432)))
|
||||||
|
|
||||||
@ -19,7 +76,7 @@
|
|||||||
|
|
||||||
## Gin v1.8.0
|
## Gin v1.8.0
|
||||||
|
|
||||||
## Break Changes
|
### BREAK CHANGES
|
||||||
|
|
||||||
* TrustedProxies: Add default IPv6 support and refactor [#2967](https://github.com/gin-gonic/gin/pull/2967). Please replace `RemoteIP() (net.IP, bool)` with `RemoteIP() net.IP`
|
* TrustedProxies: Add default IPv6 support and refactor [#2967](https://github.com/gin-gonic/gin/pull/2967). Please replace `RemoteIP() (net.IP, bool)` with `RemoteIP() net.IP`
|
||||||
* gin.Context with fallback value from gin.Context.Request.Context() [#2751](https://github.com/gin-gonic/gin/pull/2751)
|
* gin.Context with fallback value from gin.Context.Request.Context() [#2751](https://github.com/gin-gonic/gin/pull/2751)
|
||||||
@ -149,32 +206,43 @@
|
|||||||
## Gin v1.6.2
|
## Gin v1.6.2
|
||||||
|
|
||||||
### BUG FIXES
|
### BUG FIXES
|
||||||
|
|
||||||
* fix missing initial sync.RWMutex [#2305](https://github.com/gin-gonic/gin/pull/2305)
|
* fix missing initial sync.RWMutex [#2305](https://github.com/gin-gonic/gin/pull/2305)
|
||||||
|
|
||||||
### ENHANCEMENTS
|
### ENHANCEMENTS
|
||||||
|
|
||||||
* Add set samesite in cookie. [#2306](https://github.com/gin-gonic/gin/pull/2306)
|
* Add set samesite in cookie. [#2306](https://github.com/gin-gonic/gin/pull/2306)
|
||||||
|
|
||||||
## Gin v1.6.1
|
## Gin v1.6.1
|
||||||
|
|
||||||
### BUG FIXES
|
### BUG FIXES
|
||||||
|
|
||||||
* Revert "fix accept incoming network connections" [#2294](https://github.com/gin-gonic/gin/pull/2294)
|
* Revert "fix accept incoming network connections" [#2294](https://github.com/gin-gonic/gin/pull/2294)
|
||||||
|
|
||||||
## Gin v1.6.0
|
## Gin v1.6.0
|
||||||
|
|
||||||
### BREAKING
|
### BREAKING
|
||||||
|
|
||||||
* chore(performance): Improve performance for adding RemoveExtraSlash flag [#2159](https://github.com/gin-gonic/gin/pull/2159)
|
* chore(performance): Improve performance for adding RemoveExtraSlash flag [#2159](https://github.com/gin-gonic/gin/pull/2159)
|
||||||
* drop support govendor [#2148](https://github.com/gin-gonic/gin/pull/2148)
|
* drop support govendor [#2148](https://github.com/gin-gonic/gin/pull/2148)
|
||||||
* Added support for SameSite cookie flag [#1615](https://github.com/gin-gonic/gin/pull/1615)
|
* Added support for SameSite cookie flag [#1615](https://github.com/gin-gonic/gin/pull/1615)
|
||||||
|
|
||||||
### FEATURES
|
### FEATURES
|
||||||
|
|
||||||
* add yaml negotiation [#2220](https://github.com/gin-gonic/gin/pull/2220)
|
* add yaml negotiation [#2220](https://github.com/gin-gonic/gin/pull/2220)
|
||||||
* FileFromFS [#2112](https://github.com/gin-gonic/gin/pull/2112)
|
* FileFromFS [#2112](https://github.com/gin-gonic/gin/pull/2112)
|
||||||
|
|
||||||
### BUG FIXES
|
### BUG FIXES
|
||||||
|
|
||||||
* Unix Socket Handling [#2280](https://github.com/gin-gonic/gin/pull/2280)
|
* Unix Socket Handling [#2280](https://github.com/gin-gonic/gin/pull/2280)
|
||||||
* Use json marshall in context json to fix breaking new line issue. Fixes #2209 [#2228](https://github.com/gin-gonic/gin/pull/2228)
|
* Use json marshall in context json to fix breaking new line issue. Fixes #2209 [#2228](https://github.com/gin-gonic/gin/pull/2228)
|
||||||
* fix accept incoming network connections [#2216](https://github.com/gin-gonic/gin/pull/2216)
|
* fix accept incoming network connections [#2216](https://github.com/gin-gonic/gin/pull/2216)
|
||||||
* Fixed a bug in the calculation of the maximum number of parameters [#2166](https://github.com/gin-gonic/gin/pull/2166)
|
* Fixed a bug in the calculation of the maximum number of parameters [#2166](https://github.com/gin-gonic/gin/pull/2166)
|
||||||
* [FIX] allow empty headers on DataFromReader [#2121](https://github.com/gin-gonic/gin/pull/2121)
|
* [FIX] allow empty headers on DataFromReader [#2121](https://github.com/gin-gonic/gin/pull/2121)
|
||||||
* Add mutex for protect Context.Keys map [#1391](https://github.com/gin-gonic/gin/pull/1391)
|
* Add mutex for protect Context.Keys map [#1391](https://github.com/gin-gonic/gin/pull/1391)
|
||||||
|
|
||||||
### ENHANCEMENTS
|
### ENHANCEMENTS
|
||||||
|
|
||||||
* Add mitigation for log injection [#2277](https://github.com/gin-gonic/gin/pull/2277)
|
* Add mitigation for log injection [#2277](https://github.com/gin-gonic/gin/pull/2277)
|
||||||
* tree: range over nodes values [#2229](https://github.com/gin-gonic/gin/pull/2229)
|
* tree: range over nodes values [#2229](https://github.com/gin-gonic/gin/pull/2229)
|
||||||
* tree: remove duplicate assignment [#2222](https://github.com/gin-gonic/gin/pull/2222)
|
* tree: remove duplicate assignment [#2222](https://github.com/gin-gonic/gin/pull/2222)
|
||||||
@ -189,7 +257,9 @@
|
|||||||
* upgrade go-validator to v10 [#2149](https://github.com/gin-gonic/gin/pull/2149)
|
* upgrade go-validator to v10 [#2149](https://github.com/gin-gonic/gin/pull/2149)
|
||||||
* Refactor redirect request in gin.go [#1970](https://github.com/gin-gonic/gin/pull/1970)
|
* Refactor redirect request in gin.go [#1970](https://github.com/gin-gonic/gin/pull/1970)
|
||||||
* Add build tag nomsgpack [#1852](https://github.com/gin-gonic/gin/pull/1852)
|
* Add build tag nomsgpack [#1852](https://github.com/gin-gonic/gin/pull/1852)
|
||||||
|
|
||||||
### DOCS
|
### DOCS
|
||||||
|
|
||||||
* docs(path): improve comments [#2223](https://github.com/gin-gonic/gin/pull/2223)
|
* docs(path): improve comments [#2223](https://github.com/gin-gonic/gin/pull/2223)
|
||||||
* Renew README to fit the modification of SetCookie method [#2217](https://github.com/gin-gonic/gin/pull/2217)
|
* Renew README to fit the modification of SetCookie method [#2217](https://github.com/gin-gonic/gin/pull/2217)
|
||||||
* Fix spelling [#2202](https://github.com/gin-gonic/gin/pull/2202)
|
* Fix spelling [#2202](https://github.com/gin-gonic/gin/pull/2202)
|
||||||
@ -202,7 +272,9 @@
|
|||||||
* Add project to README [#2165](https://github.com/gin-gonic/gin/pull/2165)
|
* Add project to README [#2165](https://github.com/gin-gonic/gin/pull/2165)
|
||||||
* docs(benchmarks): for gin v1.5 [#2153](https://github.com/gin-gonic/gin/pull/2153)
|
* docs(benchmarks): for gin v1.5 [#2153](https://github.com/gin-gonic/gin/pull/2153)
|
||||||
* Changed wording for clarity in README.md [#2122](https://github.com/gin-gonic/gin/pull/2122)
|
* Changed wording for clarity in README.md [#2122](https://github.com/gin-gonic/gin/pull/2122)
|
||||||
|
|
||||||
### MISC
|
### MISC
|
||||||
|
|
||||||
* ci support go1.14 [#2262](https://github.com/gin-gonic/gin/pull/2262)
|
* ci support go1.14 [#2262](https://github.com/gin-gonic/gin/pull/2262)
|
||||||
* chore: upgrade depend version [#2231](https://github.com/gin-gonic/gin/pull/2231)
|
* chore: upgrade depend version [#2231](https://github.com/gin-gonic/gin/pull/2231)
|
||||||
* Drop support go1.10 [#2147](https://github.com/gin-gonic/gin/pull/2147)
|
* Drop support go1.10 [#2147](https://github.com/gin-gonic/gin/pull/2147)
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
[](https://codecov.io/gh/gin-gonic/gin)
|
[](https://codecov.io/gh/gin-gonic/gin)
|
||||||
[](https://goreportcard.com/report/github.com/gin-gonic/gin)
|
[](https://goreportcard.com/report/github.com/gin-gonic/gin)
|
||||||
[](https://pkg.go.dev/github.com/gin-gonic/gin?tab=doc)
|
[](https://pkg.go.dev/github.com/gin-gonic/gin?tab=doc)
|
||||||
[](https://gitter.im/gin-gonic/gin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
||||||
[](https://sourcegraph.com/github.com/gin-gonic/gin?badge)
|
[](https://sourcegraph.com/github.com/gin-gonic/gin?badge)
|
||||||
[](https://www.codetriage.com/gin-gonic/gin)
|
[](https://www.codetriage.com/gin-gonic/gin)
|
||||||
[](https://github.com/gin-gonic/gin/releases)
|
[](https://github.com/gin-gonic/gin/releases)
|
||||||
@ -31,7 +30,7 @@ Gin is a web framework written in [Go](https://go.dev/). It features a martini-l
|
|||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- **[Go](https://go.dev/)**: ~~any one of the **three latest major** [releases](https://go.dev/doc/devel/release)~~ (now version **1.16+** is required).
|
- **[Go](https://go.dev/)**: any one of the **three latest major** [releases](https://go.dev/doc/devel/release) (we test it with these).
|
||||||
|
|
||||||
### Getting Gin
|
### Getting Gin
|
||||||
|
|
||||||
|
10
any.go
10
any.go
@ -1,10 +0,0 @@
|
|||||||
// Copyright 2022 Gin Core Team. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build !go1.18
|
|
||||||
// +build !go1.18
|
|
||||||
|
|
||||||
package gin
|
|
||||||
|
|
||||||
type any = interface{}
|
|
@ -1,10 +0,0 @@
|
|||||||
// Copyright 2022 Gin Core Team. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build !go1.18
|
|
||||||
// +build !go1.18
|
|
||||||
|
|
||||||
package binding
|
|
||||||
|
|
||||||
type any = interface{}
|
|
@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !nomsgpack
|
//go:build !nomsgpack
|
||||||
// +build !nomsgpack
|
|
||||||
|
|
||||||
package binding
|
package binding
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !nomsgpack
|
//go:build !nomsgpack
|
||||||
// +build !nomsgpack
|
|
||||||
|
|
||||||
package binding
|
package binding
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build nomsgpack
|
//go:build nomsgpack
|
||||||
// +build nomsgpack
|
|
||||||
|
|
||||||
package binding
|
package binding
|
||||||
|
|
||||||
|
@ -239,6 +239,11 @@ func setWithProperType(val string, value reflect.Value, field reflect.StructFiel
|
|||||||
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
|
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
|
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:
|
default:
|
||||||
return errUnknownType
|
return errUnknownType
|
||||||
}
|
}
|
||||||
|
@ -269,6 +269,39 @@ func TestMappingStructField(t *testing.T) {
|
|||||||
assert.Equal(t, 9, s.J.I)
|
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) {
|
func TestMappingMapField(t *testing.T) {
|
||||||
var s struct {
|
var s struct {
|
||||||
M map[string]int
|
M map[string]int
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
|
|
||||||
// EnableDecoderUseNumber is used to call the UseNumber method on the JSON
|
// EnableDecoderUseNumber is used to call the UseNumber method on the JSON
|
||||||
// Decoder instance. UseNumber causes the Decoder to unmarshal a number into an
|
// Decoder instance. UseNumber causes the Decoder to unmarshal a number into an
|
||||||
// interface{} as a Number instead of as a float64.
|
// any as a Number instead of as a float64.
|
||||||
var EnableDecoderUseNumber = false
|
var EnableDecoderUseNumber = false
|
||||||
|
|
||||||
// EnableDecoderDisallowUnknownFields is used to call the DisallowUnknownFields method
|
// EnableDecoderDisallowUnknownFields is used to call the DisallowUnknownFields method
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !nomsgpack
|
//go:build !nomsgpack
|
||||||
// +build !nomsgpack
|
|
||||||
|
|
||||||
package binding
|
package binding
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !nomsgpack
|
//go:build !nomsgpack
|
||||||
// +build !nomsgpack
|
|
||||||
|
|
||||||
package binding
|
package binding
|
||||||
|
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
coverage:
|
|
||||||
notify:
|
|
||||||
gitter:
|
|
||||||
default:
|
|
||||||
url: https://webhooks.gitter.im/e/d90dcdeeab2f1e357165
|
|
29
context.go
29
context.go
@ -652,7 +652,7 @@ func (c *Context) BindYAML(obj any) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BindTOML is a shortcut for c.MustBindWith(obj, binding.TOML).
|
// BindTOML is a shortcut for c.MustBindWith(obj, binding.TOML).
|
||||||
func (c *Context) BindTOML(obj interface{}) error {
|
func (c *Context) BindTOML(obj any) error {
|
||||||
return c.MustBindWith(obj, binding.TOML)
|
return c.MustBindWith(obj, binding.TOML)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -717,7 +717,7 @@ func (c *Context) ShouldBindYAML(obj any) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ShouldBindTOML is a shortcut for c.ShouldBindWith(obj, binding.TOML).
|
// ShouldBindTOML is a shortcut for c.ShouldBindWith(obj, binding.TOML).
|
||||||
func (c *Context) ShouldBindTOML(obj interface{}) error {
|
func (c *Context) ShouldBindTOML(obj any) error {
|
||||||
return c.ShouldBindWith(obj, binding.TOML)
|
return c.ShouldBindWith(obj, binding.TOML)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -995,7 +995,7 @@ func (c *Context) YAML(code int, obj any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TOML serializes the given struct as TOML into the response body.
|
// TOML serializes the given struct as TOML into the response body.
|
||||||
func (c *Context) TOML(code int, obj interface{}) {
|
func (c *Context) TOML(code int, obj any) {
|
||||||
c.Render(code, render.TOML{Data: obj})
|
c.Render(code, render.TOML{Data: obj})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1052,11 +1052,17 @@ func (c *Context) FileFromFS(filepath string, fs http.FileSystem) {
|
|||||||
http.FileServer(fs).ServeHTTP(c.Writer, c.Request)
|
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
|
// 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
|
// On the client side, the file will typically be downloaded with the given filename
|
||||||
func (c *Context) FileAttachment(filepath, filename string) {
|
func (c *Context) FileAttachment(filepath, filename string) {
|
||||||
if isASCII(filename) {
|
if isASCII(filename) {
|
||||||
c.Writer.Header().Set("Content-Disposition", `attachment; filename="`+filename+`"`)
|
c.Writer.Header().Set("Content-Disposition", `attachment; filename="`+escapeQuotes(filename)+`"`)
|
||||||
} else {
|
} else {
|
||||||
c.Writer.Header().Set("Content-Disposition", `attachment; filename*=UTF-8''`+url.QueryEscape(filename))
|
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 *****/
|
/***** 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.
|
// Deadline returns that there is no deadline (ok==false) when c.Request has no Context.
|
||||||
func (c *Context) Deadline() (deadline time.Time, ok bool) {
|
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
|
||||||
}
|
}
|
||||||
return c.Request.Context().Deadline()
|
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.
|
// Done returns nil (chan which will wait forever) when c.Request has no Context.
|
||||||
func (c *Context) Done() <-chan struct{} {
|
func (c *Context) Done() <-chan struct{} {
|
||||||
if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil {
|
if !c.hasRequestContext() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return c.Request.Context().Done()
|
return c.Request.Context().Done()
|
||||||
@ -1192,7 +1205,7 @@ func (c *Context) Done() <-chan struct{} {
|
|||||||
|
|
||||||
// Err returns nil when c.Request has no Context.
|
// Err returns nil when c.Request has no Context.
|
||||||
func (c *Context) Err() error {
|
func (c *Context) Err() error {
|
||||||
if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil {
|
if !c.hasRequestContext() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return c.Request.Context().Err()
|
return c.Request.Context().Err()
|
||||||
@ -1213,7 +1226,7 @@ func (c *Context) Value(key any) any {
|
|||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !c.engine.ContextWithFallback || c.Request == nil || c.Request.Context() == nil {
|
if !c.hasRequestContext() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return c.Request.Context().Value(key)
|
return c.Request.Context().Value(key)
|
||||||
|
@ -1,94 +0,0 @@
|
|||||||
// Copyright 2021 Gin Core Team. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build go1.17
|
|
||||||
// +build go1.17
|
|
||||||
|
|
||||||
package gin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"mime/multipart"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
type interceptedWriter struct {
|
|
||||||
ResponseWriter
|
|
||||||
b *bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i interceptedWriter) WriteHeader(code int) {
|
|
||||||
i.Header().Del("X-Test")
|
|
||||||
i.ResponseWriter.WriteHeader(code)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContextFormFileFailed17(t *testing.T) {
|
|
||||||
if !isGo117OrGo118() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
mw := multipart.NewWriter(buf)
|
|
||||||
defer func(mw *multipart.Writer) {
|
|
||||||
err := mw.Close()
|
|
||||||
if err != nil {
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
}(mw)
|
|
||||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
|
||||||
c.Request, _ = http.NewRequest("POST", "/", nil)
|
|
||||||
c.Request.Header.Set("Content-Type", mw.FormDataContentType())
|
|
||||||
c.engine.MaxMultipartMemory = 8 << 20
|
|
||||||
assert.Panics(t, func() {
|
|
||||||
f, err := c.FormFile("file")
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Nil(t, f)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInterceptedHeader(t *testing.T) {
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
c, r := CreateTestContext(w)
|
|
||||||
|
|
||||||
r.Use(func(c *Context) {
|
|
||||||
i := interceptedWriter{
|
|
||||||
ResponseWriter: c.Writer,
|
|
||||||
b: bytes.NewBuffer(nil),
|
|
||||||
}
|
|
||||||
c.Writer = i
|
|
||||||
c.Next()
|
|
||||||
c.Header("X-Test", "overridden")
|
|
||||||
c.Writer = i.ResponseWriter
|
|
||||||
})
|
|
||||||
r.GET("/", func(c *Context) {
|
|
||||||
c.Header("X-Test", "original")
|
|
||||||
c.Header("X-Test-2", "present")
|
|
||||||
c.String(http.StatusOK, "hello world")
|
|
||||||
})
|
|
||||||
c.Request = httptest.NewRequest("GET", "/", nil)
|
|
||||||
r.HandleContext(c)
|
|
||||||
// Result() has headers frozen when WriteHeaderNow() has been called
|
|
||||||
// Compared to this time, this is when the response headers will be flushed
|
|
||||||
// As response is flushed on c.String, the Header cannot be set by the first
|
|
||||||
// middleware. Assert this
|
|
||||||
assert.Equal(t, "", w.Result().Header.Get("X-Test"))
|
|
||||||
assert.Equal(t, "present", w.Result().Header.Get("X-Test-2"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func isGo117OrGo118() bool {
|
|
||||||
version := strings.Split(runtime.Version()[2:], ".")
|
|
||||||
if len(version) >= 2 {
|
|
||||||
x := version[0]
|
|
||||||
y := version[1]
|
|
||||||
if x == "1" && (y == "17" || y == "18") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
@ -2,8 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT style
|
// Use of this source code is governed by a MIT style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !go1.17
|
//go:build !go1.19
|
||||||
// +build !go1.17
|
|
||||||
|
|
||||||
package gin
|
package gin
|
||||||
|
|
||||||
@ -17,15 +16,22 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestContextFormFileFailed16(t *testing.T) {
|
func TestContextFormFileFailed18(t *testing.T) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
mw := multipart.NewWriter(buf)
|
mw := multipart.NewWriter(buf)
|
||||||
mw.Close()
|
defer func(mw *multipart.Writer) {
|
||||||
|
err := mw.Close()
|
||||||
|
if err != nil {
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
}(mw)
|
||||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
c.Request, _ = http.NewRequest("POST", "/", nil)
|
c.Request, _ = http.NewRequest("POST", "/", nil)
|
||||||
c.Request.Header.Set("Content-Type", mw.FormDataContentType())
|
c.Request.Header.Set("Content-Type", mw.FormDataContentType())
|
||||||
c.engine.MaxMultipartMemory = 8 << 20
|
c.engine.MaxMultipartMemory = 8 << 20
|
||||||
|
assert.Panics(t, func() {
|
||||||
f, err := c.FormFile("file")
|
f, err := c.FormFile("file")
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Nil(t, f)
|
assert.Nil(t, f)
|
||||||
|
})
|
||||||
}
|
}
|
@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build go1.19
|
//go:build go1.19
|
||||||
// +build go1.19
|
|
||||||
|
|
||||||
package gin
|
package gin
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build appengine
|
//go:build appengine
|
||||||
// +build appengine
|
|
||||||
|
|
||||||
package gin
|
package gin
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ var errTestRender = errors.New("TestRender")
|
|||||||
// Unit tests TODO
|
// Unit tests TODO
|
||||||
// func (c *Context) File(filepath string) {
|
// func (c *Context) File(filepath string) {
|
||||||
// func (c *Context) Negotiate(code int, config Negotiate) {
|
// func (c *Context) Negotiate(code int, config Negotiate) {
|
||||||
// BAD case: func (c *Context) Render(code int, render render.Render, obj ...interface{}) {
|
// BAD case: func (c *Context) Render(code int, render render.Render, obj ...any) {
|
||||||
// test that information is not leaked when reusing Contexts (using the Pool)
|
// test that information is not leaked when reusing Contexts (using the Pool)
|
||||||
|
|
||||||
func createMultipartRequest() *http.Request {
|
func createMultipartRequest() *http.Request {
|
||||||
@ -1032,6 +1032,20 @@ func TestContextRenderAttachment(t *testing.T) {
|
|||||||
assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", newFilename), w.Header().Get("Content-Disposition"))
|
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) {
|
func TestContextRenderUTF8Attachment(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
@ -2162,6 +2176,24 @@ func TestRemoteIPFail(t *testing.T) {
|
|||||||
assert.False(t, trust)
|
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) {
|
func TestContextWithFallbackDeadlineFromRequestContext(t *testing.T) {
|
||||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
// enable ContextWithFallback feature flag
|
// enable ContextWithFallback feature flag
|
||||||
@ -2374,3 +2406,42 @@ func TestCreateTestContextWithRouteParams(t *testing.T) {
|
|||||||
assert.Equal(t, http.StatusOK, w.Code)
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
assert.Equal(t, "hello gin", w.Body.String())
|
assert.Equal(t, "hello gin", w.Body.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type interceptedWriter struct {
|
||||||
|
ResponseWriter
|
||||||
|
b *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i interceptedWriter) WriteHeader(code int) {
|
||||||
|
i.Header().Del("X-Test")
|
||||||
|
i.ResponseWriter.WriteHeader(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInterceptedHeader(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, r := CreateTestContext(w)
|
||||||
|
|
||||||
|
r.Use(func(c *Context) {
|
||||||
|
i := interceptedWriter{
|
||||||
|
ResponseWriter: c.Writer,
|
||||||
|
b: bytes.NewBuffer(nil),
|
||||||
|
}
|
||||||
|
c.Writer = i
|
||||||
|
c.Next()
|
||||||
|
c.Header("X-Test", "overridden")
|
||||||
|
c.Writer = i.ResponseWriter
|
||||||
|
})
|
||||||
|
r.GET("/", func(c *Context) {
|
||||||
|
c.Header("X-Test", "original")
|
||||||
|
c.Header("X-Test-2", "present")
|
||||||
|
c.String(http.StatusOK, "hello world")
|
||||||
|
})
|
||||||
|
c.Request = httptest.NewRequest("GET", "/", nil)
|
||||||
|
r.HandleContext(c)
|
||||||
|
// Result() has headers frozen when WriteHeaderNow() has been called
|
||||||
|
// Compared to this time, this is when the response headers will be flushed
|
||||||
|
// As response is flushed on c.String, the Header cannot be set by the first
|
||||||
|
// middleware. Assert this
|
||||||
|
assert.Equal(t, "", w.Result().Header.Get("X-Test"))
|
||||||
|
assert.Equal(t, "present", w.Result().Header.Get("X-Test-2"))
|
||||||
|
}
|
||||||
|
4
debug.go
4
debug.go
@ -12,7 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ginSupportMinGoVer = 16
|
const ginSupportMinGoVer = 18
|
||||||
|
|
||||||
// IsDebugging returns true if the framework is running in debug mode.
|
// IsDebugging returns true if the framework is running in debug mode.
|
||||||
// Use SetMode(gin.ReleaseMode) to disable debug mode.
|
// Use SetMode(gin.ReleaseMode) to disable debug mode.
|
||||||
@ -67,7 +67,7 @@ func getMinVer(v string) (uint64, error) {
|
|||||||
|
|
||||||
func debugPrintWARNINGDefault() {
|
func debugPrintWARNINGDefault() {
|
||||||
if v, e := getMinVer(runtime.Version()); e == nil && v < ginSupportMinGoVer {
|
if v, e := getMinVer(runtime.Version()); e == nil && v < ginSupportMinGoVer {
|
||||||
debugPrint(`[WARNING] Now Gin requires Go 1.16+.
|
debugPrint(`[WARNING] Now Gin requires Go 1.18+.
|
||||||
|
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import (
|
|||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
// func debugRoute(httpMethod, absolutePath string, handlers HandlersChain) {
|
// func debugRoute(httpMethod, absolutePath string, handlers HandlersChain) {
|
||||||
// func debugPrint(format string, values ...interface{}) {
|
// func debugPrint(format string, values ...any) {
|
||||||
|
|
||||||
func TestIsDebugging(t *testing.T) {
|
func TestIsDebugging(t *testing.T) {
|
||||||
SetMode(DebugMode)
|
SetMode(DebugMode)
|
||||||
@ -104,7 +104,7 @@ func TestDebugPrintWARNINGDefault(t *testing.T) {
|
|||||||
})
|
})
|
||||||
m, e := getMinVer(runtime.Version())
|
m, e := getMinVer(runtime.Version())
|
||||||
if e == nil && m < ginSupportMinGoVer {
|
if e == nil && m < ginSupportMinGoVer {
|
||||||
assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.16+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
|
assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.18+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
|
||||||
} else {
|
} else {
|
||||||
assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
|
assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
// BindWith binds the passed struct pointer using the specified binding engine.
|
// BindWith binds the passed struct pointer using the specified binding engine.
|
||||||
// See the binding package.
|
// See the binding package.
|
||||||
func (c *Context) BindWith(obj any, b binding.Binding) error {
|
func (c *Context) BindWith(obj any, b binding.Binding) error {
|
||||||
log.Println(`BindWith(\"interface{}, binding.Binding\") error is going to
|
log.Println(`BindWith(\"any, binding.Binding\") error is going to
|
||||||
be deprecated, please check issue #662 and either use MustBindWith() if you
|
be deprecated, please check issue #662 and either use MustBindWith() if you
|
||||||
want HTTP 400 to be automatically returned if any error occur, or use
|
want HTTP 400 to be automatically returned if any error occur, or use
|
||||||
ShouldBindWith() if you need to manage the error.`)
|
ShouldBindWith() if you need to manage the error.`)
|
||||||
|
@ -425,7 +425,7 @@ func main() {
|
|||||||
r.Use(gin.Logger())
|
r.Use(gin.Logger())
|
||||||
|
|
||||||
// Recovery middleware recovers from any panics and writes a 500 if there was one.
|
// Recovery middleware recovers from any panics and writes a 500 if there was one.
|
||||||
r.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
|
r.Use(gin.CustomRecovery(func(c *gin.Context, recovered any) {
|
||||||
if err, ok := recovered.(string); ok {
|
if err, ok := recovered.(string); ok {
|
||||||
c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err))
|
c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err))
|
||||||
}
|
}
|
||||||
@ -996,7 +996,7 @@ curl -X POST -v --form name=user --form "avatar=@./avatar.png" http://localhost:
|
|||||||
func main() {
|
func main() {
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
|
|
||||||
// gin.H is a shortcut for map[string]interface{}
|
// gin.H is a shortcut for map[string]any
|
||||||
r.GET("/someJSON", func(c *gin.Context) {
|
r.GET("/someJSON", func(c *gin.Context) {
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
|
c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
|
||||||
})
|
})
|
||||||
@ -1961,7 +1961,7 @@ func (customerBinding) Name() string {
|
|||||||
return "form"
|
return "form"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (customerBinding) Bind(req *http.Request, obj interface{}) error {
|
func (customerBinding) Bind(req *http.Request, obj any) error {
|
||||||
if err := req.ParseForm(); err != nil {
|
if err := req.ParseForm(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1976,7 +1976,7 @@ func (customerBinding) Bind(req *http.Request, obj interface{}) error {
|
|||||||
return validate(obj)
|
return validate(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validate(obj interface{}) error {
|
func validate(obj any) error {
|
||||||
if binding.Validator == nil {
|
if binding.Validator == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
2
fs.go
2
fs.go
@ -39,7 +39,7 @@ func (fs onlyFilesFS) Open(name string) (http.File, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Readdir overrides the http.File default implementation.
|
// Readdir overrides the http.File default implementation.
|
||||||
func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) {
|
func (f neuteredReaddirFile) Readdir(_ int) ([]os.FileInfo, error) {
|
||||||
// this disables directory listing
|
// this disables directory listing
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
14
gin.go
14
gin.go
@ -11,6 +11,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@ -40,6 +41,9 @@ var defaultTrustedCIDRs = []*net.IPNet{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var regSafePrefix = regexp.MustCompile("[^a-zA-Z0-9/-]+")
|
||||||
|
var 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)
|
||||||
|
|
||||||
@ -330,7 +334,6 @@ func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
|
|||||||
}
|
}
|
||||||
root.addRoute(path, handlers)
|
root.addRoute(path, handlers)
|
||||||
|
|
||||||
// Update maxParams
|
|
||||||
if paramsCount := countParams(path); paramsCount > engine.maxParams {
|
if paramsCount := countParams(path); paramsCount > engine.maxParams {
|
||||||
engine.maxParams = paramsCount
|
engine.maxParams = paramsCount
|
||||||
}
|
}
|
||||||
@ -511,7 +514,7 @@ func (engine *Engine) RunUnix(file string) (err error) {
|
|||||||
|
|
||||||
if engine.isUnsafeTrustedProxies() {
|
if engine.isUnsafeTrustedProxies() {
|
||||||
debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
|
debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
|
||||||
"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
|
"Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.")
|
||||||
}
|
}
|
||||||
|
|
||||||
listener, err := net.Listen("unix", file)
|
listener, err := net.Listen("unix", file)
|
||||||
@ -534,7 +537,7 @@ func (engine *Engine) RunFd(fd int) (err error) {
|
|||||||
|
|
||||||
if engine.isUnsafeTrustedProxies() {
|
if engine.isUnsafeTrustedProxies() {
|
||||||
debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
|
debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
|
||||||
"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
|
"Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.")
|
||||||
}
|
}
|
||||||
|
|
||||||
f := os.NewFile(uintptr(fd), fmt.Sprintf("fd@%d", fd))
|
f := os.NewFile(uintptr(fd), fmt.Sprintf("fd@%d", fd))
|
||||||
@ -555,7 +558,7 @@ func (engine *Engine) RunListener(listener net.Listener) (err error) {
|
|||||||
|
|
||||||
if engine.isUnsafeTrustedProxies() {
|
if engine.isUnsafeTrustedProxies() {
|
||||||
debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
|
debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
|
||||||
"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-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())
|
err = http.Serve(listener, engine.Handler())
|
||||||
@ -668,6 +671,9 @@ func redirectTrailingSlash(c *Context) {
|
|||||||
req := c.Request
|
req := c.Request
|
||||||
p := req.URL.Path
|
p := req.URL.Path
|
||||||
if prefix := path.Clean(c.Request.Header.Get("X-Forwarded-Prefix")); prefix != "." {
|
if prefix := path.Clean(c.Request.Header.Get("X-Forwarded-Prefix")); prefix != "." {
|
||||||
|
prefix = regSafePrefix.ReplaceAllString(prefix, "")
|
||||||
|
prefix = regRemoveRepeatedChar.ReplaceAllString(prefix, "/")
|
||||||
|
|
||||||
p = prefix + "/" + req.URL.Path
|
p = prefix + "/" + req.URL.Path
|
||||||
}
|
}
|
||||||
req.URL.Path = p + "/"
|
req.URL.Path = p + "/"
|
||||||
|
36
go.mod
36
go.mod
@ -1,36 +1,36 @@
|
|||||||
module github.com/gin-gonic/gin
|
module github.com/gin-gonic/gin
|
||||||
|
|
||||||
go 1.18
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bytedance/sonic v1.7.1
|
github.com/bytedance/sonic v1.9.1
|
||||||
github.com/gin-contrib/sse v0.1.0
|
github.com/gin-contrib/sse v0.1.0
|
||||||
github.com/go-playground/validator/v10 v10.11.2
|
github.com/go-playground/validator/v10 v10.16.0
|
||||||
github.com/goccy/go-json v0.10.0
|
github.com/goccy/go-json v0.10.2
|
||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/mattn/go-isatty v0.0.17
|
github.com/mattn/go-isatty v0.0.19
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6
|
github.com/pelletier/go-toml/v2 v2.0.8
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.3
|
||||||
github.com/ugorji/go/codec v1.2.9
|
github.com/ugorji/go/codec v1.2.11
|
||||||
golang.org/x/net v0.6.0
|
golang.org/x/net v0.18.0
|
||||||
google.golang.org/protobuf v1.28.1
|
google.golang.org/protobuf v1.30.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
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.0.14 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/leodido/go-urn v1.2.4 // indirect
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 // indirect
|
golang.org/x/arch v0.3.0 // indirect
|
||||||
golang.org/x/crypto v0.5.0 // indirect
|
golang.org/x/crypto v0.15.0 // indirect
|
||||||
golang.org/x/sys v0.5.0 // indirect
|
golang.org/x/sys v0.14.0 // indirect
|
||||||
golang.org/x/text v0.7.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
)
|
)
|
||||||
|
76
go.sum
76
go.sum
@ -1,13 +1,14 @@
|
|||||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
github.com/bytedance/sonic v1.7.1 h1:UYWEKUHQDye89c2U6zvrvuxWdGCI/wCrZITFQmKGtGc=
|
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||||
github.com/bytedance/sonic v1.7.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
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 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-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||||
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=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
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/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=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
@ -15,10 +16,10 @@ 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/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 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
|
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
|
||||||
github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
|
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
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/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 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
@ -26,57 +27,56 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
|
|||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
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/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.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.14 h1:QRqdp6bb9M9S5yyKeYteXKuoKE4p0tGlra81fKOpWH8=
|
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.14/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
|
||||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
|
||||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
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 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
|
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
|
||||||
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=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
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.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
|
||||||
|
github.com/stretchr/testify v1.8.3/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 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU=
|
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||||
github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15 h1:GVfVkciLYxn5mY5EncwAe0SXUn9Rm81rRkZ0TTmn/cU=
|
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||||
golang.org/x/arch v0.0.0-20220412001346-fc48f9fe4c15/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
|
||||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
|
||||||
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
|
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
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/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=
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
// Use of this source code is governed by a MIT style
|
// Use of this source code is governed by a MIT style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !go1.20
|
||||||
|
|
||||||
package bytesconv
|
package bytesconv
|
||||||
|
|
||||||
import (
|
import (
|
23
internal/bytesconv/bytesconv_1.20.go
Normal file
23
internal/bytesconv/bytesconv_1.20.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2023 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build go1.20
|
||||||
|
|
||||||
|
package bytesconv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StringToBytes converts string to byte slice without a memory allocation.
|
||||||
|
// For more details, see https://github.com/golang/go/issues/53003#issuecomment-1140276077.
|
||||||
|
func StringToBytes(s string) []byte {
|
||||||
|
return unsafe.Slice(unsafe.StringData(s), len(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesToString converts byte slice to string without a memory allocation.
|
||||||
|
// For more details, see https://github.com/golang/go/issues/53003#issuecomment-1140276077.
|
||||||
|
func BytesToString(b []byte) string {
|
||||||
|
return unsafe.String(unsafe.SliceData(b), len(b))
|
||||||
|
}
|
@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build go_json
|
//go:build go_json
|
||||||
// +build go_json
|
|
||||||
|
|
||||||
package json
|
package json
|
||||||
|
|
||||||
|
@ -3,9 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !jsoniter && !go_json && !(sonic && avx && (linux || windows || darwin) && amd64)
|
//go:build !jsoniter && !go_json && !(sonic && avx && (linux || windows || darwin) && amd64)
|
||||||
// +build !jsoniter
|
|
||||||
// +build !go_json
|
|
||||||
// +build !sonic !avx !linux,!windows,!darwin !amd64
|
|
||||||
|
|
||||||
package json
|
package json
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build jsoniter
|
//go:build jsoniter
|
||||||
// +build jsoniter
|
|
||||||
|
|
||||||
package json
|
package json
|
||||||
|
|
||||||
|
@ -3,10 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build sonic && avx && (linux || windows || darwin) && amd64
|
//go:build sonic && avx && (linux || windows || darwin) && amd64
|
||||||
// +build sonic
|
|
||||||
// +build avx
|
|
||||||
// +build linux windows darwin
|
|
||||||
// +build amd64
|
|
||||||
|
|
||||||
package json
|
package json
|
||||||
|
|
||||||
|
@ -83,6 +83,8 @@ func (p *LogFormatterParams) StatusCodeColor() string {
|
|||||||
code := p.StatusCode
|
code := p.StatusCode
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
case code >= http.StatusContinue && code < http.StatusOK:
|
||||||
|
return white
|
||||||
case code >= http.StatusOK && code < http.StatusMultipleChoices:
|
case code >= http.StatusOK && code < http.StatusMultipleChoices:
|
||||||
return green
|
return green
|
||||||
case code >= http.StatusMultipleChoices && code < http.StatusBadRequest:
|
case code >= http.StatusMultipleChoices && code < http.StatusBadRequest:
|
||||||
|
@ -310,6 +310,7 @@ func TestColorForStatus(t *testing.T) {
|
|||||||
return p.StatusCodeColor()
|
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, green, colorForStatus(http.StatusOK), "2xx should be green")
|
||||||
assert.Equal(t, white, colorForStatus(http.StatusMovedPermanently), "3xx should be white")
|
assert.Equal(t, white, colorForStatus(http.StatusMovedPermanently), "3xx should be white")
|
||||||
assert.Equal(t, yellow, colorForStatus(http.StatusNotFound), "4xx should be yellow")
|
assert.Equal(t, yellow, colorForStatus(http.StatusNotFound), "4xx should be yellow")
|
||||||
|
@ -103,7 +103,7 @@ func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultHandleRecovery(c *Context, err any) {
|
func defaultHandleRecovery(c *Context, _ any) {
|
||||||
c.AbortWithStatus(http.StatusInternalServerError)
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +164,7 @@ func function(pc uintptr) []byte {
|
|||||||
if period := bytes.Index(name, dot); period >= 0 {
|
if period := bytes.Index(name, dot); period >= 0 {
|
||||||
name = name[period+1:]
|
name = name[period+1:]
|
||||||
}
|
}
|
||||||
name = bytes.Replace(name, centerDot, dot, -1)
|
name = bytes.ReplaceAll(name, centerDot, dot)
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
// Copyright 2021 Gin Core Team. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build !go1.18
|
|
||||||
// +build !go1.18
|
|
||||||
|
|
||||||
package render
|
|
||||||
|
|
||||||
type any = interface{}
|
|
@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !nomsgpack
|
//go:build !nomsgpack
|
||||||
// +build !nomsgpack
|
|
||||||
|
|
||||||
package render
|
package render
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !nomsgpack
|
//go:build !nomsgpack
|
||||||
// +build !nomsgpack
|
|
||||||
|
|
||||||
package render
|
package render
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin/internal/json"
|
||||||
testdata "github.com/gin-gonic/gin/testdata/protoexample"
|
testdata "github.com/gin-gonic/gin/testdata/protoexample"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
@ -136,6 +137,51 @@ func TestRenderJsonpJSON(t *testing.T) {
|
|||||||
assert.Equal(t, "application/javascript; charset=utf-8", w2.Header().Get("Content-Type"))
|
assert.Equal(t, "application/javascript; charset=utf-8", w2.Header().Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type errorWriter struct {
|
||||||
|
bufString string
|
||||||
|
*httptest.ResponseRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ http.ResponseWriter = (*errorWriter)(nil)
|
||||||
|
|
||||||
|
func (w *errorWriter) Write(buf []byte) (int, error) {
|
||||||
|
if string(buf) == w.bufString {
|
||||||
|
return 0, errors.New(`write "` + w.bufString + `" error`)
|
||||||
|
}
|
||||||
|
return w.ResponseRecorder.Write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderJsonpJSONError(t *testing.T) {
|
||||||
|
ew := &errorWriter{
|
||||||
|
ResponseRecorder: httptest.NewRecorder(),
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonpJSON := JsonpJSON{
|
||||||
|
Callback: "foo",
|
||||||
|
Data: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cb := template.JSEscapeString(jsonpJSON.Callback)
|
||||||
|
ew.bufString = cb
|
||||||
|
err := jsonpJSON.Render(ew) // error was returned while writing callback
|
||||||
|
assert.Equal(t, `write "`+cb+`" error`, err.Error())
|
||||||
|
|
||||||
|
ew.bufString = `(`
|
||||||
|
err = jsonpJSON.Render(ew)
|
||||||
|
assert.Equal(t, `write "`+`(`+`" error`, err.Error())
|
||||||
|
|
||||||
|
data, _ := json.Marshal(jsonpJSON.Data) // error was returned while writing data
|
||||||
|
ew.bufString = string(data)
|
||||||
|
err = jsonpJSON.Render(ew)
|
||||||
|
assert.Equal(t, `write "`+string(data)+`" error`, err.Error())
|
||||||
|
|
||||||
|
ew.bufString = `);`
|
||||||
|
err = jsonpJSON.Render(ew)
|
||||||
|
assert.Equal(t, `write "`+`);`+`" error`, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
func TestRenderJsonpJSONError2(t *testing.T) {
|
func TestRenderJsonpJSONError2(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
data := map[string]any{
|
data := map[string]any{
|
||||||
@ -532,3 +578,16 @@ func TestRenderReaderNoContentLength(t *testing.T) {
|
|||||||
assert.Equal(t, headers["Content-Disposition"], w.Header().Get("Content-Disposition"))
|
assert.Equal(t, headers["Content-Disposition"], w.Header().Get("Content-Disposition"))
|
||||||
assert.Equal(t, headers["x-request-id"], w.Header().Get("x-request-id"))
|
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
|
// status must be 200 although we tried to change it
|
||||||
assert.Equal(t, http.StatusOK, w.Status())
|
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")
|
||||||
|
}
|
||||||
|
@ -185,6 +185,54 @@ func TestRouteRedirectTrailingSlash(t *testing.T) {
|
|||||||
w = PerformRequest(router, http.MethodGet, "/path2/", header{Key: "X-Forwarded-Prefix", Value: "/api/"})
|
w = PerformRequest(router, http.MethodGet, "/path2/", header{Key: "X-Forwarded-Prefix", Value: "/api/"})
|
||||||
assert.Equal(t, 200, w.Code)
|
assert.Equal(t, 200, w.Code)
|
||||||
|
|
||||||
|
w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "../../api#?"})
|
||||||
|
assert.Equal(t, "/api/path", w.Header().Get("Location"))
|
||||||
|
assert.Equal(t, 301, w.Code)
|
||||||
|
|
||||||
|
w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "../../api"})
|
||||||
|
assert.Equal(t, "/api/path", w.Header().Get("Location"))
|
||||||
|
assert.Equal(t, 301, w.Code)
|
||||||
|
|
||||||
|
w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "../../api"})
|
||||||
|
assert.Equal(t, "/api/path2/", w.Header().Get("Location"))
|
||||||
|
assert.Equal(t, 301, w.Code)
|
||||||
|
|
||||||
|
w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "/../../api"})
|
||||||
|
assert.Equal(t, "/api/path2/", w.Header().Get("Location"))
|
||||||
|
assert.Equal(t, 301, w.Code)
|
||||||
|
|
||||||
|
w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "api/../../"})
|
||||||
|
assert.Equal(t, "//path", w.Header().Get("Location"))
|
||||||
|
assert.Equal(t, 301, w.Code)
|
||||||
|
|
||||||
|
w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "api/../../../"})
|
||||||
|
assert.Equal(t, "/path", w.Header().Get("Location"))
|
||||||
|
assert.Equal(t, 301, w.Code)
|
||||||
|
|
||||||
|
w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "../../gin-gonic.com"})
|
||||||
|
assert.Equal(t, "/gin-goniccom/path2/", w.Header().Get("Location"))
|
||||||
|
assert.Equal(t, 301, w.Code)
|
||||||
|
|
||||||
|
w = PerformRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "/../../gin-gonic.com"})
|
||||||
|
assert.Equal(t, "/gin-goniccom/path2/", w.Header().Get("Location"))
|
||||||
|
assert.Equal(t, 301, w.Code)
|
||||||
|
|
||||||
|
w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "https://gin-gonic.com/#"})
|
||||||
|
assert.Equal(t, "https/gin-goniccom/https/gin-goniccom/path", w.Header().Get("Location"))
|
||||||
|
assert.Equal(t, 301, w.Code)
|
||||||
|
|
||||||
|
w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "#api"})
|
||||||
|
assert.Equal(t, "api/api/path", w.Header().Get("Location"))
|
||||||
|
assert.Equal(t, 301, w.Code)
|
||||||
|
|
||||||
|
w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "/nor-mal/#?a=1"})
|
||||||
|
assert.Equal(t, "/nor-mal/a1/path", w.Header().Get("Location"))
|
||||||
|
assert.Equal(t, 301, w.Code)
|
||||||
|
|
||||||
|
w = PerformRequest(router, http.MethodGet, "/path/", header{Key: "X-Forwarded-Prefix", Value: "/nor-mal/%2e%2e/"})
|
||||||
|
assert.Equal(t, "/nor-mal/2e2e/path", w.Header().Get("Location"))
|
||||||
|
assert.Equal(t, 301, w.Code)
|
||||||
|
|
||||||
router.RedirectTrailingSlash = false
|
router.RedirectTrailingSlash = false
|
||||||
|
|
||||||
w = PerformRequest(router, http.MethodGet, "/path/")
|
w = PerformRequest(router, http.MethodGet, "/path/")
|
||||||
|
10
testdata/protoexample/any.go
vendored
10
testdata/protoexample/any.go
vendored
@ -1,10 +0,0 @@
|
|||||||
// Copyright 2021 Gin Core Team. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build !go1.18
|
|
||||||
// +build !go1.18
|
|
||||||
|
|
||||||
package protoexample
|
|
||||||
|
|
||||||
type any = interface{}
|
|
2
utils.go
2
utils.go
@ -50,7 +50,7 @@ func WrapH(h http.Handler) HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// H is a shortcut for map[string]interface{}
|
// H is a shortcut for map[string]any
|
||||||
type H map[string]any
|
type H map[string]any
|
||||||
|
|
||||||
// MarshalXML allows type H to be used with xml.Marshal.
|
// MarshalXML allows type H to be used with xml.Marshal.
|
||||||
|
@ -5,4 +5,4 @@
|
|||||||
package gin
|
package gin
|
||||||
|
|
||||||
// Version is the current gin framework's version.
|
// Version is the current gin framework's version.
|
||||||
const Version = "v1.8.2"
|
const Version = "v1.9.1"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user