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
|
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 {
|
func (c *Context) requestHeader(key string) string {
|
||||||
return c.Request.Header.Get(key)
|
return c.Request.Header.Get(key)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ package gin
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
@ -2955,6 +2956,65 @@ func TestWebsocketsRequired(t *testing.T) {
|
|||||||
assert.False(t, c.IsWebsocket())
|
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) {
|
func TestGetRequestHeaderValue(t *testing.T) {
|
||||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
c.Request, _ = http.NewRequest(http.MethodGet, "/chat", nil)
|
c.Request, _ = http.NewRequest(http.MethodGet, "/chat", nil)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user