Merge 85aae676c17fa408a8c8fba005e582a021293403 into 915e4c90d28ec4cffc6eb146e208ab5a65eac772

This commit is contained in:
Enzo Lanzellotti 2025-12-27 21:13:27 +08:00 committed by GitHub
commit eae7eb8584
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 407 additions and 0 deletions

View File

@ -40,6 +40,130 @@ const (
MIMEYAML2 = binding.MIMEYAML2 MIMEYAML2 = binding.MIMEYAML2
MIMETOML = binding.MIMETOML MIMETOML = binding.MIMETOML
MIMEPROTOBUF = binding.MIMEPROTOBUF MIMEPROTOBUF = binding.MIMEPROTOBUF
HeaderAuthorization string = "Authorization"
HeaderProxyAuthenticate string = "Proxy-Authenticate"
HeaderProxyAuthorization string = "Proxy-Authorization"
HeaderWWWAuthenticate string = "WWW-Authenticate"
HeaderAge string = "Age"
HeaderCacheControl string = "Cache-Control"
HeaderClearSiteData string = "Clear-Site-Data"
HeaderExpires string = "Expires"
HeaderPragma string = "Pragma"
HeaderWarning string = "Warning"
HeaderAcceptCH string = "Accept-CH"
HeaderAcceptCHLifetime string = "Accept-CH-Lifetime"
HeaderContentDPR string = "Content-DPR"
HeaderDPR string = "DPR"
HeaderEarlyData string = "Early-Data"
HeaderSaveData string = "Save-Data"
HeaderViewportWidth string = "Viewport-Width"
HeaderWidth string = "Width"
HeaderETag string = "ETag"
HeaderIfMatch string = "If-Match"
HeaderIfModifiedSince string = "If-Modified-Since"
HeaderIfNoneMatch string = "If-None-Match"
HeaderIfUnmodifiedSince string = "If-Unmodified-Since"
HeaderLastModified string = "Last-Modified"
HeaderVary string = "Vary"
HeaderConnection string = "Connection"
HeaderKeepAlive string = "Keep-Alive"
HeaderAccept string = "Accept"
HeaderAcceptCharset string = "Accept-Charset"
HeaderAcceptEncoding string = "Accept-Encoding"
HeaderAcceptLanguage string = "Accept-Language"
HeaderCookie string = "Cookie"
HeaderExpect string = "Expect"
HeaderMaxForwards string = "Max-Forwards"
HeaderSetCookie string = "Set-Cookie"
HeaderAccessControlAllowCredentials string = "Access-Control-Allow-Credentials"
HeaderAccessControlAllowHeaders string = "Access-Control-Allow-Headers"
HeaderAccessControlAllowMethods string = "Access-Control-Allow-Methods"
HeaderAccessControlAllowOrigin string = "Access-Control-Allow-Origin"
HeaderAccessControlExposeHeaders string = "Access-Control-Expose-Headers"
HeaderAccessControlMaxAge string = "Access-Control-Max-Age"
HeaderAccessControlRequestHeaders string = "Access-Control-Request-Headers"
HeaderAccessControlRequestMethod string = "Access-Control-Request-Method"
HeaderOrigin string = "Origin"
HeaderTimingAllowOrigin string = "Timing-Allow-Origin"
HeaderXPermittedCrossDomainPolicies string = "X-Permitted-Cross-Domain-Policies"
HeaderDNT string = "DNT"
HeaderTk string = "Tk"
HeaderContentDisposition string = "Content-Disposition"
HeaderContentEncoding string = "Content-Encoding"
HeaderContentLanguage string = "Content-Language"
HeaderContentLength string = "Content-Length"
HeaderContentLocation string = "Content-Location"
HeaderContentType string = "Content-Type"
HeaderForwarded string = "Forwarded"
HeaderVia string = "Via"
HeaderXForwardedFor string = "X-Forwarded-For"
HeaderXForwardedHost string = "X-Forwarded-Host"
HeaderXForwardedProto string = "X-Forwarded-Proto"
HeaderXForwardedProtocol string = "X-Forwarded-Protocol"
HeaderXForwardedSsl string = "X-Forwarded-Ssl"
HeaderXUrlScheme string = "X-Url-Scheme"
HeaderRealIp string = "X-Real-IP"
HeaderLocation string = "Location"
HeaderFrom string = "From"
HeaderHost string = "Host"
HeaderReferer string = "Referer"
HeaderReferrerPolicy string = "Referrer-Policy"
HeaderUserAgent string = "User-Agent"
HeaderAllow string = "Allow"
HeaderServer string = "Server"
HeaderAcceptRanges string = "Accept-Ranges"
HeaderContentRange string = "Content-Range"
HeaderIfRange string = "If-Range"
HeaderRange string = "Range"
HeaderContentSecurityPolicy string = "Content-Security-Policy"
HeaderContentSecurityPolicyReportOnly string = "Content-Security-Policy-Report-Only"
HeaderCrossOriginResourcePolicy string = "Cross-Origin-Resource-Policy"
HeaderExpectCT string = "Expect-CT"
HeaderFeaturePolicy string = "Feature-Policy"
HeaderPublicKeyPins string = "Public-Key-Pins"
HeaderPublicKeyPinsReportOnly string = "Public-Key-Pins-Report-Only"
HeaderStrictTransportSecurity string = "Strict-Transport-Security"
HeaderUpgradeInsecureRequests string = "Upgrade-Insecure-Requests"
HeaderXContentTypeOptions string = "X-Content-Type-Options"
HeaderXDownloadOptions string = "X-Download-Options"
HeaderXFrameOptions string = "X-Frame-Options"
HeaderXPoweredBy string = "X-Powered-By"
HeaderXXSSProtection string = "X-XSS-Protection"
HeaderLastEventID string = "Last-Event-ID"
HeaderNEL string = "NEL"
HeaderPingFrom string = "Ping-From"
HeaderPingTo string = "Ping-To"
HeaderReportTo string = "Report-To"
HeaderTE string = "TE"
HeaderTrailer string = "Trailer"
HeaderTransferEncoding string = "Transfer-Encoding"
HeaderSecWebSocketAccept string = "Sec-WebSocket-Accept"
HeaderSecWebSocketExtensions string = "Sec-WebSocket-Extensions"
HeaderSecWebSocketKey string = "Sec-WebSocket-Key"
HeaderSecWebSocketProtocol string = "Sec-WebSocket-Protocol"
HeaderSecWebSocketVersion string = "Sec-WebSocket-Version"
HeaderAcceptPatch string = "Accept-Patch"
HeaderAcceptPushPolicy string = "Accept-Push-Policy"
HeaderAcceptSignature string = "Accept-Signature"
HeaderAltSvc string = "Alt-Svc"
HeaderDate string = "Date"
HeaderIndex string = "Index"
HeaderLargeAllocation string = "Large-Allocation"
HeaderLink string = "Link"
HeaderPushPolicy string = "Push-Policy"
HeaderRetryAfter string = "Retry-After"
HeaderServerTiming string = "Server-Timing"
HeaderSignature string = "Signature"
HeaderSignedHeaders string = "Signed-Headers"
HeaderSourceMap string = "SourceMap"
HeaderUpgrade string = "Upgrade"
HeaderXDNSPrefetchControl string = "X-DNS-Prefetch-Control"
HeaderXPingback string = "X-Pingback"
HeaderXRequestID string = "X-Request-ID"
HeaderXRequestedWith string = "X-Requested-With"
HeaderXRobotsTag string = "X-Robots-Tag"
HeaderXUACompatible string = "X-UA-Compatible"
) )
// BodyBytesKey indicates a default body bytes key. // BodyBytesKey indicates a default body bytes key.

View File

@ -1500,6 +1500,22 @@ func TestContextHeaders(t *testing.T) {
assert.False(t, exist) assert.False(t, exist)
} }
func TestContextHeadersConstants(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder())
c.Header(HeaderContentType, "text/plain")
c.Header("X-Custom", "value")
assert.Equal(t, "text/plain", c.Writer.Header().Get(HeaderContentType))
assert.Equal(t, "value", c.Writer.Header().Get("X-Custom"))
c.Header(HeaderContentType, "text/html")
c.Header("X-Custom", "")
assert.Equal(t, "text/html", c.Writer.Header().Get(HeaderContentType))
_, exist := c.Writer.Header()["X-Custom"]
assert.False(t, exist)
}
// TODO // TODO
func TestContextRenderRedirectWithRelativePath(t *testing.T) { func TestContextRenderRedirectWithRelativePath(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
@ -2009,6 +2025,137 @@ func TestContextClientIP(t *testing.T) {
assert.Empty(t, c.ClientIP()) assert.Empty(t, c.ClientIP())
} }
func TestContextClientIPConstants(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
c.engine.trustedCIDRs, _ = c.engine.prepareTrustedCIDRs()
resetContextForClientIPTests(c)
// Legacy tests (validating that the defaults don't break the
// (insecure!) old behaviour)
assert.Equal(t, "20.20.20.20", c.ClientIP())
c.Request.Header.Del(HeaderXForwardedFor)
assert.Equal(t, "10.10.10.10", c.ClientIP())
c.Request.Header.Set(HeaderXForwardedFor, "30.30.30.30 ")
assert.Equal(t, "30.30.30.30", c.ClientIP())
c.Request.Header.Del(HeaderXForwardedFor)
c.Request.Header.Del(HeaderRealIp)
c.engine.TrustedPlatform = PlatformGoogleAppEngine
assert.Equal(t, "50.50.50.50", c.ClientIP())
c.Request.Header.Del("X-Appengine-Remote-Addr")
assert.Equal(t, "40.40.40.40", c.ClientIP())
// no port
c.Request.RemoteAddr = "50.50.50.50"
assert.Empty(t, c.ClientIP())
// Tests exercising the TrustedProxies functionality
resetContextForClientIPTests(c)
// IPv6 support
c.Request.RemoteAddr = "[::1]:12345"
assert.Equal(t, "20.20.20.20", c.ClientIP())
resetContextForClientIPTests(c)
// No trusted proxies
_ = c.engine.SetTrustedProxies([]string{})
c.engine.RemoteIPHeaders = []string{HeaderXForwardedFor}
assert.Equal(t, "40.40.40.40", c.ClientIP())
// Disabled TrustedProxies feature
_ = c.engine.SetTrustedProxies(nil)
assert.Equal(t, "40.40.40.40", c.ClientIP())
// Last proxy is trusted, but the RemoteAddr is not
_ = c.engine.SetTrustedProxies([]string{"30.30.30.30"})
assert.Equal(t, "40.40.40.40", c.ClientIP())
// Only trust RemoteAddr
_ = c.engine.SetTrustedProxies([]string{"40.40.40.40"})
assert.Equal(t, "30.30.30.30", c.ClientIP())
// All steps are trusted
_ = c.engine.SetTrustedProxies([]string{"40.40.40.40", "30.30.30.30", "20.20.20.20"})
assert.Equal(t, "20.20.20.20", c.ClientIP())
// Use CIDR
_ = c.engine.SetTrustedProxies([]string{"40.40.25.25/16", "30.30.30.30"})
assert.Equal(t, "20.20.20.20", c.ClientIP())
// Use hostname that resolves to all the proxies
_ = c.engine.SetTrustedProxies([]string{"foo"})
assert.Equal(t, "40.40.40.40", c.ClientIP())
// Use hostname that returns an error
_ = c.engine.SetTrustedProxies([]string{"bar"})
assert.Equal(t, "40.40.40.40", c.ClientIP())
// X-Forwarded-For has a non-IP element
_ = c.engine.SetTrustedProxies([]string{"40.40.40.40"})
c.Request.Header.Set("X-Forwarded-For", " blah ")
assert.Equal(t, "40.40.40.40", c.ClientIP())
// Result from LookupHost has non-IP element. This should never
// happen, but we should test it to make sure we handle it
// gracefully.
_ = c.engine.SetTrustedProxies([]string{"baz"})
c.Request.Header.Set("X-Forwarded-For", " 30.30.30.30 ")
assert.Equal(t, "40.40.40.40", c.ClientIP())
_ = c.engine.SetTrustedProxies([]string{"40.40.40.40"})
c.Request.Header.Del("X-Forwarded-For")
c.engine.RemoteIPHeaders = []string{HeaderXForwardedFor, HeaderRealIp}
assert.Equal(t, "10.10.10.10", c.ClientIP())
c.engine.RemoteIPHeaders = []string{}
c.engine.TrustedPlatform = PlatformGoogleAppEngine
assert.Equal(t, "50.50.50.50", c.ClientIP())
// Use custom TrustedPlatform header
c.engine.TrustedPlatform = "X-CDN-IP"
c.Request.Header.Set("X-CDN-IP", "80.80.80.80")
assert.Equal(t, "80.80.80.80", c.ClientIP())
// wrong header
c.engine.TrustedPlatform = "X-Wrong-Header"
assert.Equal(t, "40.40.40.40", c.ClientIP())
c.Request.Header.Del("X-CDN-IP")
// TrustedPlatform is empty
c.engine.TrustedPlatform = ""
assert.Equal(t, "40.40.40.40", c.ClientIP())
// Test the legacy flag
c.engine.AppEngine = true
assert.Equal(t, "50.50.50.50", c.ClientIP())
c.engine.AppEngine = false
c.engine.TrustedPlatform = PlatformGoogleAppEngine
c.Request.Header.Del("X-Appengine-Remote-Addr")
assert.Equal(t, "40.40.40.40", c.ClientIP())
c.engine.TrustedPlatform = PlatformCloudflare
assert.Equal(t, "60.60.60.60", c.ClientIP())
c.Request.Header.Del("CF-Connecting-IP")
assert.Equal(t, "40.40.40.40", c.ClientIP())
c.engine.TrustedPlatform = PlatformFlyIO
assert.Equal(t, "70.70.70.70", c.ClientIP())
c.Request.Header.Del("Fly-Client-IP")
assert.Equal(t, "40.40.40.40", c.ClientIP())
c.engine.TrustedPlatform = ""
// no port
c.Request.RemoteAddr = "50.50.50.50"
assert.Empty(t, c.ClientIP())
}
func resetContextForClientIPTests(c *Context) { func resetContextForClientIPTests(c *Context) {
c.Request.Header.Set("X-Real-IP", " 10.10.10.10 ") c.Request.Header.Set("X-Real-IP", " 10.10.10.10 ")
c.Request.Header.Set("X-Forwarded-For", " 20.20.20.20, 30.30.30.30") c.Request.Header.Set("X-Forwarded-For", " 20.20.20.20, 30.30.30.30")

View File

@ -34,6 +34,7 @@
- [Bind HTML checkboxes](#bind-html-checkboxes) - [Bind HTML checkboxes](#bind-html-checkboxes)
- [Multipart/Urlencoded binding](#multiparturlencoded-binding) - [Multipart/Urlencoded binding](#multiparturlencoded-binding)
- [XML, JSON, YAML, TOML and ProtoBuf rendering](#xml-json-yaml-toml-and-protobuf-rendering) - [XML, JSON, YAML, TOML and ProtoBuf rendering](#xml-json-yaml-toml-and-protobuf-rendering)
- [Header Constants](#header-constants)
- [SecureJSON](#securejson) - [SecureJSON](#securejson)
- [JSONP](#jsonp) - [JSONP](#jsonp)
- [AsciiJSON](#asciijson) - [AsciiJSON](#asciijson)
@ -2431,6 +2432,141 @@ func main() {
} }
``` ```
### header-constants
```go
const (
HeaderAuthorization string = "Authorization"
HeaderProxyAuthenticate string = "Proxy-Authenticate"
HeaderProxyAuthorization string = "Proxy-Authorization"
HeaderWWWAuthenticate string = "WWW-Authenticate"
HeaderAge string = "Age"
HeaderCacheControl string = "Cache-Control"
HeaderClearSiteData string = "Clear-Site-Data"
HeaderExpires string = "Expires"
HeaderPragma string = "Pragma"
HeaderWarning string = "Warning"
HeaderAcceptCH string = "Accept-CH"
HeaderAcceptCHLifetime string = "Accept-CH-Lifetime"
HeaderContentDPR string = "Content-DPR"
HeaderDPR string = "DPR"
HeaderEarlyData string = "Early-Data"
HeaderSaveData string = "Save-Data"
HeaderViewportWidth string = "Viewport-Width"
HeaderWidth string = "Width"
HeaderETag string = "ETag"
HeaderIfMatch string = "If-Match"
HeaderIfModifiedSince string = "If-Modified-Since"
HeaderIfNoneMatch string = "If-None-Match"
HeaderIfUnmodifiedSince string = "If-Unmodified-Since"
HeaderLastModified string = "Last-Modified"
HeaderVary string = "Vary"
HeaderConnection string = "Connection"
HeaderKeepAlive string = "Keep-Alive"
HeaderAccept string = "Accept"
HeaderAcceptCharset string = "Accept-Charset"
HeaderAcceptEncoding string = "Accept-Encoding"
HeaderAcceptLanguage string = "Accept-Language"
HeaderCookie string = "Cookie"
HeaderExpect string = "Expect"
HeaderMaxForwards string = "Max-Forwards"
HeaderSetCookie string = "Set-Cookie"
HeaderAccessControlAllowCredentials string = "Access-Control-Allow-Credentials"
HeaderAccessControlAllowHeaders string = "Access-Control-Allow-Headers"
HeaderAccessControlAllowMethods string = "Access-Control-Allow-Methods"
HeaderAccessControlAllowOrigin string = "Access-Control-Allow-Origin"
HeaderAccessControlExposeHeaders string = "Access-Control-Expose-Headers"
HeaderAccessControlMaxAge string = "Access-Control-Max-Age"
HeaderAccessControlRequestHeaders string = "Access-Control-Request-Headers"
HeaderAccessControlRequestMethod string = "Access-Control-Request-Method"
HeaderOrigin string = "Origin"
HeaderTimingAllowOrigin string = "Timing-Allow-Origin"
HeaderXPermittedCrossDomainPolicies string = "X-Permitted-Cross-Domain-Policies"
HeaderDNT string = "DNT"
HeaderTk string = "Tk"
HeaderContentDisposition string = "Content-Disposition"
HeaderContentEncoding string = "Content-Encoding"
HeaderContentLanguage string = "Content-Language"
HeaderContentLength string = "Content-Length"
HeaderContentLocation string = "Content-Location"
HeaderContentType string = "Content-Type"
HeaderForwarded string = "Forwarded"
HeaderVia string = "Via"
HeaderXForwardedFor string = "X-Forwarded-For"
HeaderXForwardedHost string = "X-Forwarded-Host"
HeaderXForwardedProto string = "X-Forwarded-Proto"
HeaderXForwardedProtocol string = "X-Forwarded-Protocol"
HeaderXForwardedSsl string = "X-Forwarded-Ssl"
HeaderXUrlScheme string = "X-Url-Scheme"
HeaderRealIp string = "X-Real-IP"
HeaderLocation string = "Location"
HeaderFrom string = "From"
HeaderHost string = "Host"
HeaderReferer string = "Referer"
HeaderReferrerPolicy string = "Referrer-Policy"
HeaderUserAgent string = "User-Agent"
HeaderAllow string = "Allow"
HeaderServer string = "Server"
HeaderAcceptRanges string = "Accept-Ranges"
HeaderContentRange string = "Content-Range"
HeaderIfRange string = "If-Range"
HeaderRange string = "Range"
HeaderContentSecurityPolicy string = "Content-Security-Policy"
HeaderContentSecurityPolicyReportOnly string = "Content-Security-Policy-Report-Only"
HeaderCrossOriginResourcePolicy string = "Cross-Origin-Resource-Policy"
HeaderExpectCT string = "Expect-CT"
HeaderFeaturePolicy string = "Feature-Policy"
HeaderPublicKeyPins string = "Public-Key-Pins"
HeaderPublicKeyPinsReportOnly string = "Public-Key-Pins-Report-Only"
HeaderStrictTransportSecurity string = "Strict-Transport-Security"
HeaderUpgradeInsecureRequests string = "Upgrade-Insecure-Requests"
HeaderXContentTypeOptions string = "X-Content-Type-Options"
HeaderXDownloadOptions string = "X-Download-Options"
HeaderXFrameOptions string = "X-Frame-Options"
HeaderXPoweredBy string = "X-Powered-By"
HeaderXXSSProtection string = "X-XSS-Protection"
HeaderLastEventID string = "Last-Event-ID"
HeaderNEL string = "NEL"
HeaderPingFrom string = "Ping-From"
HeaderPingTo string = "Ping-To"
HeaderReportTo string = "Report-To"
HeaderTE string = "TE"
HeaderTrailer string = "Trailer"
HeaderTransferEncoding string = "Transfer-Encoding"
HeaderSecWebSocketAccept string = "Sec-WebSocket-Accept"
HeaderSecWebSocketExtensions string = "Sec-WebSocket-Extensions"
HeaderSecWebSocketKey string = "Sec-WebSocket-Key"
HeaderSecWebSocketProtocol string = "Sec-WebSocket-Protocol"
HeaderSecWebSocketVersion string = "Sec-WebSocket-Version"
HeaderAcceptPatch string = "Accept-Patch"
HeaderAcceptPushPolicy string = "Accept-Push-Policy"
HeaderAcceptSignature string = "Accept-Signature"
HeaderAltSvc string = "Alt-Svc"
HeaderDate string = "Date"
HeaderIndex string = "Index"
HeaderLargeAllocation string = "Large-Allocation"
HeaderLink string = "Link"
HeaderPushPolicy string = "Push-Policy"
HeaderRetryAfter string = "Retry-After"
HeaderServerTiming string = "Server-Timing"
HeaderSignature string = "Signature"
HeaderSignedHeaders string = "Signed-Headers"
HeaderSourceMap string = "SourceMap"
HeaderUpgrade string = "Upgrade"
HeaderXDNSPrefetchControl string = "X-DNS-Prefetch-Control"
HeaderXPingback string = "X-Pingback"
HeaderXRequestID string = "X-Request-ID"
HeaderXRequestedWith string = "X-Requested-With"
HeaderXRobotsTag string = "X-Robots-Tag"
HeaderXUACompatible string = "X-UA-Compatible"
)
```
## Don't trust all proxies ## Don't trust all proxies
Gin lets you specify which headers to hold the real client IP (if any), Gin lets you specify which headers to hold the real client IP (if any),