mirror of
https://github.com/gin-gonic/gin.git
synced 2026-06-04 01:38:12 +08:00
feat(context): add Scheme() with proper reverse proxy support (#4655)
* feat(context): add Scheme method to determine HTTP scheme from request * test(context): add tests for Scheme method
This commit is contained in:
parent
c79f5d466e
commit
96ece6a141
27
context.go
27
context.go
@ -1047,6 +1047,33 @@ func (c *Context) IsWebsocket() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Scheme returns the HTTP scheme of the request ("http" or "https").
|
||||
// When running behind reverse proxies or load balancers `Request.URL.Scheme` is usually empty.
|
||||
// the original scheme is commonly forwarded via headers such as X-Forwarded-Proto.
|
||||
// Reference:
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto
|
||||
func (c *Context) Scheme() string {
|
||||
if c.Request.TLS != nil {
|
||||
return "https"
|
||||
}
|
||||
if scheme := c.requestHeader("X-Forwarded-Proto"); scheme != "" {
|
||||
return scheme
|
||||
}
|
||||
if scheme := c.requestHeader("X-Forwarded-Protocol"); scheme != "" {
|
||||
return scheme
|
||||
}
|
||||
if ssl := c.requestHeader("X-Forwarded-Ssl"); ssl == "on" {
|
||||
return "https"
|
||||
}
|
||||
if scheme := c.requestHeader("X-Url-Scheme"); scheme != "" {
|
||||
return scheme
|
||||
}
|
||||
if scheme := c.Request.URL.Scheme; scheme != "" {
|
||||
return scheme
|
||||
}
|
||||
return "http"
|
||||
}
|
||||
|
||||
func (c *Context) requestHeader(key string) string {
|
||||
return c.Request.Header.Get(key)
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ package gin
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
@ -2955,6 +2956,65 @@ func TestWebsocketsRequired(t *testing.T) {
|
||||
assert.False(t, c.IsWebsocket())
|
||||
}
|
||||
|
||||
func TestContextScheme(t *testing.T) {
|
||||
// TLS connection takes highest priority.
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
|
||||
c.Request.TLS = &tls.ConnectionState{}
|
||||
assert.Equal(t, "https", c.Scheme())
|
||||
|
||||
// X-Forwarded-Proto header.
|
||||
c, _ = CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
|
||||
c.Request.Header.Set("X-Forwarded-Proto", "https")
|
||||
assert.Equal(t, "https", c.Scheme())
|
||||
|
||||
c, _ = CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
|
||||
c.Request.Header.Set("X-Forwarded-Proto", "http")
|
||||
assert.Equal(t, "http", c.Scheme())
|
||||
|
||||
// X-Forwarded-Protocol header.
|
||||
c, _ = CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
|
||||
c.Request.Header.Set("X-Forwarded-Protocol", "https")
|
||||
assert.Equal(t, "https", c.Scheme())
|
||||
|
||||
// X-Forwarded-Ssl: on header.
|
||||
c, _ = CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
|
||||
c.Request.Header.Set("X-Forwarded-Ssl", "on")
|
||||
assert.Equal(t, "https", c.Scheme())
|
||||
|
||||
c, _ = CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
|
||||
c.Request.Header.Set("X-Forwarded-Ssl", "off")
|
||||
assert.Equal(t, "http", c.Scheme())
|
||||
|
||||
// X-Url-Scheme header.
|
||||
c, _ = CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
|
||||
c.Request.Header.Set("X-Url-Scheme", "https")
|
||||
assert.Equal(t, "https", c.Scheme())
|
||||
|
||||
// Request.URL.Scheme fallback.
|
||||
c, _ = CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest(http.MethodGet, "https://example.com/", nil)
|
||||
assert.Equal(t, "https", c.Scheme())
|
||||
|
||||
// Default fallback: plain http.
|
||||
c, _ = CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
|
||||
assert.Equal(t, "http", c.Scheme())
|
||||
|
||||
// TLS takes priority over X-Forwarded-Proto.
|
||||
c, _ = CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
|
||||
c.Request.TLS = &tls.ConnectionState{}
|
||||
c.Request.Header.Set("X-Forwarded-Proto", "http")
|
||||
assert.Equal(t, "https", c.Scheme())
|
||||
}
|
||||
|
||||
func TestGetRequestHeaderValue(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest(http.MethodGet, "/chat", nil)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user