Merge branch 'master' into bugfix/form-array-binding-regression

This commit is contained in:
Bo-Yi Wu 2026-02-28 21:16:22 +08:00 committed by GitHub
commit e7abd0b4aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 905 additions and 376 deletions

View File

@ -26,14 +26,14 @@ jobs:
- name: Setup golangci-lint
uses: golangci/golangci-lint-action@v9
with:
version: v2.6
version: v2.9
args: --verbose
test:
needs: lint
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
go: ["1.24", "1.25"]
go: ["1.25", "1.26"]
test-tags:
[
"",

View File

@ -21,7 +21,7 @@ jobs:
with:
go-version: "^1"
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
uses: goreleaser/goreleaser-action@v7
with:
# either 'goreleaser' (default) or 'goreleaser-pro'
distribution: goreleaser

View File

@ -9,7 +9,7 @@ on:
- master
schedule:
# Run daily at 00:00 UTC
- cron: '0 0 * * *'
- cron: "0 0 * * *"
workflow_dispatch: # Allow manual trigger
permissions:
@ -27,30 +27,30 @@ jobs:
fetch-depth: 0
- name: Run Trivy vulnerability scanner (source code)
uses: aquasecurity/trivy-action@0.33.1
uses: aquasecurity/trivy-action@0.34.1
with:
scan-type: 'fs'
scan-ref: '.'
scanners: 'vuln,secret,misconfig'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH,MEDIUM'
scan-type: "fs"
scan-ref: "."
scanners: "vuln,secret,misconfig"
format: "sarif"
output: "trivy-results.sarif"
severity: "CRITICAL,HIGH,MEDIUM"
ignore-unfixed: true
- name: Upload Trivy results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v4
if: always()
with:
sarif_file: 'trivy-results.sarif'
sarif_file: "trivy-results.sarif"
- name: Run Trivy scanner (table output for logs)
uses: aquasecurity/trivy-action@0.33.1
uses: aquasecurity/trivy-action@0.34.1
if: always()
with:
scan-type: 'fs'
scan-ref: '.'
scanners: 'vuln,secret,misconfig'
format: 'table'
severity: 'CRITICAL,HIGH,MEDIUM'
scan-type: "fs"
scan-ref: "."
scanners: "vuln,secret,misconfig"
format: "table"
severity: "CRITICAL,HIGH,MEDIUM"
ignore-unfixed: true
exit-code: '1'
exit-code: "1"

View File

@ -1,74 +1,123 @@
# Gin ChangeLog
## Gin v1.12.0
### Features
- feat(render): add bson protocol ([#4145](https://github.com/gin-gonic/gin/pull/4145))
- feat(context): add GetError and GetErrorSlice methods for error retrieval ([#4502](https://github.com/gin-gonic/gin/pull/4502))
- feat(binding): add support for encoding.UnmarshalText in uri/query binding ([#4203](https://github.com/gin-gonic/gin/pull/4203))
- feat(gin): add option to use escaped path ([#4420](https://github.com/gin-gonic/gin/pull/4420))
- feat(context): add Protocol Buffers support to content negotiation ([#4423](https://github.com/gin-gonic/gin/pull/4423))
- feat(context): implemented Delete method ([#38e7651](https://github.com/gin-gonic/gin/commit/38e7651))
- feat(logger): color latency ([#4146](https://github.com/gin-gonic/gin/pull/4146))
### Enhancements
- perf(tree): reduce allocations in findCaseInsensitivePath ([#4417](https://github.com/gin-gonic/gin/pull/4417))
- perf(recovery): optimize line reading in stack function ([#4466](https://github.com/gin-gonic/gin/pull/4466))
- perf(path): replace regex with custom functions in redirectTrailingSlash ([#4414](https://github.com/gin-gonic/gin/pull/4414))
- perf(tree): optimize path parsing using strings.Count ([#4246](https://github.com/gin-gonic/gin/pull/4246))
- chore(logger): allow skipping query string output ([#4547](https://github.com/gin-gonic/gin/pull/4547))
- chore(context): always trust xff headers from unix socket ([#3359](https://github.com/gin-gonic/gin/pull/3359))
- chore(response): prevent Flush() panic when the underlying ResponseWriter does not implement `http.Flusher` ([#4479](https://github.com/gin-gonic/gin/pull/4479))
- refactor(recovery): smart error comparison ([#4142](https://github.com/gin-gonic/gin/pull/4142))
- refactor(context): replace hardcoded localhost IPs with constants ([#4481](https://github.com/gin-gonic/gin/pull/4481))
- refactor(utils): move util functions to utils.go ([#4467](https://github.com/gin-gonic/gin/pull/4467))
- refactor(binding): use maps.Copy for cleaner map handling ([#4352](https://github.com/gin-gonic/gin/pull/4352))
- refactor(context): using maps.Clone ([#4333](https://github.com/gin-gonic/gin/pull/4333))
- refactor(ginS): use sync.OnceValue to simplify engine function ([#4314](https://github.com/gin-gonic/gin/pull/4314))
- refactor: replace magic numbers with named constants in bodyAllowedForStatus ([#4529](https://github.com/gin-gonic/gin/pull/4529))
- refactor: for loop can be modernized using range over int ([#4392](https://github.com/gin-gonic/gin/pull/4392))
### Bug Fixes
- fix(tree): panic in findCaseInsensitivePathRec with RedirectFixedPath ([#4535](https://github.com/gin-gonic/gin/pull/4535))
- fix(render): write content length in Data.Render ([#4206](https://github.com/gin-gonic/gin/pull/4206))
- fix(context): ClientIP handling for multiple X-Forwarded-For header values ([#4472](https://github.com/gin-gonic/gin/pull/4472))
- fix(binding): empty value error ([#2169](https://github.com/gin-gonic/gin/pull/2169))
- fix(recover): suppress http.ErrAbortHandler in recover ([#4336](https://github.com/gin-gonic/gin/pull/4336))
- fix(gin): literal colon routes not working with engine.Handler() ([#4415](https://github.com/gin-gonic/gin/pull/4415))
- fix(gin): close os.File in RunFd to prevent resource leak ([#4422](https://github.com/gin-gonic/gin/pull/4422))
- fix(response): refine hijack behavior for response lifecycle ([#4373](https://github.com/gin-gonic/gin/pull/4373))
- fix(binding): improve empty slice/array handling in form binding ([#4380](https://github.com/gin-gonic/gin/pull/4380))
- fix(debug): version mismatch ([#4403](https://github.com/gin-gonic/gin/pull/4403))
- fix: correct typos, improve documentation clarity, and remove dead code ([#4511](https://github.com/gin-gonic/gin/pull/4511))
### Build process updates / CI
- ci: update Go version support to 1.25+ across CI and docs ([#4550](https://github.com/gin-gonic/gin/pull/4550))
- chore(binding): upgrade bson dependency to mongo-driver v2 ([#4549](https://github.com/gin-gonic/gin/pull/4549))
## Gin v1.11.0
### Features
* feat(gin): Experimental support for HTTP/3 using quic-go/quic-go ([#3210](https://github.com/gin-gonic/gin/pull/3210))
* feat(form): add array collection format in form binding ([#3986](https://github.com/gin-gonic/gin/pull/3986)), add custom string slice for form tag unmarshal ([#3970](https://github.com/gin-gonic/gin/pull/3970))
* feat(binding): add BindPlain ([#3904](https://github.com/gin-gonic/gin/pull/3904))
* feat(fs): Export, test and document OnlyFilesFS ([#3939](https://github.com/gin-gonic/gin/pull/3939))
* feat(binding): add support for unixMilli and unixMicro ([#4190](https://github.com/gin-gonic/gin/pull/4190))
* feat(form): Support default values for collections in form binding ([#4048](https://github.com/gin-gonic/gin/pull/4048))
* feat(context): GetXxx added support for more go native types ([#3633](https://github.com/gin-gonic/gin/pull/3633))
- feat(gin): Experimental support for HTTP/3 using quic-go/quic-go ([#3210](https://github.com/gin-gonic/gin/pull/3210))
- feat(form): add array collection format in form binding ([#3986](https://github.com/gin-gonic/gin/pull/3986)), add custom string slice for form tag unmarshal ([#3970](https://github.com/gin-gonic/gin/pull/3970))
- feat(binding): add BindPlain ([#3904](https://github.com/gin-gonic/gin/pull/3904))
- feat(fs): Export, test and document OnlyFilesFS ([#3939](https://github.com/gin-gonic/gin/pull/3939))
- feat(binding): add support for unixMilli and unixMicro ([#4190](https://github.com/gin-gonic/gin/pull/4190))
- feat(form): Support default values for collections in form binding ([#4048](https://github.com/gin-gonic/gin/pull/4048))
- feat(context): GetXxx added support for more go native types ([#3633](https://github.com/gin-gonic/gin/pull/3633))
### Enhancements
* perf(context): optimize getMapFromFormData performance ([#4339](https://github.com/gin-gonic/gin/pull/4339))
* refactor(tree): replace string(/) with "/" in node.insertChild ([#4354](https://github.com/gin-gonic/gin/pull/4354))
* refactor(render): remove headers parameter from writeHeader ([#4353](https://github.com/gin-gonic/gin/pull/4353))
* refactor(context): simplify "GetType()" functions ([#4080](https://github.com/gin-gonic/gin/pull/4080))
* refactor(slice): simplify SliceValidationError Error method ([#3910](https://github.com/gin-gonic/gin/pull/3910))
* refactor(context):Avoid using filepath.Dir twice in SaveUploadedFile ([#4181](https://github.com/gin-gonic/gin/pull/4181))
* refactor(context): refactor context handling and improve test robustness ([#4066](https://github.com/gin-gonic/gin/pull/4066))
* refactor(binding): use strings.Cut to replace strings.Index ([#3522](https://github.com/gin-gonic/gin/pull/3522))
* refactor(context): add an optional permission parameter to SaveUploadedFile ([#4068](https://github.com/gin-gonic/gin/pull/4068))
* refactor(context): verify URL is Non-nil in initQueryCache() ([#3969](https://github.com/gin-gonic/gin/pull/3969))
* refactor(context): YAML judgment logic in Negotiate ([#3966](https://github.com/gin-gonic/gin/pull/3966))
* tree: replace the self-defined 'min' to official one ([#3975](https://github.com/gin-gonic/gin/pull/3975))
* context: Remove redundant filepath.Dir usage ([#4181](https://github.com/gin-gonic/gin/pull/4181))
- perf(context): optimize getMapFromFormData performance ([#4339](https://github.com/gin-gonic/gin/pull/4339))
- refactor(tree): replace string(/) with "/" in node.insertChild ([#4354](https://github.com/gin-gonic/gin/pull/4354))
- refactor(render): remove headers parameter from writeHeader ([#4353](https://github.com/gin-gonic/gin/pull/4353))
- refactor(context): simplify "GetType()" functions ([#4080](https://github.com/gin-gonic/gin/pull/4080))
- refactor(slice): simplify SliceValidationError Error method ([#3910](https://github.com/gin-gonic/gin/pull/3910))
- refactor(context):Avoid using filepath.Dir twice in SaveUploadedFile ([#4181](https://github.com/gin-gonic/gin/pull/4181))
- refactor(context): refactor context handling and improve test robustness ([#4066](https://github.com/gin-gonic/gin/pull/4066))
- refactor(binding): use strings.Cut to replace strings.Index ([#3522](https://github.com/gin-gonic/gin/pull/3522))
- refactor(context): add an optional permission parameter to SaveUploadedFile ([#4068](https://github.com/gin-gonic/gin/pull/4068))
- refactor(context): verify URL is Non-nil in initQueryCache() ([#3969](https://github.com/gin-gonic/gin/pull/3969))
- refactor(context): YAML judgment logic in Negotiate ([#3966](https://github.com/gin-gonic/gin/pull/3966))
- tree: replace the self-defined 'min' to official one ([#3975](https://github.com/gin-gonic/gin/pull/3975))
- context: Remove redundant filepath.Dir usage ([#4181](https://github.com/gin-gonic/gin/pull/4181))
### Bug Fixes
* fix: prevent middleware re-entry issue in HandleContext ([#3987](https://github.com/gin-gonic/gin/pull/3987))
* fix(binding): prevent duplicate decoding and add validation in decodeToml ([#4193](https://github.com/gin-gonic/gin/pull/4193))
* fix(gin): Do not panic when handling method not allowed on empty tree ([#4003](https://github.com/gin-gonic/gin/pull/4003))
* fix(gin): data race warning for gin mode ([#1580](https://github.com/gin-gonic/gin/pull/1580))
* fix(context): verify URL is Non-nil in initQueryCache() ([#3969](https://github.com/gin-gonic/gin/pull/3969))
* fix(context): YAML judgment logic in Negotiate ([#3966](https://github.com/gin-gonic/gin/pull/3966))
* fix(context): check handler is nil ([#3413](https://github.com/gin-gonic/gin/pull/3413))
* fix(readme): fix broken link to English documentation ([#4222](https://github.com/gin-gonic/gin/pull/4222))
* fix(tree): Keep panic infos consistent when wildcard type build faild ([#4077](https://github.com/gin-gonic/gin/pull/4077))
- fix: prevent middleware re-entry issue in HandleContext ([#3987](https://github.com/gin-gonic/gin/pull/3987))
- fix(binding): prevent duplicate decoding and add validation in decodeToml ([#4193](https://github.com/gin-gonic/gin/pull/4193))
- fix(gin): Do not panic when handling method not allowed on empty tree ([#4003](https://github.com/gin-gonic/gin/pull/4003))
- fix(gin): data race warning for gin mode ([#1580](https://github.com/gin-gonic/gin/pull/1580))
- fix(context): verify URL is Non-nil in initQueryCache() ([#3969](https://github.com/gin-gonic/gin/pull/3969))
- fix(context): YAML judgment logic in Negotiate ([#3966](https://github.com/gin-gonic/gin/pull/3966))
- fix(context): check handler is nil ([#3413](https://github.com/gin-gonic/gin/pull/3413))
- fix(readme): fix broken link to English documentation ([#4222](https://github.com/gin-gonic/gin/pull/4222))
- fix(tree): Keep panic infos consistent when wildcard type build faild ([#4077](https://github.com/gin-gonic/gin/pull/4077))
### Build process updates / CI
* ci: integrate Trivy vulnerability scanning into CI workflow ([#4359](https://github.com/gin-gonic/gin/pull/4359))
* ci: support Go 1.25 in CI/CD ([#4341](https://github.com/gin-gonic/gin/pull/4341))
* build(deps): upgrade github.com/bytedance/sonic from v1.13.2 to v1.14.0 ([#4342](https://github.com/gin-gonic/gin/pull/4342))
* ci: add Go version 1.24 to GitHub Actions ([#4154](https://github.com/gin-gonic/gin/pull/4154))
* build: update Gin minimum Go version to 1.21 ([#3960](https://github.com/gin-gonic/gin/pull/3960))
* ci(lint): enable new linters (testifylint, usestdlibvars, perfsprint, etc.) ([#4010](https://github.com/gin-gonic/gin/pull/4010), [#4091](https://github.com/gin-gonic/gin/pull/4091), [#4090](https://github.com/gin-gonic/gin/pull/4090))
* ci(lint): update workflows and improve test request consistency ([#4126](https://github.com/gin-gonic/gin/pull/4126))
- ci: integrate Trivy vulnerability scanning into CI workflow ([#4359](https://github.com/gin-gonic/gin/pull/4359))
- ci: support Go 1.25 in CI/CD ([#4341](https://github.com/gin-gonic/gin/pull/4341))
- build(deps): upgrade github.com/bytedance/sonic from v1.13.2 to v1.14.0 ([#4342](https://github.com/gin-gonic/gin/pull/4342))
- ci: add Go version 1.24 to GitHub Actions ([#4154](https://github.com/gin-gonic/gin/pull/4154))
- build: update Gin minimum Go version to 1.21 ([#3960](https://github.com/gin-gonic/gin/pull/3960))
- ci(lint): enable new linters (testifylint, usestdlibvars, perfsprint, etc.) ([#4010](https://github.com/gin-gonic/gin/pull/4010), [#4091](https://github.com/gin-gonic/gin/pull/4091), [#4090](https://github.com/gin-gonic/gin/pull/4090))
- ci(lint): update workflows and improve test request consistency ([#4126](https://github.com/gin-gonic/gin/pull/4126))
### Dependency updates
* chore(deps): bump google.golang.org/protobuf from 1.36.6 to 1.36.9 ([#4346](https://github.com/gin-gonic/gin/pull/4346), [#4356](https://github.com/gin-gonic/gin/pull/4356))
* chore(deps): bump github.com/stretchr/testify from 1.10.0 to 1.11.1 ([#4347](https://github.com/gin-gonic/gin/pull/4347))
* chore(deps): bump actions/setup-go from 5 to 6 ([#4351](https://github.com/gin-gonic/gin/pull/4351))
* chore(deps): bump github.com/quic-go/quic-go from 0.53.0 to 0.54.0 ([#4328](https://github.com/gin-gonic/gin/pull/4328))
* chore(deps): bump golang.org/x/net from 0.33.0 to 0.38.0 ([#4178](https://github.com/gin-gonic/gin/pull/4178), [#4221](https://github.com/gin-gonic/gin/pull/4221))
* chore(deps): bump github.com/go-playground/validator/v10 from 10.20.0 to 10.22.1 ([#4052](https://github.com/gin-gonic/gin/pull/4052))
- chore(deps): bump google.golang.org/protobuf from 1.36.6 to 1.36.9 ([#4346](https://github.com/gin-gonic/gin/pull/4346), [#4356](https://github.com/gin-gonic/gin/pull/4356))
- chore(deps): bump github.com/stretchr/testify from 1.10.0 to 1.11.1 ([#4347](https://github.com/gin-gonic/gin/pull/4347))
- chore(deps): bump actions/setup-go from 5 to 6 ([#4351](https://github.com/gin-gonic/gin/pull/4351))
- chore(deps): bump github.com/quic-go/quic-go from 0.53.0 to 0.54.0 ([#4328](https://github.com/gin-gonic/gin/pull/4328))
- chore(deps): bump golang.org/x/net from 0.33.0 to 0.38.0 ([#4178](https://github.com/gin-gonic/gin/pull/4178), [#4221](https://github.com/gin-gonic/gin/pull/4221))
- chore(deps): bump github.com/go-playground/validator/v10 from 10.20.0 to 10.22.1 ([#4052](https://github.com/gin-gonic/gin/pull/4052))
### Documentation updates
* docs(changelog): update release notes for Gin v1.10.1 ([#4360](https://github.com/gin-gonic/gin/pull/4360))
* docs: Fixing English grammar mistakes and awkward sentence structure in doc/doc.md ([#4207](https://github.com/gin-gonic/gin/pull/4207))
* docs: update documentation and release notes for Gin v1.10.0 ([#3953](https://github.com/gin-gonic/gin/pull/3953))
* docs: fix typo in Gin Quick Start ([#3997](https://github.com/gin-gonic/gin/pull/3997))
* docs: fix comment and link issues ([#4205](https://github.com/gin-gonic/gin/pull/4205), [#3938](https://github.com/gin-gonic/gin/pull/3938))
* docs: fix route group example code ([#4020](https://github.com/gin-gonic/gin/pull/4020))
* docs(readme): add Portuguese documentation ([#4078](https://github.com/gin-gonic/gin/pull/4078))
* docs(context): fix some function names in comment ([#4079](https://github.com/gin-gonic/gin/pull/4079))
- docs(changelog): update release notes for Gin v1.10.1 ([#4360](https://github.com/gin-gonic/gin/pull/4360))
- docs: Fixing English grammar mistakes and awkward sentence structure in doc/doc.md ([#4207](https://github.com/gin-gonic/gin/pull/4207))
- docs: update documentation and release notes for Gin v1.10.0 ([#3953](https://github.com/gin-gonic/gin/pull/3953))
- docs: fix typo in Gin Quick Start ([#3997](https://github.com/gin-gonic/gin/pull/3997))
- docs: fix comment and link issues ([#4205](https://github.com/gin-gonic/gin/pull/4205), [#3938](https://github.com/gin-gonic/gin/pull/3938))
- docs: fix route group example code ([#4020](https://github.com/gin-gonic/gin/pull/4020))
- docs(readme): add Portuguese documentation ([#4078](https://github.com/gin-gonic/gin/pull/4078))
- docs(context): fix some function names in comment ([#4079](https://github.com/gin-gonic/gin/pull/4079))
---
@ -76,377 +125,377 @@
### Features
* refactor: strengthen HTTPS security and improve code organization
* feat(binding): Support custom BindUnmarshaler for binding. (#3933)
- refactor: strengthen HTTPS security and improve code organization
- feat(binding): Support custom BindUnmarshaler for binding. (#3933)
### Enhancements
* chore(deps): bump github.com/bytedance/sonic from 1.11.3 to 1.11.6 (#3940)
* chore(deps): bump golangci/golangci-lint-action from 4 to 5 (#3941)
* chore: update external dependencies to latest versions (#3950)
* chore: update various Go dependencies to latest versions (#3901)
* chore: refactor configuration files for better readability (#3951)
* chore: update changelog categories and improve documentation (#3917)
* feat: update version constant to v1.10.0 (#3952)
- chore(deps): bump github.com/bytedance/sonic from 1.11.3 to 1.11.6 (#3940)
- chore(deps): bump golangci/golangci-lint-action from 4 to 5 (#3941)
- chore: update external dependencies to latest versions (#3950)
- chore: update various Go dependencies to latest versions (#3901)
- chore: refactor configuration files for better readability (#3951)
- chore: update changelog categories and improve documentation (#3917)
- feat: update version constant to v1.10.0 (#3952)
### Build process updates
* ci(release): refactor changelog regex patterns and exclusions (#3914)
* ci(Makefile): vet command add .PHONY (#3915)
- ci(release): refactor changelog regex patterns and exclusions (#3914)
- ci(Makefile): vet command add .PHONY (#3915)
## Gin v1.10.0
### Features
* feat(auth): add proxy-server authentication (#3877) (@EndlessParadox1)
* feat(bind): ShouldBindBodyWith shortcut and change doc (#3871) (@RedCrazyGhost)
* feat(binding): Support custom BindUnmarshaler for binding. (#3933) (@dkkb)
* feat(binding): support override default binding implement (#3514) (@ssfyn)
* feat(engine): Added `OptionFunc` and `With` (#3572) (@flc1125)
* feat(logger): ability to skip logs based on user-defined logic (#3593) (@palvaneh)
- feat(auth): add proxy-server authentication (#3877) (@EndlessParadox1)
- feat(bind): ShouldBindBodyWith shortcut and change doc (#3871) (@RedCrazyGhost)
- feat(binding): Support custom BindUnmarshaler for binding. (#3933) (@dkkb)
- feat(binding): support override default binding implement (#3514) (@ssfyn)
- feat(engine): Added `OptionFunc` and `With` (#3572) (@flc1125)
- feat(logger): ability to skip logs based on user-defined logic (#3593) (@palvaneh)
### Bug fixes
* Revert "fix(uri): query binding bug (#3236)" (#3899) (@appleboy)
* fix(binding): binding error while not upload file (#3819) (#3820) (@clearcodecn)
* fix(binding): dereference pointer to struct (#3199) (@echovl)
* fix(context): make context Value method adhere to Go standards (#3897) (@FarmerChillax)
* fix(engine): fix unit test (#3878) (@flc1125)
* fix(header): Allow header according to RFC 7231 (HTTP 405) (#3759) (@Crocmagnon)
* fix(route): Add fullPath in context copy (#3784) (@KarthikReddyPuli)
* fix(router): catch-all conflicting wildcard (#3812) (@FirePing32)
* fix(sec): upgrade golang.org/x/crypto to 0.17.0 (#3832) (@chncaption)
* fix(tree): correctly expand the capacity of params (#3502) (@georgijd-form3)
* fix(uri): query binding bug (#3236) (@illiafox)
* fix: Add pointer support for url query params (#3659) (#3666) (@omkar-foss)
* fix: protect Context.Keys map when call Copy method (#3873) (@kingcanfish)
- Revert "fix(uri): query binding bug (#3236)" (#3899) (@appleboy)
- fix(binding): binding error while not upload file (#3819) (#3820) (@clearcodecn)
- fix(binding): dereference pointer to struct (#3199) (@echovl)
- fix(context): make context Value method adhere to Go standards (#3897) (@FarmerChillax)
- fix(engine): fix unit test (#3878) (@flc1125)
- fix(header): Allow header according to RFC 7231 (HTTP 405) (#3759) (@Crocmagnon)
- fix(route): Add fullPath in context copy (#3784) (@KarthikReddyPuli)
- fix(router): catch-all conflicting wildcard (#3812) (@FirePing32)
- fix(sec): upgrade golang.org/x/crypto to 0.17.0 (#3832) (@chncaption)
- fix(tree): correctly expand the capacity of params (#3502) (@georgijd-form3)
- fix(uri): query binding bug (#3236) (@illiafox)
- fix: Add pointer support for url query params (#3659) (#3666) (@omkar-foss)
- fix: protect Context.Keys map when call Copy method (#3873) (@kingcanfish)
### Enhancements
* chore(CI): update release args (#3595) (@qloog)
* chore(IP): add TrustedPlatform constant for Fly.io. (#3839) (@ab)
* chore(debug): add ability to override the debugPrint statement (#2337) (@josegonzalez)
* chore(deps): update dependencies to latest versions (#3835) (@appleboy)
* chore(header): Add support for RFC 9512: application/yaml (#3851) (@vincentbernat)
* chore(http): use white color for HTTP 1XX (#3741) (@viralparmarme)
* chore(optimize): the ShouldBindUri method of the Context struct (#3911) (@1911860538)
* chore(perf): Optimize the Copy method of the Context struct (#3859) (@1911860538)
* chore(refactor): modify interface check way (#3855) (@demoManito)
* chore(request): check reader if it's nil before reading (#3419) (@noahyao1024)
* chore(security): upgrade Protobuf for CVE-2024-24786 (#3893) (@Fotkurz)
* chore: refactor CI and update dependencies (#3848) (@appleboy)
* chore: refactor configuration files for better readability (#3951) (@appleboy)
* chore: update GitHub Actions configuration (#3792) (@appleboy)
* chore: update changelog categories and improve documentation (#3917) (@appleboy)
* chore: update dependencies to latest versions (#3694) (@appleboy)
* chore: update external dependencies to latest versions (#3950) (@appleboy)
* chore: update various Go dependencies to latest versions (#3901) (@appleboy)
- chore(CI): update release args (#3595) (@qloog)
- chore(IP): add TrustedPlatform constant for Fly.io. (#3839) (@ab)
- chore(debug): add ability to override the debugPrint statement (#2337) (@josegonzalez)
- chore(deps): update dependencies to latest versions (#3835) (@appleboy)
- chore(header): Add support for RFC 9512: application/yaml (#3851) (@vincentbernat)
- chore(http): use white color for HTTP 1XX (#3741) (@viralparmarme)
- chore(optimize): the ShouldBindUri method of the Context struct (#3911) (@1911860538)
- chore(perf): Optimize the Copy method of the Context struct (#3859) (@1911860538)
- chore(refactor): modify interface check way (#3855) (@demoManito)
- chore(request): check reader if it's nil before reading (#3419) (@noahyao1024)
- chore(security): upgrade Protobuf for CVE-2024-24786 (#3893) (@Fotkurz)
- chore: refactor CI and update dependencies (#3848) (@appleboy)
- chore: refactor configuration files for better readability (#3951) (@appleboy)
- chore: update GitHub Actions configuration (#3792) (@appleboy)
- chore: update changelog categories and improve documentation (#3917) (@appleboy)
- chore: update dependencies to latest versions (#3694) (@appleboy)
- chore: update external dependencies to latest versions (#3950) (@appleboy)
- chore: update various Go dependencies to latest versions (#3901) (@appleboy)
### Build process updates
* build(codecov): Added a codecov configuration (#3891) (@flc1125)
* ci(Makefile): vet command add .PHONY (#3915) (@imalasong)
* ci(lint): update tooling and workflows for consistency (#3834) (@appleboy)
* ci(release): refactor changelog regex patterns and exclusions (#3914) (@appleboy)
* ci(testing): add go1.22 version (#3842) (@appleboy)
- build(codecov): Added a codecov configuration (#3891) (@flc1125)
- ci(Makefile): vet command add .PHONY (#3915) (@imalasong)
- ci(lint): update tooling and workflows for consistency (#3834) (@appleboy)
- ci(release): refactor changelog regex patterns and exclusions (#3914) (@appleboy)
- ci(testing): add go1.22 version (#3842) (@appleboy)
### Documentation updates
* docs(context): Added deprecation comments to BindWith (#3880) (@flc1125)
* docs(middleware): comments to function `BasicAuthForProxy` (#3881) (@EndlessParadox1)
* docs: Add document to constant `AuthProxyUserKey` and `BasicAuthForProxy`. (#3887) (@EndlessParadox1)
* docs: fix typo in comment (#3868) (@testwill)
* docs: fix typo in function documentation (#3872) (@TotomiEcio)
* docs: remove redundant comments (#3765) (@WeiTheShinobi)
* feat: update version constant to v1.10.0 (#3952) (@appleboy)
- docs(context): Added deprecation comments to BindWith (#3880) (@flc1125)
- docs(middleware): comments to function `BasicAuthForProxy` (#3881) (@EndlessParadox1)
- docs: Add document to constant `AuthProxyUserKey` and `BasicAuthForProxy`. (#3887) (@EndlessParadox1)
- docs: fix typo in comment (#3868) (@testwill)
- docs: fix typo in function documentation (#3872) (@TotomiEcio)
- docs: remove redundant comments (#3765) (@WeiTheShinobi)
- feat: update version constant to v1.10.0 (#3952) (@appleboy)
### Others
* Upgrade golang.org/x/net -> v0.13.0 (#3684) (@cpcf)
* test(git): gitignore add develop tools (#3370) (@demoManito)
* test(http): use constant instead of numeric literal (#3863) (@testwill)
* test(path): Optimize unit test execution results (#3883) (@flc1125)
* test(render): increased unit tests coverage (#3691) (@araujo88)
- Upgrade golang.org/x/net -> v0.13.0 (#3684) (@cpcf)
- test(git): gitignore add develop tools (#3370) (@demoManito)
- test(http): use constant instead of numeric literal (#3863) (@testwill)
- test(path): Optimize unit test execution results (#3883) (@flc1125)
- test(render): increased unit tests coverage (#3691) (@araujo88)
## Gin v1.9.1
### BUG FIXES
* fix Request.Context() checks [#3512](https://github.com/gin-gonic/gin/pull/3512)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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
### BUG FIXES
* 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(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)>))
### 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)>))
## Gin v1.8.1
### ENHANCEMENTS
* feat(context): add ContextWithFallback feature flag [#3172](https://github.com/gin-gonic/gin/pull/3172)
- feat(context): add ContextWithFallback feature flag [#3172](https://github.com/gin-gonic/gin/pull/3172)
## Gin v1.8.0
### 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`
* gin.Context with fallback value from gin.Context.Request.Context() [#2751](https://github.com/gin-gonic/gin/pull/2751)
- 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)
### BUG FIXES
* Fixed SetOutput() panics on go 1.17 [#2861](https://github.com/gin-gonic/gin/pull/2861)
* Fix: wrong when wildcard follows named param [#2983](https://github.com/gin-gonic/gin/pull/2983)
* Fix: missing sameSite when do context.reset() [#3123](https://github.com/gin-gonic/gin/pull/3123)
- Fixed SetOutput() panics on go 1.17 [#2861](https://github.com/gin-gonic/gin/pull/2861)
- Fix: wrong when wildcard follows named param [#2983](https://github.com/gin-gonic/gin/pull/2983)
- Fix: missing sameSite when do context.reset() [#3123](https://github.com/gin-gonic/gin/pull/3123)
### ENHANCEMENTS
* Use Header() instead of deprecated HeaderMap [#2694](https://github.com/gin-gonic/gin/pull/2694)
* RouterGroup.Handle regular match optimization of http method [#2685](https://github.com/gin-gonic/gin/pull/2685)
* Add support go-json, another drop-in json replacement [#2680](https://github.com/gin-gonic/gin/pull/2680)
* Use errors.New to replace fmt.Errorf will much better [#2707](https://github.com/gin-gonic/gin/pull/2707)
* Use Duration.Truncate for truncating precision [#2711](https://github.com/gin-gonic/gin/pull/2711)
* Get client IP when using Cloudflare [#2723](https://github.com/gin-gonic/gin/pull/2723)
* Optimize code adjust [#2700](https://github.com/gin-gonic/gin/pull/2700/files)
* Optimize code and reduce code cyclomatic complexity [#2737](https://github.com/gin-gonic/gin/pull/2737)
* Improve sliceValidateError.Error performance [#2765](https://github.com/gin-gonic/gin/pull/2765)
* Support custom struct tag [#2720](https://github.com/gin-gonic/gin/pull/2720)
* Improve router group tests [#2787](https://github.com/gin-gonic/gin/pull/2787)
* Fallback Context.Deadline() Context.Done() Context.Err() to Context.Request.Context() [#2769](https://github.com/gin-gonic/gin/pull/2769)
* Some codes optimize [#2830](https://github.com/gin-gonic/gin/pull/2830) [#2834](https://github.com/gin-gonic/gin/pull/2834) [#2838](https://github.com/gin-gonic/gin/pull/2838) [#2837](https://github.com/gin-gonic/gin/pull/2837) [#2788](https://github.com/gin-gonic/gin/pull/2788) [#2848](https://github.com/gin-gonic/gin/pull/2848) [#2851](https://github.com/gin-gonic/gin/pull/2851) [#2701](https://github.com/gin-gonic/gin/pull/2701)
* TrustedProxies: Add default IPv6 support and refactor [#2967](https://github.com/gin-gonic/gin/pull/2967)
* Test(route): expose performRequest func [#3012](https://github.com/gin-gonic/gin/pull/3012)
* Support h2c with prior knowledge [#1398](https://github.com/gin-gonic/gin/pull/1398)
* Feat attachment filename support utf8 [#3071](https://github.com/gin-gonic/gin/pull/3071)
* Feat: add StaticFileFS [#2749](https://github.com/gin-gonic/gin/pull/2749)
* Feat(context): return GIN Context from Value method [#2825](https://github.com/gin-gonic/gin/pull/2825)
* Feat: automatically SetMode to TestMode when run go test [#3139](https://github.com/gin-gonic/gin/pull/3139)
* Add TOML bining for gin [#3081](https://github.com/gin-gonic/gin/pull/3081)
* IPv6 add default trusted proxies [#3033](https://github.com/gin-gonic/gin/pull/3033)
- Use Header() instead of deprecated HeaderMap [#2694](https://github.com/gin-gonic/gin/pull/2694)
- RouterGroup.Handle regular match optimization of http method [#2685](https://github.com/gin-gonic/gin/pull/2685)
- Add support go-json, another drop-in json replacement [#2680](https://github.com/gin-gonic/gin/pull/2680)
- Use errors.New to replace fmt.Errorf will much better [#2707](https://github.com/gin-gonic/gin/pull/2707)
- Use Duration.Truncate for truncating precision [#2711](https://github.com/gin-gonic/gin/pull/2711)
- Get client IP when using Cloudflare [#2723](https://github.com/gin-gonic/gin/pull/2723)
- Optimize code adjust [#2700](https://github.com/gin-gonic/gin/pull/2700/files)
- Optimize code and reduce code cyclomatic complexity [#2737](https://github.com/gin-gonic/gin/pull/2737)
- Improve sliceValidateError.Error performance [#2765](https://github.com/gin-gonic/gin/pull/2765)
- Support custom struct tag [#2720](https://github.com/gin-gonic/gin/pull/2720)
- Improve router group tests [#2787](https://github.com/gin-gonic/gin/pull/2787)
- Fallback Context.Deadline() Context.Done() Context.Err() to Context.Request.Context() [#2769](https://github.com/gin-gonic/gin/pull/2769)
- Some codes optimize [#2830](https://github.com/gin-gonic/gin/pull/2830) [#2834](https://github.com/gin-gonic/gin/pull/2834) [#2838](https://github.com/gin-gonic/gin/pull/2838) [#2837](https://github.com/gin-gonic/gin/pull/2837) [#2788](https://github.com/gin-gonic/gin/pull/2788) [#2848](https://github.com/gin-gonic/gin/pull/2848) [#2851](https://github.com/gin-gonic/gin/pull/2851) [#2701](https://github.com/gin-gonic/gin/pull/2701)
- TrustedProxies: Add default IPv6 support and refactor [#2967](https://github.com/gin-gonic/gin/pull/2967)
- Test(route): expose performRequest func [#3012](https://github.com/gin-gonic/gin/pull/3012)
- Support h2c with prior knowledge [#1398](https://github.com/gin-gonic/gin/pull/1398)
- Feat attachment filename support utf8 [#3071](https://github.com/gin-gonic/gin/pull/3071)
- Feat: add StaticFileFS [#2749](https://github.com/gin-gonic/gin/pull/2749)
- Feat(context): return GIN Context from Value method [#2825](https://github.com/gin-gonic/gin/pull/2825)
- Feat: automatically SetMode to TestMode when run go test [#3139](https://github.com/gin-gonic/gin/pull/3139)
- Add TOML bining for gin [#3081](https://github.com/gin-gonic/gin/pull/3081)
- IPv6 add default trusted proxies [#3033](https://github.com/gin-gonic/gin/pull/3033)
### DOCS
* Add note about nomsgpack tag to the readme [#2703](https://github.com/gin-gonic/gin/pull/2703)
- Add note about nomsgpack tag to the readme [#2703](https://github.com/gin-gonic/gin/pull/2703)
## Gin v1.7.7
### BUG FIXES
* Fixed X-Forwarded-For unsafe handling of CVE-2020-28483 [#2844](https://github.com/gin-gonic/gin/pull/2844), closed issue [#2862](https://github.com/gin-gonic/gin/issues/2862).
* Tree: updated the code logic for `latestNode` [#2897](https://github.com/gin-gonic/gin/pull/2897), closed issue [#2894](https://github.com/gin-gonic/gin/issues/2894) [#2878](https://github.com/gin-gonic/gin/issues/2878).
* Tree: fixed the misplacement of adding slashes [#2847](https://github.com/gin-gonic/gin/pull/2847), closed issue [#2843](https://github.com/gin-gonic/gin/issues/2843).
* Tree: fixed tsr with mixed static and wildcard paths [#2924](https://github.com/gin-gonic/gin/pull/2924), closed issue [#2918](https://github.com/gin-gonic/gin/issues/2918).
- Fixed X-Forwarded-For unsafe handling of CVE-2020-28483 [#2844](https://github.com/gin-gonic/gin/pull/2844), closed issue [#2862](https://github.com/gin-gonic/gin/issues/2862).
- Tree: updated the code logic for `latestNode` [#2897](https://github.com/gin-gonic/gin/pull/2897), closed issue [#2894](https://github.com/gin-gonic/gin/issues/2894) [#2878](https://github.com/gin-gonic/gin/issues/2878).
- Tree: fixed the misplacement of adding slashes [#2847](https://github.com/gin-gonic/gin/pull/2847), closed issue [#2843](https://github.com/gin-gonic/gin/issues/2843).
- Tree: fixed tsr with mixed static and wildcard paths [#2924](https://github.com/gin-gonic/gin/pull/2924), closed issue [#2918](https://github.com/gin-gonic/gin/issues/2918).
### ENHANCEMENTS
* TrustedProxies: make it backward-compatible [#2887](https://github.com/gin-gonic/gin/pull/2887), closed issue [#2819](https://github.com/gin-gonic/gin/issues/2819).
* TrustedPlatform: provide custom options for another CDN services [#2906](https://github.com/gin-gonic/gin/pull/2906).
- TrustedProxies: make it backward-compatible [#2887](https://github.com/gin-gonic/gin/pull/2887), closed issue [#2819](https://github.com/gin-gonic/gin/issues/2819).
- TrustedPlatform: provide custom options for another CDN services [#2906](https://github.com/gin-gonic/gin/pull/2906).
### DOCS
* NoMethod: added usage annotation ([#2832](https://github.com/gin-gonic/gin/pull/2832#issuecomment-929954463)).
- NoMethod: added usage annotation ([#2832](https://github.com/gin-gonic/gin/pull/2832#issuecomment-929954463)).
## Gin v1.7.6
### BUG FIXES
* bump new release to fix v1.7.5 release error by using v1.7.4 codes.
- bump new release to fix v1.7.5 release error by using v1.7.4 codes.
## Gin v1.7.4
### BUG FIXES
* bump new release to fix checksum mismatch
- bump new release to fix checksum mismatch
## Gin v1.7.3
### BUG FIXES
* fix level 1 router match [#2767](https://github.com/gin-gonic/gin/issues/2767), [#2796](https://github.com/gin-gonic/gin/issues/2796)
- fix level 1 router match [#2767](https://github.com/gin-gonic/gin/issues/2767), [#2796](https://github.com/gin-gonic/gin/issues/2796)
## Gin v1.7.2
### BUG FIXES
* Fix conflict between param and exact path [#2706](https://github.com/gin-gonic/gin/issues/2706). Close issue [#2682](https://github.com/gin-gonic/gin/issues/2682) [#2696](https://github.com/gin-gonic/gin/issues/2696).
- Fix conflict between param and exact path [#2706](https://github.com/gin-gonic/gin/issues/2706). Close issue [#2682](https://github.com/gin-gonic/gin/issues/2682) [#2696](https://github.com/gin-gonic/gin/issues/2696).
## Gin v1.7.1
### BUG FIXES
* fix: data race with trustedCIDRs from [#2674](https://github.com/gin-gonic/gin/issues/2674)([#2675](https://github.com/gin-gonic/gin/pull/2675))
- fix: data race with trustedCIDRs from [#2674](https://github.com/gin-gonic/gin/issues/2674)([#2675](https://github.com/gin-gonic/gin/pull/2675))
## Gin v1.7.0
### BUG FIXES
* fix compile error from [#2572](https://github.com/gin-gonic/gin/pull/2572) ([#2600](https://github.com/gin-gonic/gin/pull/2600))
* fix: print headers without Authorization header on broken pipe ([#2528](https://github.com/gin-gonic/gin/pull/2528))
* fix(tree): reassign fullpath when register new node ([#2366](https://github.com/gin-gonic/gin/pull/2366))
- fix compile error from [#2572](https://github.com/gin-gonic/gin/pull/2572) ([#2600](https://github.com/gin-gonic/gin/pull/2600))
- fix: print headers without Authorization header on broken pipe ([#2528](https://github.com/gin-gonic/gin/pull/2528))
- fix(tree): reassign fullpath when register new node ([#2366](https://github.com/gin-gonic/gin/pull/2366))
### ENHANCEMENTS
* Support params and exact routes without creating conflicts ([#2663](https://github.com/gin-gonic/gin/pull/2663))
* chore: improve render string performance ([#2365](https://github.com/gin-gonic/gin/pull/2365))
* Sync route tree to httprouter latest code ([#2368](https://github.com/gin-gonic/gin/pull/2368))
* chore: rename getQueryCache/getFormCache to initQueryCache/initFormCa ([#2375](https://github.com/gin-gonic/gin/pull/2375))
* chore(performance): improve countParams ([#2378](https://github.com/gin-gonic/gin/pull/2378))
* Remove some functions that have the same effect as the bytes package ([#2387](https://github.com/gin-gonic/gin/pull/2387))
* update:SetMode function ([#2321](https://github.com/gin-gonic/gin/pull/2321))
* remove an unused type SecureJSONPrefix ([#2391](https://github.com/gin-gonic/gin/pull/2391))
* Add a redirect sample for POST method ([#2389](https://github.com/gin-gonic/gin/pull/2389))
* Add CustomRecovery builtin middleware ([#2322](https://github.com/gin-gonic/gin/pull/2322))
* binding: avoid 2038 problem on 32-bit architectures ([#2450](https://github.com/gin-gonic/gin/pull/2450))
* Prevent panic in Context.GetQuery() when there is no Request ([#2412](https://github.com/gin-gonic/gin/pull/2412))
* Add GetUint and GetUint64 method on gin.context ([#2487](https://github.com/gin-gonic/gin/pull/2487))
* update content-disposition header to MIME-style ([#2512](https://github.com/gin-gonic/gin/pull/2512))
* reduce allocs and improve the render `WriteString` ([#2508](https://github.com/gin-gonic/gin/pull/2508))
* implement ".Unwrap() error" on Error type ([#2525](https://github.com/gin-gonic/gin/pull/2525)) ([#2526](https://github.com/gin-gonic/gin/pull/2526))
* Allow bind with a map[string]string ([#2484](https://github.com/gin-gonic/gin/pull/2484))
* chore: update tree ([#2371](https://github.com/gin-gonic/gin/pull/2371))
* Support binding for slice/array obj [Rewrite] ([#2302](https://github.com/gin-gonic/gin/pull/2302))
* basic auth: fix timing oracle ([#2609](https://github.com/gin-gonic/gin/pull/2609))
* Add mixed param and non-param paths (port of httprouter[#329](https://github.com/gin-gonic/gin/pull/329)) ([#2663](https://github.com/gin-gonic/gin/pull/2663))
* feat(engine): add trustedproxies and remoteIP ([#2632](https://github.com/gin-gonic/gin/pull/2632))
- Support params and exact routes without creating conflicts ([#2663](https://github.com/gin-gonic/gin/pull/2663))
- chore: improve render string performance ([#2365](https://github.com/gin-gonic/gin/pull/2365))
- Sync route tree to httprouter latest code ([#2368](https://github.com/gin-gonic/gin/pull/2368))
- chore: rename getQueryCache/getFormCache to initQueryCache/initFormCa ([#2375](https://github.com/gin-gonic/gin/pull/2375))
- chore(performance): improve countParams ([#2378](https://github.com/gin-gonic/gin/pull/2378))
- Remove some functions that have the same effect as the bytes package ([#2387](https://github.com/gin-gonic/gin/pull/2387))
- update:SetMode function ([#2321](https://github.com/gin-gonic/gin/pull/2321))
- remove an unused type SecureJSONPrefix ([#2391](https://github.com/gin-gonic/gin/pull/2391))
- Add a redirect sample for POST method ([#2389](https://github.com/gin-gonic/gin/pull/2389))
- Add CustomRecovery builtin middleware ([#2322](https://github.com/gin-gonic/gin/pull/2322))
- binding: avoid 2038 problem on 32-bit architectures ([#2450](https://github.com/gin-gonic/gin/pull/2450))
- Prevent panic in Context.GetQuery() when there is no Request ([#2412](https://github.com/gin-gonic/gin/pull/2412))
- Add GetUint and GetUint64 method on gin.context ([#2487](https://github.com/gin-gonic/gin/pull/2487))
- update content-disposition header to MIME-style ([#2512](https://github.com/gin-gonic/gin/pull/2512))
- reduce allocs and improve the render `WriteString` ([#2508](https://github.com/gin-gonic/gin/pull/2508))
- implement ".Unwrap() error" on Error type ([#2525](https://github.com/gin-gonic/gin/pull/2525)) ([#2526](https://github.com/gin-gonic/gin/pull/2526))
- Allow bind with a map[string]string ([#2484](https://github.com/gin-gonic/gin/pull/2484))
- chore: update tree ([#2371](https://github.com/gin-gonic/gin/pull/2371))
- Support binding for slice/array obj [Rewrite] ([#2302](https://github.com/gin-gonic/gin/pull/2302))
- basic auth: fix timing oracle ([#2609](https://github.com/gin-gonic/gin/pull/2609))
- Add mixed param and non-param paths (port of httprouter[#329](https://github.com/gin-gonic/gin/pull/329)) ([#2663](https://github.com/gin-gonic/gin/pull/2663))
- feat(engine): add trustedproxies and remoteIP ([#2632](https://github.com/gin-gonic/gin/pull/2632))
## Gin v1.6.3
### ENHANCEMENTS
* Improve performance: Change `*sync.RWMutex` to `sync.RWMutex` in context. [#2351](https://github.com/gin-gonic/gin/pull/2351)
- Improve performance: Change `*sync.RWMutex` to `sync.RWMutex` in context. [#2351](https://github.com/gin-gonic/gin/pull/2351)
## Gin v1.6.2
### 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
* 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
### 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
### BREAKING
* 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)
* Added support for SameSite cookie flag [#1615](https://github.com/gin-gonic/gin/pull/1615)
- 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)
- Added support for SameSite cookie flag [#1615](https://github.com/gin-gonic/gin/pull/1615)
### FEATURES
* add yaml negotiation [#2220](https://github.com/gin-gonic/gin/pull/2220)
* FileFromFS [#2112](https://github.com/gin-gonic/gin/pull/2112)
- add yaml negotiation [#2220](https://github.com/gin-gonic/gin/pull/2220)
- FileFromFS [#2112](https://github.com/gin-gonic/gin/pull/2112)
### BUG FIXES
* 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)
* 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)
* [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)
- 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)
- 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)
- [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)
### ENHANCEMENTS
* 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: remove duplicate assignment [#2222](https://github.com/gin-gonic/gin/pull/2222)
* chore: upgrade go-isatty and json-iterator/go [#2215](https://github.com/gin-gonic/gin/pull/2215)
* path: sync code with httprouter [#2212](https://github.com/gin-gonic/gin/pull/2212)
* Use zero-copy approach to convert types between string and byte slice [#2206](https://github.com/gin-gonic/gin/pull/2206)
* Reuse bytes when cleaning the URL paths [#2179](https://github.com/gin-gonic/gin/pull/2179)
* tree: remove one else statement [#2177](https://github.com/gin-gonic/gin/pull/2177)
* tree: sync httprouter update (#2173) (#2172) [#2171](https://github.com/gin-gonic/gin/pull/2171)
* tree: sync part httprouter codes and reduce if/else [#2163](https://github.com/gin-gonic/gin/pull/2163)
* use http method constant [#2155](https://github.com/gin-gonic/gin/pull/2155)
* 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)
* Add build tag nomsgpack [#1852](https://github.com/gin-gonic/gin/pull/1852)
- 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: remove duplicate assignment [#2222](https://github.com/gin-gonic/gin/pull/2222)
- chore: upgrade go-isatty and json-iterator/go [#2215](https://github.com/gin-gonic/gin/pull/2215)
- path: sync code with httprouter [#2212](https://github.com/gin-gonic/gin/pull/2212)
- Use zero-copy approach to convert types between string and byte slice [#2206](https://github.com/gin-gonic/gin/pull/2206)
- Reuse bytes when cleaning the URL paths [#2179](https://github.com/gin-gonic/gin/pull/2179)
- tree: remove one else statement [#2177](https://github.com/gin-gonic/gin/pull/2177)
- tree: sync httprouter update (#2173) (#2172) [#2171](https://github.com/gin-gonic/gin/pull/2171)
- tree: sync part httprouter codes and reduce if/else [#2163](https://github.com/gin-gonic/gin/pull/2163)
- use http method constant [#2155](https://github.com/gin-gonic/gin/pull/2155)
- 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)
- Add build tag nomsgpack [#1852](https://github.com/gin-gonic/gin/pull/1852)
### DOCS
* 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)
* Fix spelling [#2202](https://github.com/gin-gonic/gin/pull/2202)
* Remove broken link from README. [#2198](https://github.com/gin-gonic/gin/pull/2198)
* Update docs on Context.Done(), Context.Deadline() and Context.Err() [#2196](https://github.com/gin-gonic/gin/pull/2196)
* Update validator to v10 [#2190](https://github.com/gin-gonic/gin/pull/2190)
* upgrade go-validator to v10 for README [#2189](https://github.com/gin-gonic/gin/pull/2189)
* Update to currently output [#2188](https://github.com/gin-gonic/gin/pull/2188)
* Fix "Custom Validators" example [#2186](https://github.com/gin-gonic/gin/pull/2186)
* 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)
* Changed wording for clarity in README.md [#2122](https://github.com/gin-gonic/gin/pull/2122)
- 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)
- Fix spelling [#2202](https://github.com/gin-gonic/gin/pull/2202)
- Remove broken link from README. [#2198](https://github.com/gin-gonic/gin/pull/2198)
- Update docs on Context.Done(), Context.Deadline() and Context.Err() [#2196](https://github.com/gin-gonic/gin/pull/2196)
- Update validator to v10 [#2190](https://github.com/gin-gonic/gin/pull/2190)
- upgrade go-validator to v10 for README [#2189](https://github.com/gin-gonic/gin/pull/2189)
- Update to currently output [#2188](https://github.com/gin-gonic/gin/pull/2188)
- Fix "Custom Validators" example [#2186](https://github.com/gin-gonic/gin/pull/2186)
- 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)
- Changed wording for clarity in README.md [#2122](https://github.com/gin-gonic/gin/pull/2122)
### MISC
* 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)
* Drop support go1.10 [#2147](https://github.com/gin-gonic/gin/pull/2147)
* fix comment in `mode.go` [#2129](https://github.com/gin-gonic/gin/pull/2129)
- 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)
- Drop support go1.10 [#2147](https://github.com/gin-gonic/gin/pull/2147)
- fix comment in `mode.go` [#2129](https://github.com/gin-gonic/gin/pull/2129)
## Gin v1.5.0
@ -485,14 +534,14 @@
### Gin v1.4.0
- [NEW] Support for [Go Modules](https://github.com/golang/go/wiki/Modules) [#1569](https://github.com/gin-gonic/gin/pull/1569)
- [NEW] Support for [Go Modules](https://github.com/golang/go/wiki/Modules) [#1569](https://github.com/gin-gonic/gin/pull/1569)
- [NEW] Refactor of form mapping multipart request [#1829](https://github.com/gin-gonic/gin/pull/1829)
- [FIX] Truncate Latency precision in long running request [#1830](https://github.com/gin-gonic/gin/pull/1830)
- [FIX] IsTerm flag should not be affected by DisableConsoleColor method. [#1802](https://github.com/gin-gonic/gin/pull/1802)
- [NEW] Supporting file binding [#1264](https://github.com/gin-gonic/gin/pull/1264)
- [NEW] Add support for mapping arrays [#1797](https://github.com/gin-gonic/gin/pull/1797)
- [FIX] Readme updates [#1793](https://github.com/gin-gonic/gin/pull/1793) [#1788](https://github.com/gin-gonic/gin/pull/1788) [1789](https://github.com/gin-gonic/gin/pull/1789)
- [FIX] StaticFS: Fixed Logging two log lines on 404. [#1805](https://github.com/gin-gonic/gin/pull/1805), [#1804](https://github.com/gin-gonic/gin/pull/1804)
- [FIX] StaticFS: Fixed Logging two log lines on 404. [#1805](https://github.com/gin-gonic/gin/pull/1805), [#1804](https://github.com/gin-gonic/gin/pull/1804)
- [NEW] Make context.Keys available as LogFormatterParams [#1779](https://github.com/gin-gonic/gin/pull/1779)
- [NEW] Use internal/json for Marshal/Unmarshal [#1791](https://github.com/gin-gonic/gin/pull/1791)
- [NEW] Support mapping time.Duration [#1794](https://github.com/gin-gonic/gin/pull/1794)
@ -524,7 +573,7 @@
- [NEW] RunFd method to run http.Server through a file descriptor [#1609](https://github.com/gin-gonic/gin/pull/1609)
- [NEW] Yaml binding support [#1618](https://github.com/gin-gonic/gin/pull/1618)
- [FIX] Pass MaxMultipartMemory when FormFile is called [#1600](https://github.com/gin-gonic/gin/pull/1600)
- [FIX] LoadHTML* tests [#1559](https://github.com/gin-gonic/gin/pull/1559)
- [FIX] LoadHTML\* tests [#1559](https://github.com/gin-gonic/gin/pull/1559)
- [FIX] Removed use of sync.pool from HandleContext [#1565](https://github.com/gin-gonic/gin/pull/1565)
- [FIX] Format output log to os.Stderr [#1571](https://github.com/gin-gonic/gin/pull/1571)
- [FIX] Make logger use a yellow background and a darkgray text for legibility [#1570](https://github.com/gin-gonic/gin/pull/1570)
@ -539,7 +588,6 @@
- [FIX] Add BindXML and ShouldBindXML [#1485](https://github.com/gin-gonic/gin/pull/1485)
- [NEW] Upgrade dependency libraries [#1491](https://github.com/gin-gonic/gin/pull/1491)
## Gin v1.3.0
- [NEW] Add [`func (*Context) QueryMap`](https://godoc.org/github.com/gin-gonic/gin#Context.QueryMap), [`func (*Context) GetQueryMap`](https://godoc.org/github.com/gin-gonic/gin#Context.GetQueryMap), [`func (*Context) PostFormMap`](https://godoc.org/github.com/gin-gonic/gin#Context.PostFormMap) and [`func (*Context) GetPostFormMap`](https://godoc.org/github.com/gin-gonic/gin#Context.GetPostFormMap) to support `type map[string]string` as query string or form parameters, see [#1383](https://github.com/gin-gonic/gin/pull/1383)
@ -637,7 +685,6 @@
- [FIX] Error implements the json.Marshaller interface
- [FIX] MIT license in every file
## Gin 1.0rc1 (May 22, 2015)
- [PERFORMANCE] Zero allocation router
@ -681,7 +728,6 @@
- [FIX] Hijacking http
- [FIX] Better support for Google App Engine (using log instead of fmt)
## Gin 0.6 (Mar 9, 2015)
- [NEW] Support multipart/form-data
@ -691,14 +737,12 @@
- [FIX] Unsigned integers in binding
- [FIX] Improve color logger
## Gin 0.5 (Feb 7, 2015)
- [NEW] Content Negotiation
- [FIX] Solved security bug that allow a client to spoof ip
- [FIX] Fix unexported/ignored fields in binding
## Gin 0.4 (Aug 21, 2014)
- [NEW] Development mode
@ -707,7 +751,6 @@
- [FIX] Deferring WriteHeader()
- [FIX] Improved documentation for model binding
## Gin 0.3 (Jul 18, 2014)
- [PERFORMANCE] Normal log and error log are printed in the same call.
@ -725,8 +768,8 @@
- [FIX] Renaming Context.Req to Context.Request
- [FIX] Check application/x-www-form-urlencoded when parsing form
## Gin 0.2b (Jul 08, 2014)
- [PERFORMANCE] Using sync.Pool to allocatio/gc overhead
- [NEW] Travis CI integration
- [NEW] Completely new logger

View File

@ -11,9 +11,9 @@
[![Open Source Helpers](https://www.codetriage.com/gin-gonic/gin/badges/users.svg)](https://www.codetriage.com/gin-gonic/gin)
[![Release](https://img.shields.io/github/release/gin-gonic/gin.svg?style=flat-square)](https://github.com/gin-gonic/gin/releases)
## 📰 [Announcing Gin 1.11.0!](https://gin-gonic.com/en/blog/news/gin-1-11-0-release-announcement/)
## 📰 Gin 1.12.0 is now available!
Read about the latest features and improvements in Gin 1.11.0 on our official blog.
We're excited to announce the release of **[Gin 1.12.0](https://gin-gonic.com/en/blog/news/gin-1-12-0-release-announcement/)**! This release brings new features, performance improvements, and important bug fixes. Check out the [release announcement](https://gin-gonic.com/en/blog/news/gin-1-12-0-release-announcement/) on our official blog for the full details.
---
@ -44,7 +44,7 @@ Gin combines the simplicity of Express.js-style routing with Go's performance ch
### Prerequisites
- **Go version**: Gin requires [Go](https://go.dev/) version [1.24](https://go.dev/doc/devel/release#go1.24.0) or above
- **Go version**: Gin requires [Go](https://go.dev/) version [1.25](https://go.dev/doc/devel/release#go1.25.0) or above
- **Basic Go knowledge**: Familiarity with Go syntax and package management is helpful
### Installation

View File

@ -21,7 +21,7 @@ import (
"github.com/gin-gonic/gin/testdata/protoexample"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/v2/bson"
"google.golang.org/protobuf/proto"
)

View File

@ -8,7 +8,7 @@ import (
"io"
"net/http"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/v2/bson"
)
type bsonBinding struct{}

View File

@ -1056,6 +1056,7 @@ func (c *Context) requestHeader(key string) string {
/************************************/
// bodyAllowedForStatus is a copy of http.bodyAllowedForStatus non-exported function.
// Uses http.StatusContinue constant for better code clarity.
func bodyAllowedForStatus(status int) bool {
switch {
case status >= http.StatusContinue && status < http.StatusOK:
@ -1223,6 +1224,12 @@ func (c *Context) XML(code int, obj any) {
c.Render(code, render.XML{Data: obj})
}
// PDF writes the given PDF binary data into the response body.
// It also sets the Content-Type as "application/pdf".
func (c *Context) PDF(code int, data []byte) {
c.Render(code, render.PDF{Data: data})
}
// YAML serializes the given struct as YAML into the response body.
func (c *Context) YAML(code int, obj any) {
c.Render(code, render.YAML{Data: obj})

View File

@ -32,7 +32,7 @@ import (
testdata "github.com/gin-gonic/gin/testdata/protoexample"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/v2/bson"
"google.golang.org/protobuf/proto"
)
@ -1031,6 +1031,7 @@ func TestContextGetCookie(t *testing.T) {
}
func TestContextBodyAllowedForStatus(t *testing.T) {
assert.False(t, bodyAllowedForStatus(http.StatusContinue))
assert.False(t, bodyAllowedForStatus(http.StatusProcessing))
assert.False(t, bodyAllowedForStatus(http.StatusNoContent))
assert.False(t, bodyAllowedForStatus(http.StatusNotModified))
@ -1319,6 +1320,33 @@ func TestContextRenderNoContentXML(t *testing.T) {
assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
}
// TestContextRenderPDF tests that the response is serialized as PDF
// and Content-Type is set to application/pdf
func TestContextRenderPDF(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
data := []byte("%Test pdf content")
c.PDF(http.StatusCreated, data)
assert.Equal(t, http.StatusCreated, w.Code)
assert.Equal(t, data, w.Body.Bytes())
assert.Equal(t, "application/pdf", w.Header().Get("Content-Type"))
}
// Tests that no PDF is rendered if code is 204
func TestContextRenderNoContentPDF(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
data := []byte("%Test pdf content")
c.PDF(http.StatusNoContent, data)
assert.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.Bytes())
assert.Equal(t, "application/pdf", w.Header().Get("Content-Type"))
}
// TestContextRenderString tests that the response is returned
// with Content-Type set to text/plain
func TestContextRenderString(t *testing.T) {
@ -2947,6 +2975,16 @@ func TestContextGetRawData(t *testing.T) {
assert.Equal(t, "Fetch binary post data", string(data))
}
func TestContextGetRawDataNilBody(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
data, err := c.GetRawData()
assert.Nil(t, data)
require.Error(t, err)
assert.Equal(t, "cannot read nil body", err.Error())
}
func TestContextRenderDataFromReader(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
@ -3535,6 +3573,24 @@ func TestContextSetCookieData(t *testing.T) {
setCookie := c.Writer.Header().Get("Set-Cookie")
assert.Contains(t, setCookie, "SameSite=None")
})
// Test that SameSiteDefaultMode inherits from context's sameSite
t.Run("SameSiteDefaultMode inherits context sameSite", func(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder())
c.SetSameSite(http.SameSiteStrictMode)
cookie := &http.Cookie{
Name: "user",
Value: "gin",
Path: "/",
Domain: "localhost",
Secure: true,
HttpOnly: true,
SameSite: http.SameSiteDefaultMode,
}
c.SetCookieData(cookie)
setCookie := c.Writer.Header().Get("Set-Cookie")
assert.Contains(t, setCookie, "SameSite=Strict")
})
}
func TestGetMapFromFormData(t *testing.T) {

View File

@ -13,7 +13,7 @@ import (
"sync/atomic"
)
const ginSupportMinGoVer = 24
const ginSupportMinGoVer = 25
var runtimeVersion = runtime.Version()
@ -80,7 +80,7 @@ func getMinVer(v string) (uint64, error) {
func debugPrintWARNINGDefault() {
if v, e := getMinVer(runtimeVersion); e == nil && v < ginSupportMinGoVer {
debugPrint(`[WARNING] Now Gin requires Go 1.24+.
debugPrint(`[WARNING] Now Gin requires Go 1.25+.
`)
}

View File

@ -121,7 +121,7 @@ func TestDebugPrintWARNINGDefaultWithUnsupportedVersion(t *testing.T) {
debugPrintWARNINGDefault()
SetMode(TestMode)
})
assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.24+.\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.25+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
}
func TestDebugPrintWARNINGNew(t *testing.T) {

View File

@ -22,6 +22,7 @@
- [How to write log file](#how-to-write-log-file)
- [Custom Log Format](#custom-log-format)
- [Controlling Log output coloring](#controlling-log-output-coloring)
- [Avoid logging query strings](#avoid-loging-query-strings)
- [Model binding and validation](#model-binding-and-validation)
- [Custom Validators](#custom-validators)
- [Only Bind Query String](#only-bind-query-string)
@ -592,6 +593,20 @@ func main() {
}
```
### Avoid logging query strings
```go
func main() {
router := gin.New()
// SkipQueryString indicates that the logger should not log the query string.
// For example, /path?q=1 will be logged as /path
loggerConfig := gin.LoggerConfig{SkipQueryString: true}
router.Use(gin.LoggerWithConfig(loggerConfig))
}
```
### Model binding and validation
To bind a request body into a type, use model binding. We currently support binding of JSON, XML, YAML, TOML and standard form values (foo=bar&boo=baz).

26
go.mod
View File

@ -1,24 +1,22 @@
module github.com/gin-gonic/gin
go 1.24.0
toolchain go1.24.7
go 1.25.0
require (
github.com/bytedance/sonic v1.14.2
github.com/bytedance/sonic v1.15.0
github.com/gin-contrib/sse v1.1.0
github.com/go-playground/validator/v10 v10.28.0
github.com/go-playground/validator/v10 v10.30.1
github.com/goccy/go-json v0.10.5
github.com/goccy/go-yaml v1.19.1
github.com/goccy/go-yaml v1.19.2
github.com/json-iterator/go v1.1.12
github.com/mattn/go-isatty v0.0.20
github.com/modern-go/reflect2 v1.0.2
github.com/pelletier/go-toml/v2 v2.2.4
github.com/quic-go/quic-go v0.57.1
github.com/quic-go/quic-go v0.59.0
github.com/stretchr/testify v1.11.1
github.com/ugorji/go/codec v1.3.1
go.mongodb.org/mongo-driver v1.17.7
golang.org/x/net v0.47.0
go.mongodb.org/mongo-driver/v2 v2.5.0
golang.org/x/net v0.51.0
google.golang.org/protobuf v1.36.10
)
@ -26,10 +24,10 @@ require gopkg.in/yaml.v3 v3.0.1 // indirect
require (
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic/loader v0.4.0 // indirect
github.com/bytedance/sonic/loader v0.5.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
github.com/gabriel-vasile/mimetype v1.4.12 // 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.3.0 // indirect
@ -41,7 +39,7 @@ require (
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
go.uber.org/mock v0.6.0 // indirect
golang.org/x/arch v0.22.0 // indirect
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/text v0.34.0 // indirect
)

46
go.sum
View File

@ -1,17 +1,17 @@
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=
github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=
github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.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.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
@ -20,12 +20,12 @@ 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.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.19.1 h1:3rG3+v8pkhRqoQ/88NYNMHYVGYztCOCIZ7UQhu7H+NE=
github.com/goccy/go-yaml v1.19.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@ -52,8 +52,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10=
github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s=
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -71,23 +71,21 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
go.mongodb.org/mongo-driver v1.17.7 h1:a9w+U3Vt67eYzcfq3k/OAv284/uUUkL0uP75VE5rCOU=
go.mongodb.org/mongo-driver v1.17.7/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -48,6 +48,11 @@ type LoggerConfig struct {
// Optional.
SkipPaths []string
// SkipQueryString indicates that query strings should not be written
// for cases such as when API keys are passed via query strings.
// Optional. Default value is false.
SkipQueryString bool
// Skip is a Skipper that indicates which logs should not be written.
// Optional.
Skip Skipper
@ -298,7 +303,7 @@ func LoggerWithConfig(conf LoggerConfig) HandlerFunc {
param.BodySize = c.Writer.Size()
if raw != "" {
if raw != "" && !conf.SkipQueryString {
path = path + "?" + raw
}

View File

@ -318,20 +318,21 @@ func TestColorForStatus(t *testing.T) {
}
func TestColorForLatency(t *testing.T) {
colorForLantency := func(latency time.Duration) string {
colorForLatency := func(latency time.Duration) string {
p := LogFormatterParams{
Latency: latency,
}
return p.LatencyColor()
}
assert.Equal(t, white, colorForLantency(time.Duration(0)), "0 should be white")
assert.Equal(t, white, colorForLantency(time.Millisecond*20), "20ms should be white")
assert.Equal(t, green, colorForLantency(time.Millisecond*150), "150ms should be green")
assert.Equal(t, cyan, colorForLantency(time.Millisecond*250), "250ms should be cyan")
assert.Equal(t, yellow, colorForLantency(time.Millisecond*600), "600ms should be yellow")
assert.Equal(t, magenta, colorForLantency(time.Millisecond*1500), "1.5s should be magenta")
assert.Equal(t, red, colorForLantency(time.Second*3), "other things should be red")
assert.Equal(t, white, colorForLatency(time.Duration(0)), "0 should be white")
assert.Equal(t, white, colorForLatency(time.Millisecond*20), "20ms should be white")
assert.Equal(t, green, colorForLatency(time.Millisecond*150), "150ms should be green")
assert.Equal(t, cyan, colorForLatency(time.Millisecond*250), "250ms should be cyan")
assert.Equal(t, blue, colorForLatency(time.Millisecond*400), "400ms should be blue")
assert.Equal(t, yellow, colorForLatency(time.Millisecond*600), "600ms should be yellow")
assert.Equal(t, magenta, colorForLatency(time.Millisecond*1500), "1.5s should be magenta")
assert.Equal(t, red, colorForLatency(time.Second*3), "other things should be red")
}
func TestResetColor(t *testing.T) {
@ -471,3 +472,17 @@ func TestForceConsoleColor(t *testing.T) {
// reset console color mode.
consoleColorMode = autoColor
}
func TestLoggerWithConfigSkipQueryString(t *testing.T) {
buffer := new(strings.Builder)
router := New()
router.Use(LoggerWithConfig(LoggerConfig{
Output: buffer,
SkipQueryString: true,
}))
router.GET("/logged", func(c *Context) { c.Status(http.StatusOK) })
PerformRequest(router, "GET", "/logged?a=21")
assert.Contains(t, buffer.String(), "200")
assert.NotContains(t, buffer.String(), "a=21")
}

View File

@ -7,7 +7,7 @@ package render
import (
"net/http"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/v2/bson"
)
// BSON contains the given interface object.

View File

@ -4,7 +4,10 @@
package render
import "net/http"
import (
"net/http"
"strconv"
)
// Data contains ContentType and bytes data.
type Data struct {
@ -15,6 +18,9 @@ type Data struct {
// Render (Data) writes data with custom ContentType.
func (r Data) Render(w http.ResponseWriter) (err error) {
r.WriteContentType(w)
if len(r.Data) > 0 {
w.Header().Set("Content-Length", strconv.Itoa(len(r.Data)))
}
_, err = w.Write(r.Data)
return
}

26
render/pdf.go Normal file
View File

@ -0,0 +1,26 @@
// Copyright 2026 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.
package render
import "net/http"
// PDF contains the given PDF binary data.
type PDF struct {
Data []byte
}
var pdfContentType = []string{"application/pdf"}
// Render (PDF) writes PDF data with custom ContentType.
func (r PDF) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
_, err := w.Write(r.Data)
return err
}
// WriteContentType (PDF) writes PDF ContentType for response.
func (r PDF) WriteContentType(w http.ResponseWriter) {
writeContentType(w, pdfContentType)
}

View File

@ -31,6 +31,7 @@ var (
_ Render = (*AsciiJSON)(nil)
_ Render = (*ProtoBuf)(nil)
_ Render = (*TOML)(nil)
_ Render = (*PDF)(nil)
)
func writeContentType(w http.ResponseWriter, value []string) {

View File

@ -7,7 +7,7 @@
package render
import (
"bytes"
"errors"
"net/http/httptest"
"testing"
@ -16,9 +16,6 @@ import (
"github.com/ugorji/go/codec"
)
// TODO unit tests
// test errors
func TestRenderMsgPack(t *testing.T) {
w := httptest.NewRecorder()
data := map[string]any{
@ -32,13 +29,52 @@ func TestRenderMsgPack(t *testing.T) {
require.NoError(t, err)
h := new(codec.MsgpackHandle)
assert.NotNil(t, h)
buf := bytes.NewBuffer([]byte{})
assert.NotNil(t, buf)
err = codec.NewEncoder(buf, h).Encode(data)
var decoded map[string]any
var mh codec.MsgpackHandle
mh.RawToString = true
err = codec.NewDecoderBytes(w.Body.Bytes(), &mh).Decode(&decoded)
require.NoError(t, err)
assert.Equal(t, w.Body.String(), buf.String())
assert.Equal(t, data, decoded)
assert.Equal(t, "application/msgpack; charset=utf-8", w.Header().Get("Content-Type"))
}
func TestWriteMsgPack(t *testing.T) {
w := httptest.NewRecorder()
data := map[string]any{
"foo": "bar",
"num": 42,
}
err := WriteMsgPack(w, data)
require.NoError(t, err)
assert.Equal(t, "application/msgpack; charset=utf-8", w.Header().Get("Content-Type"))
var decoded map[string]any
var mh codec.MsgpackHandle
mh.RawToString = true
err = codec.NewDecoderBytes(w.Body.Bytes(), &mh).Decode(&decoded)
require.NoError(t, err)
assert.Len(t, decoded, 2)
assert.Equal(t, "bar", decoded["foo"])
assert.EqualValues(t, 42, decoded["num"])
}
type failWriter struct {
*httptest.ResponseRecorder
}
func (w *failWriter) Write(data []byte) (int, error) {
return 0, errors.New("write error")
}
func TestRenderMsgPackError(t *testing.T) {
w := httptest.NewRecorder()
data := map[string]any{
"foo": "bar",
}
err := (MsgPack{data}).Render(&failWriter{w})
require.Error(t, err)
assert.Contains(t, err.Error(), "write error")
}

View File

@ -8,6 +8,7 @@ import (
"encoding/xml"
"errors"
"html/template"
"io"
"net"
"net/http"
"net/http/httptest"
@ -15,17 +16,13 @@ import (
"strings"
"testing"
"github.com/gin-gonic/gin/codec/json"
testdata "github.com/gin-gonic/gin/testdata/protoexample"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/v2/bson"
"google.golang.org/protobuf/proto"
)
// TODO unit tests
// test errors
func TestRenderJSON(t *testing.T) {
w := httptest.NewRecorder()
data := map[string]any{
@ -140,19 +137,44 @@ func TestRenderJsonpJSON(t *testing.T) {
}
type errorWriter struct {
bufString string
bufString string
ErrThreshold int // 1-based threshold. If 1, errors on the 1st Write call.
writeCount int
*httptest.ResponseRecorder
}
var _ http.ResponseWriter = (*errorWriter)(nil)
func (w *errorWriter) Header() http.Header {
if w.ResponseRecorder == nil {
w.ResponseRecorder = httptest.NewRecorder()
}
return w.ResponseRecorder.Header()
}
func (w *errorWriter) WriteHeader(statusCode int) {
if w.ResponseRecorder == nil {
w.ResponseRecorder = httptest.NewRecorder()
}
w.ResponseRecorder.WriteHeader(statusCode)
}
func (w *errorWriter) Write(buf []byte) (int, error) {
if string(buf) == w.bufString {
return 0, errors.New(`write "` + w.bufString + `" error`)
w.writeCount++
if (w.bufString != "" && string(buf) == w.bufString) || (w.ErrThreshold > 0 && w.writeCount >= w.ErrThreshold) {
return 0, errors.New(`write error`)
}
if w.ResponseRecorder == nil {
w.ResponseRecorder = httptest.NewRecorder()
}
return w.ResponseRecorder.Write(buf)
}
func (w *errorWriter) reset() {
w.writeCount = 0
w.ResponseRecorder = httptest.NewRecorder()
}
func TestRenderJsonpJSONError(t *testing.T) {
ew := &errorWriter{
ResponseRecorder: httptest.NewRecorder(),
@ -165,23 +187,33 @@ func TestRenderJsonpJSONError(t *testing.T) {
},
}
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())
// error was returned while writing callback
ew.reset()
ew.ErrThreshold = 1
err := jsonpJSON.Render(ew)
require.Error(t, err)
assert.Equal(t, "write error", err.Error())
ew.bufString = `(`
// error was returned while writing "("
ew.reset()
ew.ErrThreshold = 2
err = jsonpJSON.Render(ew)
assert.Equal(t, `write "`+`(`+`" error`, err.Error())
require.Error(t, err)
assert.Equal(t, "write error", err.Error())
data, _ := json.API.Marshal(jsonpJSON.Data) // error was returned while writing data
ew.bufString = string(data)
// error was returned while writing data
ew.reset()
ew.ErrThreshold = 3
err = jsonpJSON.Render(ew)
assert.Equal(t, `write "`+string(data)+`" error`, err.Error())
require.Error(t, err)
assert.Equal(t, "write error", err.Error())
ew.bufString = `);`
// error was returned while writing ");"
ew.reset()
ew.ErrThreshold = 4
err = jsonpJSON.Render(ew)
assert.Equal(t, `write "`+`);`+`" error`, err.Error())
require.Error(t, err)
assert.Equal(t, "write error", err.Error())
}
func TestRenderJsonpJSONError2(t *testing.T) {
@ -385,6 +417,30 @@ func TestRenderBSON(t *testing.T) {
assert.Equal(t, "application/bson", w.Header().Get("Content-Type"))
}
func TestRenderBSONError(t *testing.T) {
w := httptest.NewRecorder()
data := make(chan int)
err := (BSON{data}).Render(w)
require.Error(t, err)
}
func TestRenderBSONWriteError(t *testing.T) {
type testStruct struct {
Value string
}
data := &testStruct{Value: "test"}
ew := &errorWriter{
ErrThreshold: 1,
ResponseRecorder: httptest.NewRecorder(),
}
err := (BSON{data}).Render(ew)
require.Error(t, err)
assert.Equal(t, "write error", err.Error())
}
func TestRenderXML(t *testing.T) {
w := httptest.NewRecorder()
data := xmlmap{
@ -401,6 +457,31 @@ func TestRenderXML(t *testing.T) {
assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
}
func TestRenderXMLError(t *testing.T) {
w := httptest.NewRecorder()
data := make(chan int)
err := (XML{data}).Render(w)
require.Error(t, err)
assert.Contains(t, err.Error(), "xml: unsupported type: chan int")
}
func TestRenderPDF(t *testing.T) {
w := httptest.NewRecorder()
data := []byte("%Test pdf content")
pdf := PDF{data}
pdf.WriteContentType(w)
assert.Equal(t, "application/pdf", w.Header().Get("Content-Type"))
err := pdf.Render(w)
require.NoError(t, err)
assert.Equal(t, data, w.Body.Bytes())
assert.Equal(t, "application/pdf", w.Header().Get("Content-Type"))
}
func TestRenderRedirect(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, "/test-redirect", nil)
require.NoError(t, err)
@ -453,6 +534,52 @@ func TestRenderData(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, "#!PNG some raw data", w.Body.String())
assert.Equal(t, "image/png", w.Header().Get("Content-Type"))
assert.Equal(t, "19", w.Header().Get("Content-Length"))
}
func TestRenderDataContentLength(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
size, err := strconv.Atoi(r.URL.Query().Get("size"))
assert.NoError(t, err)
data := Data{
ContentType: "application/octet-stream",
Data: make([]byte, size),
}
assert.NoError(t, data.Render(w))
}))
t.Cleanup(srv.Close)
for _, size := range []int{0, 1, 100, 100_000} {
t.Run(strconv.Itoa(size), func(t *testing.T) {
resp, err := http.Get(srv.URL + "?size=" + strconv.Itoa(size))
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, "application/octet-stream", resp.Header.Get("Content-Type"))
assert.Equal(t, strconv.Itoa(size), resp.Header.Get("Content-Length"))
actual, err := io.Copy(io.Discard, resp.Body)
require.NoError(t, err)
assert.EqualValues(t, size, actual)
})
}
}
func TestRenderDataError(t *testing.T) {
ew := &errorWriter{
ErrThreshold: 1,
ResponseRecorder: httptest.NewRecorder(),
}
data := []byte("#!PNG some raw data")
err := (Data{
ContentType: "image/png",
Data: data,
}).Render(ew)
require.Error(t, err)
assert.Equal(t, "write error", err.Error())
}
func TestRenderString(t *testing.T) {
@ -594,6 +721,32 @@ func TestRenderHTMLDebugPanics(t *testing.T) {
assert.Panics(t, func() { htmlRender.Instance("", nil) })
}
func TestRenderHTMLTemplateError(t *testing.T) {
w := httptest.NewRecorder()
templ := template.Must(template.New("t").Parse(`Hello {{if .name}}{{.name.DoesNotExist}}{{end}}`))
htmlRender := HTMLProduction{Template: templ}
instance := htmlRender.Instance("t", map[string]any{
"name": "alexandernyquist",
})
err := instance.Render(w)
require.Error(t, err)
}
func TestRenderHTMLTemplateExecuteError(t *testing.T) {
w := httptest.NewRecorder()
templ := template.Must(template.New("t").Parse(`Hello {{.name.invalid}}`))
htmlRender := HTMLProduction{Template: templ}
instance := htmlRender.Instance("t", map[string]any{
"name": "alexandernyquist",
})
err := instance.Render(w)
require.Error(t, err)
}
func TestRenderReader(t *testing.T) {
w := httptest.NewRecorder()
@ -645,10 +798,10 @@ func TestRenderWriteError(t *testing.T) {
prefix := "my-prefix:"
r := SecureJSON{Data: data, Prefix: prefix}
ew := &errorWriter{
bufString: prefix,
ErrThreshold: 1,
ResponseRecorder: httptest.NewRecorder(),
}
err := r.Render(ew)
require.Error(t, err)
assert.Equal(t, `write "my-prefix:" error`, err.Error())
assert.Equal(t, "write error", err.Error())
}

67
tree.go
View File

@ -818,7 +818,72 @@ walk: // Outer loop for walking the tree
return nil
}
n = n.children[0]
// When wildChild is true, try static children first (via indices)
// before falling back to the wildcard child. This ensures that
// case-insensitive lookups prefer static routes over param routes
// (e.g., /PREFIX/XXX should resolve to /prefix/xxx, not match :id).
if len(n.indices) > 0 {
rb = shiftNRuneBytes(rb, npLen)
if rb[0] != 0 {
idxc := rb[0]
for i, c := range []byte(n.indices) {
if c == idxc {
if out := n.children[i].findCaseInsensitivePathRec(
path, ciPath, rb, fixTrailingSlash,
); out != nil {
return out
}
break
}
}
} else {
var rv rune
var off int
for max_ := min(npLen, 3); off < max_; off++ {
if i := npLen - off; utf8.RuneStart(oldPath[i]) {
rv, _ = utf8.DecodeRuneInString(oldPath[i:])
break
}
}
lo := unicode.ToLower(rv)
utf8.EncodeRune(rb[:], lo)
rb = shiftNRuneBytes(rb, off)
idxc := rb[0]
for i, c := range []byte(n.indices) {
if c == idxc {
if out := n.children[i].findCaseInsensitivePathRec(
path, ciPath, rb, fixTrailingSlash,
); out != nil {
return out
}
break
}
}
if up := unicode.ToUpper(rv); up != lo {
utf8.EncodeRune(rb[:], up)
rb = shiftNRuneBytes(rb, off)
idxc := rb[0]
for i, c := range []byte(n.indices) {
if c == idxc {
if out := n.children[i].findCaseInsensitivePathRec(
path, ciPath, rb, fixTrailingSlash,
); out != nil {
return out
}
break
}
}
}
}
}
// Fall back to wildcard child, which is always at the end of the array
n = n.children[len(n.children)-1]
switch n.nType {
case param:
// Find param end (either '/' or path end)

View File

@ -1018,3 +1018,96 @@ func TestWildcardInvalidSlash(t *testing.T) {
}
}
}
func TestTreeFindCaseInsensitivePathWithMultipleChildrenAndWildcard(t *testing.T) {
tree := &node{}
// Setup routes that create a node with both static children and a wildcard child.
// This configuration previously caused a panic ("invalid node type") in
// findCaseInsensitivePathRec because it accessed children[0] instead of the
// wildcard child (which is always at the end of the children array).
// See: https://github.com/gin-gonic/gin/issues/2959
routes := [...]string{
"/aa/aa",
"/:bb/aa",
}
for _, route := range routes {
recv := catchPanic(func() {
tree.addRoute(route, fakeHandler(route))
})
if recv != nil {
t.Fatalf("panic inserting route '%s': %v", route, recv)
}
}
// These lookups previously panicked with "invalid node type" because
// findCaseInsensitivePathRec picked children[0] (a static node) instead
// of the wildcard child at the end of the array.
out, found := tree.findCaseInsensitivePath("/aa", true)
if found {
t.Errorf("Expected no match for '/aa', but got: %s", string(out))
}
out, found = tree.findCaseInsensitivePath("/aa/aa/aa/aa", true)
if found {
t.Errorf("Expected no match for '/aa/aa/aa/aa', but got: %s", string(out))
}
// Case-insensitive lookup should match the static route /aa/aa
out, found = tree.findCaseInsensitivePath("/AA/AA", true)
if !found {
t.Error("Route '/AA/AA' not found via case-insensitive lookup")
} else if string(out) != "/aa/aa" {
t.Errorf("Wrong result for '/AA/AA': expected '/aa/aa', got: %s", string(out))
}
}
func TestTreeFindCaseInsensitivePathWildcardParamAndStaticChild(t *testing.T) {
tree := &node{}
// Another variant: param route + static route under same prefix
routes := [...]string{
"/prefix/:id",
"/prefix/xxx",
}
for _, route := range routes {
recv := catchPanic(func() {
tree.addRoute(route, fakeHandler(route))
})
if recv != nil {
t.Fatalf("panic inserting route '%s': %v", route, recv)
}
}
// Should NOT panic even for paths that don't match any route
out, found := tree.findCaseInsensitivePath("/prefix/a/b/c", true)
if found {
t.Errorf("Expected no match for '/prefix/a/b/c', but got: %s", string(out))
}
// Exact match should still work
out, found = tree.findCaseInsensitivePath("/prefix/xxx", true)
if !found {
t.Error("Route '/prefix/xxx' not found")
} else if string(out) != "/prefix/xxx" {
t.Errorf("Wrong result for '/prefix/xxx': %s", string(out))
}
// Case-insensitive match should work
out, found = tree.findCaseInsensitivePath("/PREFIX/XXX", true)
if !found {
t.Error("Route '/PREFIX/XXX' not found via case-insensitive lookup")
} else if string(out) != "/prefix/xxx" {
t.Errorf("Wrong result for '/PREFIX/XXX': expected '/prefix/xxx', got: %s", string(out))
}
// Param route should still match
out, found = tree.findCaseInsensitivePath("/prefix/something", true)
if !found {
t.Error("Route '/prefix/something' not found via param match")
} else if string(out) != "/prefix/something" {
t.Errorf("Wrong result for '/prefix/something': %s", string(out))
}
}

View File

@ -13,6 +13,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func init() {
@ -145,6 +146,17 @@ func TestMarshalXMLforH(t *testing.T) {
assert.Error(t, e)
}
func TestMarshalXMLforHSuccess(t *testing.T) {
h := H{
"key1": "value1",
"key2": 123,
}
data, err := xml.Marshal(h)
require.NoError(t, err)
assert.Contains(t, string(data), "<key1>value1</key1>")
assert.Contains(t, string(data), "<key2>123</key2>")
}
func TestIsASCII(t *testing.T) {
assert.True(t, isASCII("test"))
assert.False(t, isASCII("🧡💛💚💙💜"))

View File

@ -5,4 +5,4 @@
package gin
// Version is the current gin framework's version.
const Version = "v1.11.0"
const Version = "v1.12.0"