mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-05-11 23:36:34 +00:00

Going forward, the only protocols supported will be QUIC and HTTP2, defaulting to QUIC for "auto". Selecting h2mux protocol will be forcibly upgraded to http2 internally.
121 lines
3.7 KiB
Go
121 lines
3.7 KiB
Go
package supervisor
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/lucas-clemente/quic-go"
|
|
"github.com/rs/zerolog"
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/cloudflare/cloudflared/connection"
|
|
"github.com/cloudflare/cloudflared/edgediscovery"
|
|
"github.com/cloudflare/cloudflared/retry"
|
|
)
|
|
|
|
type dynamicMockFetcher struct {
|
|
protocolPercents edgediscovery.ProtocolPercents
|
|
err error
|
|
}
|
|
|
|
func (dmf *dynamicMockFetcher) fetch() edgediscovery.PercentageFetcher {
|
|
return func() (edgediscovery.ProtocolPercents, error) {
|
|
return dmf.protocolPercents, dmf.err
|
|
}
|
|
}
|
|
|
|
func TestWaitForBackoffFallback(t *testing.T) {
|
|
maxRetries := uint(3)
|
|
backoff := retry.BackoffHandler{
|
|
MaxRetries: maxRetries,
|
|
BaseTime: time.Millisecond * 10,
|
|
}
|
|
log := zerolog.Nop()
|
|
resolveTTL := time.Duration(0)
|
|
mockFetcher := dynamicMockFetcher{
|
|
protocolPercents: edgediscovery.ProtocolPercents{edgediscovery.ProtocolPercent{Protocol: "quic", Percentage: 100}},
|
|
}
|
|
protocolSelector, err := connection.NewProtocolSelector(
|
|
"auto",
|
|
"",
|
|
false,
|
|
false,
|
|
mockFetcher.fetch(),
|
|
resolveTTL,
|
|
&log,
|
|
)
|
|
assert.NoError(t, err)
|
|
|
|
initProtocol := protocolSelector.Current()
|
|
assert.Equal(t, connection.QUIC, initProtocol)
|
|
|
|
protoFallback := &protocolFallback{
|
|
backoff,
|
|
initProtocol,
|
|
false,
|
|
}
|
|
|
|
// Retry #0 and #1. At retry #2, we switch protocol, so the fallback loop has one more retry than this
|
|
for i := 0; i < int(maxRetries-1); i++ {
|
|
protoFallback.BackoffTimer() // simulate retry
|
|
ok := selectNextProtocol(&log, protoFallback, protocolSelector, nil)
|
|
assert.True(t, ok)
|
|
assert.Equal(t, initProtocol, protoFallback.protocol)
|
|
}
|
|
|
|
// Retry fallback protocol
|
|
for i := 0; i < int(maxRetries); i++ {
|
|
protoFallback.BackoffTimer() // simulate retry
|
|
ok := selectNextProtocol(&log, protoFallback, protocolSelector, nil)
|
|
assert.True(t, ok)
|
|
fallback, ok := protocolSelector.Fallback()
|
|
assert.True(t, ok)
|
|
assert.Equal(t, fallback, protoFallback.protocol)
|
|
}
|
|
|
|
currentGlobalProtocol := protocolSelector.Current()
|
|
assert.Equal(t, initProtocol, currentGlobalProtocol)
|
|
|
|
// No protocol to fallback, return error
|
|
protoFallback.BackoffTimer() // simulate retry
|
|
ok := selectNextProtocol(&log, protoFallback, protocolSelector, nil)
|
|
assert.False(t, ok)
|
|
|
|
protoFallback.reset()
|
|
protoFallback.BackoffTimer() // simulate retry
|
|
ok = selectNextProtocol(&log, protoFallback, protocolSelector, nil)
|
|
assert.True(t, ok)
|
|
assert.Equal(t, initProtocol, protoFallback.protocol)
|
|
|
|
protoFallback.reset()
|
|
protoFallback.BackoffTimer() // simulate retry
|
|
ok = selectNextProtocol(&log, protoFallback, protocolSelector, &quic.IdleTimeoutError{})
|
|
// Check that we get a true after the first try itself when this flag is true. This allows us to immediately
|
|
// switch protocols when there is a fallback.
|
|
assert.True(t, ok)
|
|
|
|
// But if there is no fallback available, then we exhaust the retries despite the type of error.
|
|
// The reason why there's no fallback available is because we pick a specific protocol instead of letting it be auto.
|
|
protocolSelector, err = connection.NewProtocolSelector(
|
|
"quic",
|
|
"",
|
|
false,
|
|
false,
|
|
mockFetcher.fetch(),
|
|
resolveTTL,
|
|
&log,
|
|
)
|
|
assert.NoError(t, err)
|
|
protoFallback = &protocolFallback{backoff, protocolSelector.Current(), false}
|
|
for i := 0; i < int(maxRetries-1); i++ {
|
|
protoFallback.BackoffTimer() // simulate retry
|
|
ok := selectNextProtocol(&log, protoFallback, protocolSelector, &quic.IdleTimeoutError{})
|
|
assert.True(t, ok)
|
|
assert.Equal(t, connection.QUIC, protoFallback.protocol)
|
|
}
|
|
// And finally it fails as it should, with no fallback.
|
|
protoFallback.BackoffTimer()
|
|
ok = selectNextProtocol(&log, protoFallback, protocolSelector, &quic.IdleTimeoutError{})
|
|
assert.False(t, ok)
|
|
}
|