mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-16 03:31:39 +00:00
ipn/ipnlocal: initiate proxy transport once (#9883)
Initiates http/h2c transport for userspace proxy backend lazily and at most once. Updates tailscale/tailscale#9725 Signed-off-by: Irbe Krumina <irbe@tailscale.com>
This commit is contained in:
parent
73bbf941f8
commit
f09cb45f9d
@ -31,6 +31,7 @@ import (
|
|||||||
"tailscale.com/net/netutil"
|
"tailscale.com/net/netutil"
|
||||||
"tailscale.com/syncs"
|
"tailscale.com/syncs"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
|
"tailscale.com/types/lazy"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/util/mak"
|
"tailscale.com/util/mak"
|
||||||
"tailscale.com/version"
|
"tailscale.com/version"
|
||||||
@ -568,6 +569,10 @@ type reverseProxy struct {
|
|||||||
insecure bool
|
insecure bool
|
||||||
backend string
|
backend string
|
||||||
lb *LocalBackend
|
lb *LocalBackend
|
||||||
|
// transport for non-h2c backends
|
||||||
|
httpTransport lazy.SyncValue[http.RoundTripper]
|
||||||
|
// transport for h2c backends
|
||||||
|
h2cTransport lazy.SyncValue[http.RoundTripper]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rp *reverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (rp *reverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -586,15 +591,19 @@ func (rp *reverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
// gRPC to fix a known problem pf plaintext gRPC backends
|
// gRPC to fix a known problem pf plaintext gRPC backends
|
||||||
if rp.shouldProxyViaH2C(r) {
|
if rp.shouldProxyViaH2C(r) {
|
||||||
rp.logf("received a proxy request for plaintext gRPC")
|
rp.logf("received a proxy request for plaintext gRPC")
|
||||||
p.Transport = &http2.Transport{
|
p.Transport = rp.getH2CTransport()
|
||||||
AllowHTTP: true,
|
|
||||||
DialTLSContext: func(ctx context.Context, network string, addr string, _ *tls.Config) (net.Conn, error) {
|
|
||||||
return rp.lb.dialer.SystemDial(ctx, "tcp", rp.url.Host)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
p.Transport = &http.Transport{
|
p.Transport = rp.getTransport()
|
||||||
|
}
|
||||||
|
p.ServeHTTP(w, r)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTransport gets transport for http backends. Transport gets created lazily
|
||||||
|
// at most once.
|
||||||
|
func (rp *reverseProxy) getTransport() http.RoundTripper {
|
||||||
|
return rp.httpTransport.Get(func() http.RoundTripper {
|
||||||
|
return &http.Transport{
|
||||||
DialContext: rp.lb.dialer.SystemDial,
|
DialContext: rp.lb.dialer.SystemDial,
|
||||||
TLSClientConfig: &tls.Config{
|
TLSClientConfig: &tls.Config{
|
||||||
InsecureSkipVerify: rp.insecure,
|
InsecureSkipVerify: rp.insecure,
|
||||||
@ -606,9 +615,20 @@ func (rp *reverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
TLSHandshakeTimeout: 10 * time.Second,
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
ExpectContinueTimeout: 1 * time.Second,
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
p.ServeHTTP(w, r)
|
}
|
||||||
|
|
||||||
|
// getH2CTranport gets transport for h2c backends. Creates it lazily at most
|
||||||
|
// once.
|
||||||
|
func (rp *reverseProxy) getH2CTransport() http.RoundTripper {
|
||||||
|
return rp.h2cTransport.Get(func() http.RoundTripper {
|
||||||
|
return &http2.Transport{
|
||||||
|
AllowHTTP: true,
|
||||||
|
DialTLSContext: func(ctx context.Context, network string, addr string, _ *tls.Config) (net.Conn, error) {
|
||||||
|
return rp.lb.dialer.SystemDial(ctx, "tcp", rp.url.Host)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is not a generally reliable way how to determine whether a request is
|
// This is not a generally reliable way how to determine whether a request is
|
||||||
@ -625,7 +645,7 @@ func (rp *reverseProxy) shouldProxyViaH2C(r *http.Request) bool {
|
|||||||
// https://github.com/grpc/grpc-go/blob/v1.60.0-dev/internal/grpcutil/method.go#L41-L78
|
// https://github.com/grpc/grpc-go/blob/v1.60.0-dev/internal/grpcutil/method.go#L41-L78
|
||||||
func isGRPCContentType(contentType string) bool {
|
func isGRPCContentType(contentType string) bool {
|
||||||
s, ok := strings.CutPrefix(contentType, grpcBaseContentType)
|
s, ok := strings.CutPrefix(contentType, grpcBaseContentType)
|
||||||
return ok && len(s) == 0 || s[0] == '+' || s[0] == ';'
|
return ok && (len(s) == 0 || s[0] == '+' || s[0] == ';')
|
||||||
}
|
}
|
||||||
|
|
||||||
func addProxyForwardedHeaders(r *httputil.ProxyRequest) {
|
func addProxyForwardedHeaders(r *httputil.ProxyRequest) {
|
||||||
|
@ -614,6 +614,9 @@ func Test_isGRPCContentType(t *testing.T) {
|
|||||||
"foobar": {
|
"foobar": {
|
||||||
contentType: "foobar",
|
contentType: "foobar",
|
||||||
},
|
},
|
||||||
|
"no content type": {
|
||||||
|
contentType: "",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for name, scenario := range tests {
|
for name, scenario := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user