diff --git a/cliv2/internal/proxy/proxy.go b/cliv2/internal/proxy/proxy.go index 7d56651264..4c511561b8 100644 --- a/cliv2/internal/proxy/proxy.go +++ b/cliv2/internal/proxy/proxy.go @@ -10,6 +10,7 @@ import ( "net/http" "net/url" "os" + "time" "github.com/snyk/cli/cliv2/internal/proxy/interceptor" @@ -221,13 +222,55 @@ func (p *WrapperProxy) Start() error { p.port = l.Addr().(*net.TCPAddr).Port p.DebugLogger.Print("Wrapper proxy is listening on port: ", p.port) + // Start the server in a goroutine + serverErr := make(chan error, 1) go func() { - _ = p.httpServer.Serve(l) // this blocks until the server stops and gives you an error which can be ignored + // http.Server.Serve will return an error if it fails to start + // or nil when the server is shut down + if serveErr := p.httpServer.Serve(l); serveErr != nil && serveErr != http.ErrServerClosed { + serverErr <- serveErr + } }() + // Wait for the server to be ready by attempting to connect to it + // This prevents a race condition where the legacy CLI tries to connect + // before the proxy server is ready to accept connections + err = p.waitForProxyReady(serverErr) + if err != nil { + return fmt.Errorf("proxy server failed to become ready: %w", err) + } + return nil } +// waitForProxyReady waits for the proxy server to be ready to accept connections +// by attempting to connect to it with retries. It also checks for server startup errors. +func (p *WrapperProxy) waitForProxyReady(serverErr chan error) error { + maxRetries := 10 + retryDelay := 50 * time.Millisecond + + for i := 0; i < maxRetries; i++ { + // Check if server failed to start + select { + case err := <-serverErr: + return fmt.Errorf("proxy server failed to start: %w", err) + default: + // Server hasn't reported an error yet, continue checking readiness + } + + // Try to connect to verify the server is ready + conn, err := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", p.port), 100*time.Millisecond) + if err == nil { + conn.Close() + p.DebugLogger.Print("Proxy server is ready") + return nil + } + time.Sleep(retryDelay) + } + + return fmt.Errorf("proxy server did not become ready after %d attempts", maxRetries) +} + func (p *WrapperProxy) Stop() { err := p.httpServer.Shutdown(context.Background()) if err == nil { diff --git a/src/cli/commands/test/index.ts b/src/cli/commands/test/index.ts index 8471e5564d..99e1529b0d 100644 --- a/src/cli/commands/test/index.ts +++ b/src/cli/commands/test/index.ts @@ -44,10 +44,7 @@ import { } from '../../../lib/spotlight-vuln-notification'; import iacTestCommand from './iac'; import * as iacTestCommandV2 from './iac/v2'; -import { - hasFeatureFlag, - hasFeatureFlagOrDefault, -} from '../../../lib/feature-flags'; +import { hasFeatureFlagOrDefault } from '../../../lib/feature-flags'; import { SCAN_USR_LIB_JARS_FEATURE_FLAG, CONTAINER_CLI_APP_VULNS_ENABLED_FEATURE_FLAG, @@ -76,8 +73,8 @@ export default async function test( const options = setDefaultTestOptions(originalOptions); if (originalOptions.iac) { - const iacNewEngine = await hasFeatureFlag('iacNewEngine', options); - const iacIntegratedExperience = await hasFeatureFlag( + const iacNewEngine = await hasFeatureFlagOrDefault('iacNewEngine', options); + const iacIntegratedExperience = await hasFeatureFlagOrDefault( 'iacIntegratedExperience', options, );