diff --git a/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/command_headers.go b/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/command_headers.go index 04889da094a..0eea122fe67 100644 --- a/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/command_headers.go +++ b/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/command_headers.go @@ -77,3 +77,15 @@ func (c *CommandHeaderRoundTripper) ParseCommandHeaders(cmd *cobra.Command, args c.Headers[kubectlCommandHeader] = strings.Join(cmdStrs, " ") } } + +// CancelRequest is propagated to the Delegate RoundTripper within +// if the wrapped RoundTripper implements this function. +func (c *CommandHeaderRoundTripper) CancelRequest(req *http.Request) { + type canceler interface { + CancelRequest(*http.Request) + } + // If possible, call "CancelRequest" on the wrapped Delegate RoundTripper. + if cr, ok := c.Delegate.(canceler); ok { + cr.CancelRequest(req) + } +} diff --git a/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/command_headers_test.go b/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/command_headers_test.go index b95f8cacc95..7a692a56e4c 100644 --- a/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/command_headers_test.go +++ b/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/command_headers_test.go @@ -17,6 +17,7 @@ limitations under the License. package genericclioptions import ( + "net/http" "testing" "github.com/spf13/cobra" @@ -103,3 +104,52 @@ func buildCommandChain(commands []*cobra.Command) *cobra.Command { } return currCmd } + +// Tests that the CancelRequest function is propogated to the wrapped Delegate +// RoundTripper; but only if the Delegate implements the CancelRequest function. +func TestCancelRequest(t *testing.T) { + tests := map[string]struct { + delegate http.RoundTripper + cancelled bool + }{ + "CancelRequest propagated to delegate": { + delegate: &cancellableRoundTripper{}, + cancelled: true, + }, + "CancelRequest not propagated to delegate": { + delegate: &nonCancellableRoundTripper{}, + cancelled: false, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + rt := &CommandHeaderRoundTripper{ + Delegate: tc.delegate, + } + req := http.Request{} + rt.CancelRequest(&req) + if tc.cancelled != req.Close { + t.Errorf("expected RoundTripper cancel (%v), got (%v)", tc.cancelled, req.Close) + } + }) + } +} + +// Test RoundTripper with CancelRequest function. +type cancellableRoundTripper struct{} + +func (rtc *cancellableRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + return nil, nil +} + +func (rtc *cancellableRoundTripper) CancelRequest(req *http.Request) { + req.Close = true +} + +// Test RoundTripper without CancelRequest function. +type nonCancellableRoundTripper struct{} + +func (rtc *nonCancellableRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + return nil, nil +}