2020-03-23 15:48:06 +00:00
|
|
|
package service
|
|
|
|
|
|
|
|
import (
|
2021-11-09 11:16:08 +00:00
|
|
|
"crypto/tls"
|
|
|
|
"net"
|
2020-03-23 15:48:06 +00:00
|
|
|
"net/http"
|
2021-11-09 11:16:08 +00:00
|
|
|
"time"
|
2020-03-23 15:48:06 +00:00
|
|
|
|
2023-02-03 14:24:05 +00:00
|
|
|
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
2020-03-23 15:48:06 +00:00
|
|
|
"golang.org/x/net/http/httpguts"
|
|
|
|
"golang.org/x/net/http2"
|
|
|
|
)
|
|
|
|
|
2024-09-26 09:00:05 +00:00
|
|
|
type h2cTransportWrapper struct {
|
|
|
|
*http2.Transport
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *h2cTransportWrapper) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
|
|
req.URL.Scheme = "http"
|
|
|
|
return t.Transport.RoundTrip(req)
|
|
|
|
}
|
|
|
|
|
2024-02-06 16:34:07 +00:00
|
|
|
func newSmartRoundTripper(transport *http.Transport, forwardingTimeouts *dynamic.ForwardingTimeouts) (*smartRoundTripper, error) {
|
2020-03-23 15:48:06 +00:00
|
|
|
transportHTTP1 := transport.Clone()
|
|
|
|
|
2021-11-09 11:16:08 +00:00
|
|
|
transportHTTP2, err := http2.ConfigureTransports(transport)
|
2020-03-23 15:48:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-11-09 11:16:08 +00:00
|
|
|
if forwardingTimeouts != nil {
|
|
|
|
transportHTTP2.ReadIdleTimeout = time.Duration(forwardingTimeouts.ReadIdleTimeout)
|
|
|
|
transportHTTP2.PingTimeout = time.Duration(forwardingTimeouts.PingTimeout)
|
|
|
|
}
|
|
|
|
|
|
|
|
transportH2C := &h2cTransportWrapper{
|
|
|
|
Transport: &http2.Transport{
|
|
|
|
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
|
|
|
|
return net.Dial(network, addr)
|
|
|
|
},
|
|
|
|
AllowHTTP: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
if forwardingTimeouts != nil {
|
|
|
|
transportH2C.ReadIdleTimeout = time.Duration(forwardingTimeouts.ReadIdleTimeout)
|
|
|
|
transportH2C.PingTimeout = time.Duration(forwardingTimeouts.PingTimeout)
|
|
|
|
}
|
|
|
|
|
|
|
|
transport.RegisterProtocol("h2c", transportH2C)
|
|
|
|
|
2020-03-23 15:48:06 +00:00
|
|
|
return &smartRoundTripper{
|
|
|
|
http2: transport,
|
|
|
|
http: transportHTTP1,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-11-09 11:16:08 +00:00
|
|
|
// smartRoundTripper implements RoundTrip while making sure that HTTP/2 is not used
|
|
|
|
// with protocols that start with a Connection Upgrade, such as SPDY or Websocket.
|
2020-03-23 15:48:06 +00:00
|
|
|
type smartRoundTripper struct {
|
|
|
|
http2 *http.Transport
|
|
|
|
http *http.Transport
|
|
|
|
}
|
|
|
|
|
2024-02-06 16:34:07 +00:00
|
|
|
func (m *smartRoundTripper) Clone() http.RoundTripper {
|
|
|
|
h := m.http.Clone()
|
|
|
|
h2 := m.http2.Clone()
|
|
|
|
return &smartRoundTripper{http: h, http2: h2}
|
|
|
|
}
|
|
|
|
|
2020-03-23 15:48:06 +00:00
|
|
|
func (m *smartRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
|
|
// If we have a connection upgrade, we don't use HTTP/2
|
|
|
|
if httpguts.HeaderValuesContainsToken(req.Header["Connection"], "Upgrade") {
|
|
|
|
return m.http.RoundTrip(req)
|
|
|
|
}
|
|
|
|
|
|
|
|
return m.http2.RoundTrip(req)
|
|
|
|
}
|