mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 14:37:00 +00:00
Merge pull request #97385 from FabianKramm/fix-http2-proxy
kubectl proxy: override request host
This commit is contained in:
commit
49e0c4456d
@ -74,6 +74,12 @@ type UpgradeAwareHandler struct {
|
|||||||
RequireSameHostRedirects bool
|
RequireSameHostRedirects bool
|
||||||
// UseRequestLocation will use the incoming request URL when talking to the backend server.
|
// UseRequestLocation will use the incoming request URL when talking to the backend server.
|
||||||
UseRequestLocation bool
|
UseRequestLocation bool
|
||||||
|
// UseLocationHost overrides the HTTP host header in requests to the backend server to use the Host from Location.
|
||||||
|
// This will override the req.Host field of a request, while UseRequestLocation will override the req.URL field
|
||||||
|
// of a request. The req.URL.Host specifies the server to connect to, while the req.Host field
|
||||||
|
// specifies the Host header value to send in the HTTP request. If this is false, the incoming req.Host header will
|
||||||
|
// just be forwarded to the backend server.
|
||||||
|
UseLocationHost bool
|
||||||
// FlushInterval controls how often the standard HTTP proxy will flush content from the upstream.
|
// FlushInterval controls how often the standard HTTP proxy will flush content from the upstream.
|
||||||
FlushInterval time.Duration
|
FlushInterval time.Duration
|
||||||
// MaxBytesPerSec controls the maximum rate for an upstream connection. No rate is imposed if the value is zero.
|
// MaxBytesPerSec controls the maximum rate for an upstream connection. No rate is imposed if the value is zero.
|
||||||
@ -227,6 +233,11 @@ func (h *UpgradeAwareHandler) ServeHTTP(w http.ResponseWriter, req *http.Request
|
|||||||
if !h.UseRequestLocation {
|
if !h.UseRequestLocation {
|
||||||
newReq.URL = &loc
|
newReq.URL = &loc
|
||||||
}
|
}
|
||||||
|
if h.UseLocationHost {
|
||||||
|
// exchanging req.Host with the backend location is necessary for backends that act on the HTTP host header (e.g. API gateways),
|
||||||
|
// because req.Host has preference over req.URL.Host in filling this header field
|
||||||
|
newReq.Host = h.Location.Host
|
||||||
|
}
|
||||||
|
|
||||||
proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: h.Location.Scheme, Host: h.Location.Host})
|
proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: h.Location.Scheme, Host: h.Location.Host})
|
||||||
proxy.Transport = h.Transport
|
proxy.Transport = h.Transport
|
||||||
@ -282,6 +293,9 @@ func (h *UpgradeAwareHandler) tryUpgrade(w http.ResponseWriter, req *http.Reques
|
|||||||
backendConn, rawResponse, err = utilnet.ConnectWithRedirects(req.Method, &location, clone.Header, req.Body, utilnet.DialerFunc(h.DialForUpgrade), h.RequireSameHostRedirects)
|
backendConn, rawResponse, err = utilnet.ConnectWithRedirects(req.Method, &location, clone.Header, req.Body, utilnet.DialerFunc(h.DialForUpgrade), h.RequireSameHostRedirects)
|
||||||
} else {
|
} else {
|
||||||
klog.V(6).Infof("Connecting to backend proxy (direct dial) %s\n Headers: %v", &location, clone.Header)
|
klog.V(6).Infof("Connecting to backend proxy (direct dial) %s\n Headers: %v", &location, clone.Header)
|
||||||
|
if h.UseLocationHost {
|
||||||
|
clone.Host = h.Location.Host
|
||||||
|
}
|
||||||
clone.URL = &location
|
clone.URL = &location
|
||||||
backendConn, err = h.DialForUpgrade(clone)
|
backendConn, err = h.DialForUpgrade(clone)
|
||||||
}
|
}
|
||||||
|
@ -85,6 +85,7 @@ func (fakeConn) SetWriteDeadline(t time.Time) error { return nil }
|
|||||||
|
|
||||||
type SimpleBackendHandler struct {
|
type SimpleBackendHandler struct {
|
||||||
requestURL url.URL
|
requestURL url.URL
|
||||||
|
requestHost string
|
||||||
requestHeader http.Header
|
requestHeader http.Header
|
||||||
requestBody []byte
|
requestBody []byte
|
||||||
requestMethod string
|
requestMethod string
|
||||||
@ -95,6 +96,7 @@ type SimpleBackendHandler struct {
|
|||||||
|
|
||||||
func (s *SimpleBackendHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
func (s *SimpleBackendHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
s.requestURL = *req.URL
|
s.requestURL = *req.URL
|
||||||
|
s.requestHost = req.Host
|
||||||
s.requestHeader = req.Header
|
s.requestHeader = req.Header
|
||||||
s.requestMethod = req.Method
|
s.requestMethod = req.Method
|
||||||
var err error
|
var err error
|
||||||
@ -162,6 +164,7 @@ func TestServeHTTP(t *testing.T) {
|
|||||||
notExpectedRespHeader []string
|
notExpectedRespHeader []string
|
||||||
upgradeRequired bool
|
upgradeRequired bool
|
||||||
expectError func(err error) bool
|
expectError func(err error) bool
|
||||||
|
useLocationHost bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "root path, simple get",
|
name: "root path, simple get",
|
||||||
@ -222,6 +225,27 @@ func TestServeHTTP(t *testing.T) {
|
|||||||
"Access-Control-Allow-Methods",
|
"Access-Control-Allow-Methods",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "use location host",
|
||||||
|
method: "GET",
|
||||||
|
requestPath: "/some/path",
|
||||||
|
expectedPath: "/some/path",
|
||||||
|
useLocationHost: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "use location host - invalid upgrade",
|
||||||
|
method: "GET",
|
||||||
|
upgradeRequired: true,
|
||||||
|
requestHeader: map[string]string{
|
||||||
|
httpstream.HeaderConnection: httpstream.HeaderUpgrade,
|
||||||
|
},
|
||||||
|
expectError: func(err error) bool {
|
||||||
|
return err != nil && strings.Contains(err.Error(), "invalid upgrade response: status code 200")
|
||||||
|
},
|
||||||
|
requestPath: "/some/path",
|
||||||
|
expectedPath: "/some/path",
|
||||||
|
useLocationHost: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
@ -244,6 +268,7 @@ func TestServeHTTP(t *testing.T) {
|
|||||||
backendURL, _ := url.Parse(backendServer.URL)
|
backendURL, _ := url.Parse(backendServer.URL)
|
||||||
backendURL.Path = test.requestPath
|
backendURL.Path = test.requestPath
|
||||||
proxyHandler := NewUpgradeAwareHandler(backendURL, nil, false, test.upgradeRequired, responder)
|
proxyHandler := NewUpgradeAwareHandler(backendURL, nil, false, test.upgradeRequired, responder)
|
||||||
|
proxyHandler.UseLocationHost = test.useLocationHost
|
||||||
proxyServer := httptest.NewServer(proxyHandler)
|
proxyServer := httptest.NewServer(proxyHandler)
|
||||||
defer proxyServer.Close()
|
defer proxyServer.Close()
|
||||||
proxyURL, _ := url.Parse(proxyServer.URL)
|
proxyURL, _ := url.Parse(proxyServer.URL)
|
||||||
@ -274,6 +299,13 @@ func TestServeHTTP(t *testing.T) {
|
|||||||
t.Errorf("Error from proxy request: %v", err)
|
t.Errorf("Error from proxy request: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Host
|
||||||
|
if test.useLocationHost && backendHandler.requestHost != backendURL.Host {
|
||||||
|
t.Errorf("Unexpected request host: %s", backendHandler.requestHost)
|
||||||
|
} else if !test.useLocationHost && backendHandler.requestHost == backendURL.Host {
|
||||||
|
t.Errorf("Unexpected request host: %s", backendHandler.requestHost)
|
||||||
|
}
|
||||||
|
|
||||||
if test.expectError != nil {
|
if test.expectError != nil {
|
||||||
if !responder.called {
|
if !responder.called {
|
||||||
t.Errorf("%d: responder was not invoked", i)
|
t.Errorf("%d: responder was not invoked", i)
|
||||||
|
@ -211,6 +211,7 @@ func NewProxyHandler(apiProxyPrefix string, filter *FilterServer, cfg *rest.Conf
|
|||||||
proxy := proxy.NewUpgradeAwareHandler(target, transport, false, false, responder)
|
proxy := proxy.NewUpgradeAwareHandler(target, transport, false, false, responder)
|
||||||
proxy.UpgradeTransport = upgradeTransport
|
proxy.UpgradeTransport = upgradeTransport
|
||||||
proxy.UseRequestLocation = true
|
proxy.UseRequestLocation = true
|
||||||
|
proxy.UseLocationHost = true
|
||||||
|
|
||||||
proxyServer := http.Handler(proxy)
|
proxyServer := http.Handler(proxy)
|
||||||
if filter != nil {
|
if filter != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user