diff --git a/cmd/cloudflared/tunnel/config_test.go b/cmd/cloudflared/tunnel/config_test.go new file mode 100644 index 00000000..edfb77cd --- /dev/null +++ b/cmd/cloudflared/tunnel/config_test.go @@ -0,0 +1,13 @@ +package tunnel + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestDedup(t *testing.T) { + expected := []string{"a", "b"} + actual := dedup([]string{"a", "b", "a"}) + require.Equal(t, expected, actual) +} diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index 559b84ac..5d226f71 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -201,9 +201,10 @@ func prepareTunnelConfig( if err != nil { return nil, ingress.Ingress{}, errors.Wrap(err, "can't generate clientUUID") } + features := append(c.StringSlice("features"), origin.FeatureSerializedHeaders) namedTunnel.Client = tunnelpogs.ClientInfo{ ClientID: clientUUID[:], - Features: []string{origin.FeatureSerializedHeaders}, + Features: dedup(features), Version: version, Arch: fmt.Sprintf("%s_%s", buildInfo.GoOS, buildInfo.GoArch), } @@ -301,3 +302,22 @@ func isWarpRoutingEnabled(warpConfig config.WarpRoutingConfig, isNamedTunnel boo func isRunningFromTerminal() bool { return terminal.IsTerminal(int(os.Stdout.Fd())) } + +// Remove any duplicates from the slice +func dedup(slice []string) []string { + + // Convert the slice into a set + set := make(map[string]bool, 0) + for _, str := range slice { + set[str] = true + } + + // Convert the set back into a slice + keys := make([]string, len(set)) + i := 0 + for str := range set { + keys[i] = str + i++ + } + return keys +} diff --git a/cmd/cloudflared/tunnel/subcommands.go b/cmd/cloudflared/tunnel/subcommands.go index 0852a8df..c8d61c98 100644 --- a/cmd/cloudflared/tunnel/subcommands.go +++ b/cmd/cloudflared/tunnel/subcommands.go @@ -87,6 +87,11 @@ var ( "overwrite the previous tunnel. If you want to use a single hostname with multiple " + "tunnels, you can do so with Cloudflare's Load Balancer product.", }) + featuresFlag = altsrc.NewStringSliceFlag(&cli.StringSliceFlag{ + Name: "features", + Aliases: []string{"F"}, + Usage: "Opt into various features that are still being developed or tested.", + }) credentialsFileFlag = altsrc.NewStringFlag(&cli.StringFlag{ Name: CredFileFlag, Aliases: []string{CredFileFlagAlias}, @@ -370,6 +375,7 @@ func buildRunCommand() *cli.Command { forceFlag, credentialsFileFlag, selectProtocolFlag, + featuresFlag, } flags = append(flags, configureProxyFlags(false)...) return &cli.Command{