diff --git a/api/proxy/option_test.go b/api/proxy/option_test.go index 8525010507..d8021b36d6 100644 --- a/api/proxy/option_test.go +++ b/api/proxy/option_test.go @@ -108,4 +108,13 @@ func Test_GetOpts(t *testing.T) { require.NoError(t, err) assert.Equal(client, opts.withApiClient) }) + t.Run("WithInactivityTimeout", func(t *testing.T) { + assert := assert.New(t) + opts, err := getOpts() + require.NoError(t, err) + assert.Empty(opts.withInactivityTimeout) + opts, err = getOpts(WithInactivityTimeout(3 * time.Millisecond)) + require.NoError(t, err) + assert.Equal(3*time.Millisecond, opts.withInactivityTimeout) + }) } diff --git a/api/proxy/proxy.go b/api/proxy/proxy.go index 99f16bb4f5..32aedd07cf 100644 --- a/api/proxy/proxy.go +++ b/api/proxy/proxy.go @@ -216,11 +216,16 @@ func (p *ClientProxy) Start(opt ...Option) (retErr error) { // Ensure closing the listener runs on any other return condition defer listenerCloseFunc() - // automatically close the proxy when inactive - proxyAutoClose := time.AfterFunc(10*time.Minute, func() { - p.cancel() - p.setCloseReason("Inactivity timeout reached") - }) + var proxyAutoClose *time.Timer + if opts.withInactivityTimeout > 0 { + // automatically close the proxy when inactive + proxyAutoClose = time.AfterFunc(opts.withInactivityTimeout, func() { + p.cancel() + p.setCloseReason("Inactivity timeout reached") + }) + // Stop timer until we have at least one connection, then reset/stop based on activity + proxyAutoClose.Stop() + } activeConnCh := make(chan int32) activeConnFn := func(d int32) { @@ -342,13 +347,16 @@ func (p *ClientProxy) Start(opt ...Option) (retErr error) { } case activeConns := <-activeConnCh: switch { - case activeConns > 0: - // always stop the timer when a new connection is made, - // even if timeout opt is 0 - proxyAutoClose.Stop() + case proxyAutoClose == nil: + // do nothing if timer is not set case opts.withInactivityTimeout <= 0: // no timeout was set, timer should not be reset for inactivity + // this should never happen due to the proxyAutoClose nil check above, but just in case + case activeConns > 0: + // stop timer when we have a new conneciton + proxyAutoClose.Stop() case activeConns == 0: + // reset timer when we have no more active connections proxyAutoClose.Reset(opts.withInactivityTimeout) } } diff --git a/internal/cmd/commands/connect/connect.go b/internal/cmd/commands/connect/connect.go index 30f498ddde..fcf95d33f6 100644 --- a/internal/cmd/commands/connect/connect.go +++ b/internal/cmd/commands/connect/connect.go @@ -54,6 +54,10 @@ type TerminationInfo struct { var ( _ cli.Command = (*Command)(nil) _ cli.CommandAutocomplete = (*Command)(nil) + + // rdpDefaultTimeout is the default inactivity timeout for boundary connect rdp + // The default is zero (no timeout), however it is overridden for macOS clients only in connect_darwin.go + rdpDefaultTimeout time.Duration = 0 ) type Command struct { @@ -516,24 +520,17 @@ func (c *Command) Run(args []string) (retCode int) { clientProxyCloseCh := make(chan struct{}) connCountCloseCh := make(chan struct{}) - if c.flagInactiveTimeout == 0 { - // no timeout was specified by the user, so use our defaults based on subcommand + switch { + case c.flagInactiveTimeout < 0: + // timeout has been disabled, no need for option + case c.flagInactiveTimeout == 0: + // no timeout was specified, use protocol-specific defaults switch c.Func { - case "connect": - // connect is when there is no subcommand specified, this case should - // have the most generous timeout - apiProxyOpts = append(apiProxyOpts, apiproxy.WithInactivityTimeout(30*time.Second)) case "rdp": - // rdp has a gui, so give the user a chance to click "reconnect" - apiProxyOpts = append(apiProxyOpts, apiproxy.WithInactivityTimeout(5*time.Second)) - case "ssh": - // one second is probably enough for ssh - apiProxyOpts = append(apiProxyOpts, apiproxy.WithInactivityTimeout(time.Second)) - default: - // for other protocols, give some extra leeway just in case - apiProxyOpts = append(apiProxyOpts, apiproxy.WithInactivityTimeout(3*time.Second)) + apiProxyOpts = append(apiProxyOpts, apiproxy.WithInactivityTimeout(rdpDefaultTimeout)) } - } else { + default: + // use provided timeout apiProxyOpts = append(apiProxyOpts, apiproxy.WithInactivityTimeout(c.flagInactiveTimeout)) } diff --git a/internal/cmd/commands/connect/connect_darwin.go b/internal/cmd/commands/connect/connect_darwin.go new file mode 100644 index 0000000000..9df6717e95 --- /dev/null +++ b/internal/cmd/commands/connect/connect_darwin.go @@ -0,0 +1,10 @@ +// Copyright IBM Corp. 2020, 2025 +// SPDX-License-Identifier: BUSL-1.1 + +package connect + +import "time" + +func init() { + rdpDefaultTimeout = 30 * time.Second +}