getValue resliced the backtrack stack to index+1 on the assumption that
its capacity equals Engine.maxSections. The pool captures that capacity
at allocation. Routes registered afterwards raise maxSections without
resizing the pooled slice. The next request that needs one more frame
panics inside the router, before Recovery.
append grows the slice when the captured capacity is short and preserves
the existing backtrack behaviour when it is already sufficient. The reset
at entry stays: getValue still runs once per method tree on the same stack.
Co-authored-by: hazyhaar <hazyhaarlabs@gmail.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
The SecureJSON docs and the Context.SecureJSON comment said the default
prefix is "while(1),". The engine sets secureJSONPrefix to "while(1);"
(gin.go), and the example output a few lines below already shows the
semicolon.
The default-values binding example printed a "Colors" field that the
Person struct does not have; the struct's array field is Addresses
with default "foo bar", which binds to ["foo","bar"].
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Copy read c.Keys before RLock. Set stores that field under Lock when the
map is created. The race detector reports the unsynchronized access, and
the copy can observe a nil header and drop keys Set has already published.
maps.Clone(nil) returns nil, so the load and the clone belong in the same
critical section. Map iteration was already under the mutex.
Co-authored-by: hazyhaar <hazyhaarlabs@gmail.com>
Engine.Run, RunTLS, and RunListener created an http.Server without
ReadHeaderTimeout, leaving applications exposed to slowloris-style
resource exhaustion. A new Engine.ReadHeaderTimeout field (default
10s) is now applied to the http.Server created by these methods.
Users can customize the value or use a custom http.Server for full
control.
Fixes#4760
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
The example calls errors.Is but never imports errors, so copying it as
written fails to build with "undefined: errors".
It also passes an unbuffered channel to signal.Notify, which go vet
reports as a misuse: signal delivery is non-blocking, so a signal that
arrives before the receive is dropped.
* feat: add support for the HTTP QUERY method (RFC 10008)
Add a QUERY shortcut on RouterGroup, a gin.MethodQuery constant and a
gin.IQueryRoutes interface for callers that only hold an IRoutes value.
QUERY is deliberately kept out of IRoutes and out of Any:
- Adding a method to IRoutes would break every type outside of Gin that
implements it, such as test doubles and wrappers.
- Leaving Any untouched keeps existing catch-all routes matching exactly
the methods they matched before. Match([]string{gin.MethodQuery, ...})
covers the case where QUERY is wanted alongside other methods.
MethodQuery is declared in Gin because the standard library does not
provide http.MethodQuery yet. The value stays correct once it does.
Ref #4721
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(router): remove duplicate QUERY method
- Consolidate QUERY into the existing router method after merging master
- Preserve the MethodQuery constant and request body documentation
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
- Upgrade x/crypto and its required dependency versions
- Align documentation and CI with the required Go version
BREAKING CHANGE: Go 1.26 or later is now required by x/crypto v0.56.0. Upgrade the Go toolchain before building Gin.
Handle panics on invalid HTTP method, StaticFS panics when relativePath
contains URL parameters, and Bind panics when given a pointer. These
panic conditions are reasonable startup-time guards but were not
documented, making the crash risk undiscoverable without reading source.
Fixes#4594
* feat(router): add QUERY method shortcut for RFC 10008
Add a QUERY() router method as a shortcut for Handle("QUERY", path, handlers),
following the same pattern as GET, POST, HEAD and other HTTP method shortcuts.
The QUERY method is defined by RFC 10008 and allows sending a query in the
request body, similar to GET but with a body. Go's net/http does not yet
expose an http.MethodQuery constant, so the literal string "QUERY" is used.
Closes#4721
* style(router): format QUERY method test
- Remove an extra blank line to satisfy gofmt.
---------
Co-authored-by: Jia-ben00 <316397950+Jia-ben00@users.noreply.github.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
The scheduled Trivy scan has been failing on master with two HIGH
findings, and it fails every pull request along with it:
- CVE-2026-56852, golang.org/x/text, denial of service, fixed in 0.39.0
- CVE-2026-46600, golang.org/x/net/dns/dnsmessage, denial of service,
fixed in 0.56.0
Bump both to the first patched release. golang.org/x/crypto and
golang.org/x/sys move with them as transitive requirements of x/net.
Verified with the same settings the workflow uses:
trivy fs --scanners vuln --severity CRITICAL,HIGH,MEDIUM \
--ignore-unfixed --exit-code 1 .
which now exits 0. go build ./... and go test ./... pass.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
The doc comment on line 790 read:
"BindXML is a shortcut for c.MustBindWith(obj, binding.BindXML)."
`binding.BindXML` does not exist. The correct symbol is `binding.XML`
(defined in binding/binding.go), which is also what the implementation
uses. All sibling methods (BindJSON, BindYAML, BindTOML, etc.) already
reference the correct symbol in their comments.
* fix: skip chmod on pre-existing dirs in SaveUploadedFile
Fixes#4622
Prior to this change, SaveUploadedFile unconditionally called
os.Chmod(dir, mode) after os.MkdirAll, even when the target
directory already existed. This caused 'operation not permitted'
errors when saving files into system-owned directories like /tmp
that the current process does not own.
The fix uses os.Stat before MkdirAll to detect whether the
directory already exists, and only calls os.Chmod when the
directory was freshly created by MkdirAll.
This restores the behaviour from v1.10.1 where only os.MkdirAll
was used (which correctly skips permission changes on existing dirs)
while preserving the custom permission feature added in v1.12.0.
Regression test added: TestSaveUploadedFileToExistingDir saves
a file into os.TempDir() (a pre-existing system directory) and
asserts no error is returned.
* test: make SaveUploadedFile #4622 regression test platform-independent
The regression test relied on os.Chmod failing on a pre-existing directory,
which only happens for a non-owner, non-root process. When tests run as root
(common in containers/CI) or with a user-owned $TMPDIR, the buggy chmod
succeeds, so the test passed even against the unfixed code.
Assert the actual contract instead: a pre-existing directory's permissions
are left unchanged. This catches the regression deterministically on every
platform.
Also tighten the SaveUploadedFile doc/comments: the requested perm is enforced
only on the newly created destination directory, not on every directory in the
path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: change file creation to use c.Temp instead to work on wsl
* test(context): use t.TempDir() in SaveUploadedFile failure test for WSL
Mirror the WSL-portability fix applied to TestSaveUploadedFileWithPermission:
write under t.TempDir() instead of a relative path so the test does not depend
on the working directory's filesystem (drvfs reports 0o777 on WSL) and is
cleaned up automatically.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(response_writer): add tests for Flush() with and without http.Flusher
* test(response_writer): drop stray comment and clarify Flush regression note
- Remove orphaned doc comment left at the end of the file
- Reword the issue #4460 reference to reflect the no-panic guard
---------
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
* fix: record recovered panic errors
* chore(deps): bump quic-go to v0.59.1
* refactor(recovery): simplify panic error recording
- Collapse the if/else into a single c.Error call in defaultHandleRecovery
- Assert the recorded panic error is ErrorTypePrivate in the test
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: m0_66095053 <2876430886@qq.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Previously, Copy() only copied Keys and Params, leaving Errors and
Accepted as nil even when set on the original context. Goroutines
receiving a copied context could not observe errors attached before
the copy, and content-negotiation state set by middleware was silently
lost.
* Replace assert.Equal(t, len(c.Errors), len(cp.Errors)) with assert.Len(t, cp.Errors, 2) to satisfy the testifylint linter rule
---------
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Add two regression tests for GitHub issue #1848 to verify that
Context.handlers are properly isolated in HandleContext when used
from a NoRoute handler with group and engine middleware.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
- Remove 20 deprecated frameworks from benchmark report
- Add BunRouter and Fiber as newly benchmarked routers
- Add ranked summary table based on GitHub API throughput
- Add memory consumption tables sorted by bytes ascending
- Include all micro and API benchmark results with rankings
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AUTHORS.md was a manually maintained list of 400+ contributors that
had no automation to keep it current. Replace it with a link to
GitHub s built-in contributors page which is always up-to-date.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update test environment to Apple M4 Pro, Go 1.25.8, macOS
- Add table of contents for easier navigation
- Add ranked summary tables for each API benchmark
- Reorganize full results by API section with clear headings
- Refresh all memory consumption and benchmark data
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Bump golangci-lint from v2.9 to v2.11
- Bump aquasecurity/trivy-action from 0.34.2 to 0.35.0
- Upgrade golang.org/x packages (net, crypto, sys, text, arch)
- Upgrade go-json, mimetype, and protobuf to latest patch versions
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The flat TOC under a single "API Examples" heading made it hard to scan
and find relevant content. Reorganize 40+ sections into 7 logical groups
(Routing, Middleware, Logging, Request Binding & Validation, Response
Rendering, Server Configuration, Testing) with description lines for
each group. Move scattered sections into their correct groups and fix
the "avoid-loging-query-strings" anchor typo.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* render: add PDF renderer and tests
* render: update PDF renderer copyright header
* test: add PDF rendering tests including 204 No Content in context_test.go
- Update release announcement section to introduce Gin 1.12.0
- Provide a more detailed description of new features and improvements in version 1.12.0
Signed-off-by: appleboy <appleboy.tw@gmail.com>
* ci: update Go version support to 1.25+ across CI and docs
- Remove Go 1.24 from CI test matrix to only support 1.25 and 1.26
- Update documentation to require Go version 1.25 or above
Signed-off-by: appleboy <appleboy.tw@gmail.com>
* chore: increase required Go version for Gin to 1.25
- Update minimum required Go version for Gin from 1.24 to 1.25
- Revise related warning message and test expectations to match new version requirement
Signed-off-by: appleboy <appleboy.tw@gmail.com>
---------
Signed-off-by: appleboy <appleboy.tw@gmail.com>
* fix(tree): panic in findCaseInsensitivePathRec with RedirectFixedPath enabled
When RedirectFixedPath is enabled and a request doesn't match any route,
findCaseInsensitivePathRec panics with "invalid node type". This happens
because it accesses children[0] to find the wildcard child, but addChild()
keeps the wildcard child at the end of the array (not the beginning).
When a node has both static and wildcard children, children[0] is a static
node, so the switch on nType falls through to the default panic case.
The fix mirrors what getValue() already does correctly (line 483):
use children[len(children)-1] to access the wildcard child.
Fixes#2959
* Address review feedback: improve test assertions and prefer static routes in case-insensitive lookup
- Assert found=false for non-matching paths instead of only checking for panics
- Fix expected casing for case-insensitive static route match (/PREFIX/XXX -> /prefix/xxx)
- Update findCaseInsensitivePathRec to try static children before falling back to
wildcard child, ensuring static routes win over param routes in case-insensitive matching
---------
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>