diff --git a/cmd/toolsrv/internal/bypass/bypass.go b/cmd/toolsrv/internal/bypass/bypass.go index 7e88547..9dcc0e3 100644 --- a/cmd/toolsrv/internal/bypass/bypass.go +++ b/cmd/toolsrv/internal/bypass/bypass.go @@ -4,6 +4,7 @@ package bypass import ( + "context" "crypto/rand" "encoding/hex" "errors" @@ -29,10 +30,10 @@ var ( requests = make(map[string]*Request) ) -// Submit submits a bypass request and blocks until resolved. +// Submit submits a bypass request and blocks until resolved or context cancelled. // Returns (approved, error). If denied, approved is false and error is nil. -// If there's an error (e.g., timeout, connection lost), error is non-nil. -func Submit(cmd, cwd string, env map[string]string) (bool, error) { +// If context is cancelled, returns (false, ctx.Err()). +func Submit(ctx context.Context, cmd, cwd string, env map[string]string) (bool, error) { req := &Request{ ID: nextID(), Cmd: cmd, @@ -45,8 +46,25 @@ func Submit(cmd, cwd string, env map[string]string) (bool, error) { requests[req.ID] = req mu.Unlock() - pending <- req - <-req.done + // Send to pending channel (may block if channel is full) + select { + case pending <- req: + case <-ctx.Done(): + mu.Lock() + delete(requests, req.ID) + mu.Unlock() + return false, ctx.Err() + } + + // Wait for resolution or context cancellation + select { + case <-req.done: + case <-ctx.Done(): + mu.Lock() + delete(requests, req.ID) + mu.Unlock() + return false, ctx.Err() + } mu.Lock() delete(requests, req.ID) diff --git a/cmd/toolsrv/internal/exec/exec.go b/cmd/toolsrv/internal/exec/exec.go index e2fb2fd..f8b6eee 100644 --- a/cmd/toolsrv/internal/exec/exec.go +++ b/cmd/toolsrv/internal/exec/exec.go @@ -240,7 +240,7 @@ func executeBypassDirect(ctx context.Context, cmd, cwd string, envExtra map[stri } // Submit bypass request and wait for approval - approved, err := bypass.Submit(cmd, cwd, envMap) + approved, err := bypass.Submit(ctx, cmd, cwd, envMap) if err != nil { return "", fmt.Errorf("bypass request failed: %w", err) }