mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-19 18:02:01 +00:00
switch spdy round trip tests to simple http proxy
github.com/elazarl/goproxy does not properly handle upgrades over HTTP
proxy. The problem is this misinterpretation:
6691876326/vendor/github.com/elazarl/goproxy/proxy.go (L89-L95)
These should be stripped but recalculated then added back:
https://cs.opensource.google/go/go/+/refs/tags/go1.19.3:src/net/http/httputil/reverseproxy.go;l=292-297;drc=f6d844510d5f1e3b3098eba255d9b633d45eac3b
Let's just stop using goproxy, and use the stdlib instead. The
functionality we need is straightforward to implement and goproxy is
overkill.
This commit is contained in:
parent
c63434aaff
commit
6bdb8ed566
@ -17,6 +17,7 @@ require (
|
|||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/moby/spdystream v0.2.0
|
github.com/moby/spdystream v0.2.0
|
||||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f
|
||||||
|
github.com/onsi/ginkgo/v2 v2.7.0
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.1
|
||||||
golang.org/x/net v0.4.0
|
golang.org/x/net v0.4.0
|
||||||
@ -35,10 +36,10 @@ require (
|
|||||||
github.com/kr/pretty v0.3.0 // indirect
|
github.com/kr/pretty v0.3.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.7.0 // indirect
|
|
||||||
github.com/onsi/gomega v1.24.2 // indirect
|
github.com/onsi/gomega v1.24.2 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
golang.org/x/sys v0.3.0 // indirect
|
||||||
golang.org/x/text v0.5.0 // indirect
|
golang.org/x/text v0.5.0 // indirect
|
||||||
google.golang.org/protobuf v1.28.1 // indirect
|
google.golang.org/protobuf v1.28.1 // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||||
|
1
staging/src/k8s.io/apimachinery/go.sum
generated
1
staging/src/k8s.io/apimachinery/go.sum
generated
@ -125,6 +125,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||||
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||||
|
@ -29,11 +29,11 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/armon/go-socks5"
|
"github.com/armon/go-socks5"
|
||||||
"github.com/elazarl/goproxy"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/httpstream"
|
"k8s.io/apimachinery/pkg/util/httpstream"
|
||||||
|
utilnettesting "k8s.io/apimachinery/pkg/util/net/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type serverHandlerConfig struct {
|
type serverHandlerConfig struct {
|
||||||
@ -313,6 +313,7 @@ func TestRoundTripAndNewConnection(t *testing.T) {
|
|||||||
},
|
},
|
||||||
))
|
))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
t.Logf("Server URL: %v", server.URL)
|
||||||
|
|
||||||
serverURL, err := url.Parse(server.URL)
|
serverURL, err := url.Parse(server.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -330,18 +331,20 @@ func TestRoundTripAndNewConnection(t *testing.T) {
|
|||||||
var proxyCalledWithAuth bool
|
var proxyCalledWithAuth bool
|
||||||
var proxyCalledWithAuthHeader string
|
var proxyCalledWithAuthHeader string
|
||||||
if testCase.proxyServerFunc != nil {
|
if testCase.proxyServerFunc != nil {
|
||||||
proxyHandler := goproxy.NewProxyHttpServer()
|
proxyHandler := utilnettesting.NewHTTPProxyHandler(t, func(req *http.Request) bool {
|
||||||
|
proxyCalledWithHost = req.Host
|
||||||
proxyHandler.OnRequest().HandleConnectFunc(func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
|
|
||||||
proxyCalledWithHost = host
|
|
||||||
|
|
||||||
proxyAuthHeaderName := "Proxy-Authorization"
|
proxyAuthHeaderName := "Proxy-Authorization"
|
||||||
_, proxyCalledWithAuth = ctx.Req.Header[proxyAuthHeaderName]
|
_, proxyCalledWithAuth = req.Header[proxyAuthHeaderName]
|
||||||
proxyCalledWithAuthHeader = ctx.Req.Header.Get(proxyAuthHeaderName)
|
proxyCalledWithAuthHeader = req.Header.Get(proxyAuthHeaderName)
|
||||||
return goproxy.OkConnect, host
|
return true
|
||||||
})
|
})
|
||||||
|
defer proxyHandler.Wait()
|
||||||
|
|
||||||
proxy := testCase.proxyServerFunc(proxyHandler)
|
proxy := testCase.proxyServerFunc(proxyHandler)
|
||||||
|
defer proxy.Close()
|
||||||
|
|
||||||
|
t.Logf("Proxy URL: %v", proxy.URL)
|
||||||
|
|
||||||
spdyTransport.proxier = func(proxierReq *http.Request) (*url.URL, error) {
|
spdyTransport.proxier = func(proxierReq *http.Request) (*url.URL, error) {
|
||||||
proxierCalled = true
|
proxierCalled = true
|
||||||
@ -352,7 +355,6 @@ func TestRoundTripAndNewConnection(t *testing.T) {
|
|||||||
proxyURL.User = testCase.proxyAuth
|
proxyURL.User = testCase.proxyAuth
|
||||||
return proxyURL, nil
|
return proxyURL, nil
|
||||||
}
|
}
|
||||||
defer proxy.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client := &http.Client{Transport: spdyTransport}
|
client := &http.Client{Transport: spdyTransport}
|
||||||
|
150
staging/src/k8s.io/apimachinery/pkg/util/net/testing/http.go
Normal file
150
staging/src/k8s.io/apimachinery/pkg/util/net/testing/http.go
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2023 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package nettesting contains utilities for testing networking functionality.
|
||||||
|
// Don't use these utilities in production code. They have not been security
|
||||||
|
// reviewed.
|
||||||
|
package nettesting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/onsi/ginkgo/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TB interface {
|
||||||
|
Logf(format string, args ...any)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHTTPProxyHandler returns a new HTTPProxyHandler. It accepts an optional
|
||||||
|
// hook which is called early in the handler to export request state. If the
|
||||||
|
// hook returns false, the handler returns immediately with a server error.
|
||||||
|
func NewHTTPProxyHandler(t TB, hook func(req *http.Request) bool) *HTTPProxyHandler {
|
||||||
|
// Ensure that this is only used in tests. This code has not been security
|
||||||
|
// reviewed.
|
||||||
|
switch t.(type) {
|
||||||
|
case testing.TB, ginkgo.GinkgoTInterface:
|
||||||
|
default:
|
||||||
|
panic("t is not a known test interface")
|
||||||
|
}
|
||||||
|
h := &HTTPProxyHandler{
|
||||||
|
hook: hook,
|
||||||
|
httpProxy: httputil.ReverseProxy{
|
||||||
|
Director: func(req *http.Request) {
|
||||||
|
req.URL.Scheme = "http"
|
||||||
|
req.URL.Host = req.Host
|
||||||
|
},
|
||||||
|
},
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPProxyHandler implements a simple handler for http_proxy and https_proxy
|
||||||
|
// requests for use in testing.
|
||||||
|
type HTTPProxyHandler struct {
|
||||||
|
handlerDone sync.WaitGroup
|
||||||
|
hook func(r *http.Request) bool
|
||||||
|
// httpProxy is the reverse proxy we use for standard http proxy requests.
|
||||||
|
httpProxy httputil.ReverseProxy
|
||||||
|
t TB
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTP handles an HTTP proxy request.
|
||||||
|
func (h *HTTPProxyHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
h.handlerDone.Add(1)
|
||||||
|
defer h.handlerDone.Done()
|
||||||
|
|
||||||
|
if h.hook != nil {
|
||||||
|
if ok := h.hook(req); !ok {
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := httputil.DumpRequest(req, false)
|
||||||
|
if err != nil {
|
||||||
|
h.t.Logf("Failed to dump request, host=%s: %v", req.Host, err)
|
||||||
|
} else {
|
||||||
|
h.t.Logf("Proxy Request: %s", string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Method != http.MethodConnect {
|
||||||
|
h.httpProxy.ServeHTTP(rw, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CONNECT proxy
|
||||||
|
|
||||||
|
sconn, err := net.Dial("tcp", req.Host)
|
||||||
|
if err != nil {
|
||||||
|
h.t.Logf("Failed to dial proxy backend, host=%s: %v", req.Host, err)
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer sconn.Close()
|
||||||
|
|
||||||
|
hj, ok := rw.(http.Hijacker)
|
||||||
|
if !ok {
|
||||||
|
h.t.Logf("Can't switch protocols using non-Hijacker ResponseWriter: type=%T, host=%s", rw, req.Host)
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rw.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
conn, brw, err := hj.Hijack()
|
||||||
|
if err != nil {
|
||||||
|
h.t.Logf("Failed to hijack client connection, host=%s: %v", req.Host, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
if err := brw.Flush(); err != nil {
|
||||||
|
h.t.Logf("Failed to flush pending writes to client, host=%s: %v", req.Host, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(sconn, io.LimitReader(brw, int64(brw.Reader.Buffered()))); err != nil {
|
||||||
|
h.t.Logf("Failed to flush buffered reads to server, host=%s: %v", req.Host, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer h.t.Logf("Server read close, host=%s", req.Host)
|
||||||
|
defer wg.Done()
|
||||||
|
io.Copy(conn, sconn)
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
defer h.t.Logf("Server write close, host=%s", req.Host)
|
||||||
|
defer wg.Done()
|
||||||
|
io.Copy(sconn, conn)
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
h.t.Logf("Done handling CONNECT request, host=%s", req.Host)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HTTPProxyHandler) Wait() {
|
||||||
|
h.handlerDone.Wait()
|
||||||
|
}
|
@ -24,7 +24,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
@ -38,7 +37,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/elazarl/goproxy"
|
|
||||||
openapi_v2 "github.com/google/gnostic/openapiv2"
|
openapi_v2 "github.com/google/gnostic/openapiv2"
|
||||||
|
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
@ -52,6 +50,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||||
|
utilnettesting "k8s.io/apimachinery/pkg/util/net/testing"
|
||||||
"k8s.io/apimachinery/pkg/util/uuid"
|
"k8s.io/apimachinery/pkg/util/uuid"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/apiserver/pkg/authentication/serviceaccount"
|
"k8s.io/apiserver/pkg/authentication/serviceaccount"
|
||||||
@ -472,8 +471,12 @@ var _ = SIGDescribe("Kubectl client", func() {
|
|||||||
framework.Failf("--host variable must be set to the full URI to the api server on e2e run.")
|
framework.Failf("--host variable must be set to the full URI to the api server on e2e run.")
|
||||||
}
|
}
|
||||||
|
|
||||||
ginkgo.By("Starting goproxy")
|
ginkgo.By("Starting http_proxy")
|
||||||
testSrv, proxyLogs := startLocalProxy()
|
var proxyLogs bytes.Buffer
|
||||||
|
testSrv := httptest.NewServer(utilnettesting.NewHTTPProxyHandler(ginkgo.GinkgoT(), func(req *http.Request) bool {
|
||||||
|
fmt.Fprintf(&proxyLogs, "Accepting %s to %s\n", req.Method, req.Host)
|
||||||
|
return true
|
||||||
|
}))
|
||||||
defer testSrv.Close()
|
defer testSrv.Close()
|
||||||
proxyAddr := testSrv.URL
|
proxyAddr := testSrv.URL
|
||||||
|
|
||||||
@ -2260,14 +2263,6 @@ func newBlockingReader(s string) (io.Reader, io.Closer, error) {
|
|||||||
return r, w, nil
|
return r, w, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func startLocalProxy() (srv *httptest.Server, logs *bytes.Buffer) {
|
|
||||||
logs = &bytes.Buffer{}
|
|
||||||
p := goproxy.NewProxyHttpServer()
|
|
||||||
p.Verbose = true
|
|
||||||
p.Logger = log.New(logs, "", 0)
|
|
||||||
return httptest.NewServer(p), logs
|
|
||||||
}
|
|
||||||
|
|
||||||
// createApplyCustomResource asserts that given CustomResource be created and applied
|
// createApplyCustomResource asserts that given CustomResource be created and applied
|
||||||
// without being rejected by kubectl validation
|
// without being rejected by kubectl validation
|
||||||
func createApplyCustomResource(resource, namespace, name string, crd *crd.TestCrd) error {
|
func createApplyCustomResource(resource, namespace, name string, crd *crd.TestCrd) error {
|
||||||
|
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
@ -1422,6 +1422,7 @@ k8s.io/apimachinery/pkg/util/managedfields
|
|||||||
k8s.io/apimachinery/pkg/util/mergepatch
|
k8s.io/apimachinery/pkg/util/mergepatch
|
||||||
k8s.io/apimachinery/pkg/util/naming
|
k8s.io/apimachinery/pkg/util/naming
|
||||||
k8s.io/apimachinery/pkg/util/net
|
k8s.io/apimachinery/pkg/util/net
|
||||||
|
k8s.io/apimachinery/pkg/util/net/testing
|
||||||
k8s.io/apimachinery/pkg/util/proxy
|
k8s.io/apimachinery/pkg/util/proxy
|
||||||
k8s.io/apimachinery/pkg/util/rand
|
k8s.io/apimachinery/pkg/util/rand
|
||||||
k8s.io/apimachinery/pkg/util/remotecommand
|
k8s.io/apimachinery/pkg/util/remotecommand
|
||||||
|
Loading…
Reference in New Issue
Block a user