Merge 85aae676c17fa408a8c8fba005e582a021293403 into 9914178584e42458ff7d23891463a880f58c9d86

This commit is contained in:
Enzo Lanzellotti 2026-01-02 15:50:30 +08:00 committed by GitHub
commit f1305ca301
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
MIMETOML = binding.MIMETOML
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.

View File

@ -1531,6 +1531,22 @@ func TestContextHeaders(t *testing.T) {
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
func TestContextRenderRedirectWithRelativePath(t *testing.T) {
w := httptest.NewRecorder()
@ -2040,6 +2056,137 @@ func TestContextClientIP(t *testing.T) {
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) {
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")

View File

@ -34,6 +34,7 @@
- [Bind HTML checkboxes](#bind-html-checkboxes)
- [Multipart/Urlencoded binding](#multiparturlencoded-binding)
- [XML, JSON, YAML, TOML and ProtoBuf rendering](#xml-json-yaml-toml-and-protobuf-rendering)
- [Header Constants](#header-constants)
- [SecureJSON](#securejson)
- [JSONP](#jsonp)
- [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
Gin lets you specify which headers to hold the real client IP (if any),