Skip to content

Commit bc1d8c6

Browse files
authored
Merge pull request kubernetes#71757 from mikedanese/fixcancel
implement request cancellation in token transport
2 parents 8cf05f5 + a42e029 commit bc1d8c6

File tree

4 files changed

+115
-34
lines changed

4 files changed

+115
-34
lines changed

staging/src/k8s.io/client-go/transport/round_trippers.go

Lines changed: 6 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,6 @@ func DebugWrappers(rt http.RoundTripper) http.RoundTripper {
8080
return rt
8181
}
8282

83-
type requestCanceler interface {
84-
CancelRequest(*http.Request)
85-
}
86-
8783
type authProxyRoundTripper struct {
8884
username string
8985
groups []string
@@ -140,11 +136,7 @@ func SetAuthProxyHeaders(req *http.Request, username string, groups []string, ex
140136
}
141137

142138
func (rt *authProxyRoundTripper) CancelRequest(req *http.Request) {
143-
if canceler, ok := rt.rt.(requestCanceler); ok {
144-
canceler.CancelRequest(req)
145-
} else {
146-
klog.Errorf("CancelRequest not implemented by %T", rt.rt)
147-
}
139+
tryCancelRequest(rt.WrappedRoundTripper(), req)
148140
}
149141

150142
func (rt *authProxyRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt }
@@ -168,11 +160,7 @@ func (rt *userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, e
168160
}
169161

170162
func (rt *userAgentRoundTripper) CancelRequest(req *http.Request) {
171-
if canceler, ok := rt.rt.(requestCanceler); ok {
172-
canceler.CancelRequest(req)
173-
} else {
174-
klog.Errorf("CancelRequest not implemented by %T", rt.rt)
175-
}
163+
tryCancelRequest(rt.WrappedRoundTripper(), req)
176164
}
177165

178166
func (rt *userAgentRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt }
@@ -199,11 +187,7 @@ func (rt *basicAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, e
199187
}
200188

201189
func (rt *basicAuthRoundTripper) CancelRequest(req *http.Request) {
202-
if canceler, ok := rt.rt.(requestCanceler); ok {
203-
canceler.CancelRequest(req)
204-
} else {
205-
klog.Errorf("CancelRequest not implemented by %T", rt.rt)
206-
}
190+
tryCancelRequest(rt.WrappedRoundTripper(), req)
207191
}
208192

209193
func (rt *basicAuthRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt }
@@ -259,11 +243,7 @@ func (rt *impersonatingRoundTripper) RoundTrip(req *http.Request) (*http.Respons
259243
}
260244

261245
func (rt *impersonatingRoundTripper) CancelRequest(req *http.Request) {
262-
if canceler, ok := rt.delegate.(requestCanceler); ok {
263-
canceler.CancelRequest(req)
264-
} else {
265-
klog.Errorf("CancelRequest not implemented by %T", rt.delegate)
266-
}
246+
tryCancelRequest(rt.WrappedRoundTripper(), req)
267247
}
268248

269249
func (rt *impersonatingRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.delegate }
@@ -318,11 +298,7 @@ func (rt *bearerAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response,
318298
}
319299

320300
func (rt *bearerAuthRoundTripper) CancelRequest(req *http.Request) {
321-
if canceler, ok := rt.rt.(requestCanceler); ok {
322-
canceler.CancelRequest(req)
323-
} else {
324-
klog.Errorf("CancelRequest not implemented by %T", rt.rt)
325-
}
301+
tryCancelRequest(rt.WrappedRoundTripper(), req)
326302
}
327303

328304
func (rt *bearerAuthRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt }
@@ -402,11 +378,7 @@ func newDebuggingRoundTripper(rt http.RoundTripper, levels ...debugLevel) *debug
402378
}
403379

404380
func (rt *debuggingRoundTripper) CancelRequest(req *http.Request) {
405-
if canceler, ok := rt.delegatedRoundTripper.(requestCanceler); ok {
406-
canceler.CancelRequest(req)
407-
} else {
408-
klog.Errorf("CancelRequest not implemented by %T", rt.delegatedRoundTripper)
409-
}
381+
tryCancelRequest(rt.WrappedRoundTripper(), req)
410382
}
411383

412384
var knownAuthTypes = map[string]bool{

staging/src/k8s.io/client-go/transport/token_source.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"time"
2626

2727
"golang.org/x/oauth2"
28+
2829
"k8s.io/klog"
2930
)
3031

@@ -81,6 +82,14 @@ func (tst *tokenSourceTransport) RoundTrip(req *http.Request) (*http.Response, e
8182
return tst.ort.RoundTrip(req)
8283
}
8384

85+
func (tst *tokenSourceTransport) CancelRequest(req *http.Request) {
86+
if req.Header.Get("Authorization") != "" {
87+
tryCancelRequest(tst.base, req)
88+
return
89+
}
90+
tryCancelRequest(tst.ort, req)
91+
}
92+
8493
type fileTokenSource struct {
8594
path string
8695
period time.Duration

staging/src/k8s.io/client-go/transport/token_source_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package transport
1818

1919
import (
2020
"fmt"
21+
"net/http"
2122
"reflect"
2223
"sync"
2324
"testing"
@@ -154,3 +155,85 @@ func TestCachingTokenSourceRace(t *testing.T) {
154155
}
155156
}
156157
}
158+
159+
type uncancellableRT struct {
160+
rt http.RoundTripper
161+
}
162+
163+
func (urt *uncancellableRT) RoundTrip(req *http.Request) (*http.Response, error) {
164+
return urt.rt.RoundTrip(req)
165+
}
166+
167+
func TestCancellation(t *testing.T) {
168+
tests := []struct {
169+
name string
170+
header http.Header
171+
wrapTransport func(http.RoundTripper) http.RoundTripper
172+
expectCancel bool
173+
}{
174+
{
175+
name: "cancel req with bearer token skips oauth rt",
176+
header: map[string][]string{"Authorization": {"Bearer TOKEN"}},
177+
expectCancel: true,
178+
},
179+
{
180+
name: "cancel req without bearer token hits both rts",
181+
expectCancel: true,
182+
},
183+
{
184+
name: "cancel req without bearer token hits both wrapped rts",
185+
wrapTransport: func(rt http.RoundTripper) http.RoundTripper {
186+
return NewUserAgentRoundTripper("testing testing", rt)
187+
},
188+
expectCancel: true,
189+
},
190+
{
191+
name: "can't cancel request with rts that doesn't implent unwrap or cancel",
192+
wrapTransport: func(rt http.RoundTripper) http.RoundTripper {
193+
return &uncancellableRT{rt: rt}
194+
},
195+
expectCancel: false,
196+
},
197+
}
198+
for _, test := range tests {
199+
t.Run(test.name, func(t *testing.T) {
200+
baseRecorder := &recordCancelRoundTripper{}
201+
202+
var base http.RoundTripper = baseRecorder
203+
if test.wrapTransport != nil {
204+
base = test.wrapTransport(base)
205+
}
206+
207+
rt := &tokenSourceTransport{
208+
base: base,
209+
ort: &oauth2.Transport{
210+
Base: base,
211+
},
212+
}
213+
214+
rt.CancelRequest(&http.Request{
215+
Header: test.header,
216+
})
217+
218+
if baseRecorder.canceled != test.expectCancel {
219+
t.Errorf("unexpected cancel: got=%v, want=%v", baseRecorder.canceled, test.expectCancel)
220+
}
221+
})
222+
}
223+
}
224+
225+
type recordCancelRoundTripper struct {
226+
canceled bool
227+
base http.RoundTripper
228+
}
229+
230+
func (rt *recordCancelRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
231+
return nil, nil
232+
}
233+
234+
func (rt *recordCancelRoundTripper) CancelRequest(req *http.Request) {
235+
rt.canceled = true
236+
if rt.base != nil {
237+
tryCancelRequest(rt.base, req)
238+
}
239+
}

staging/src/k8s.io/client-go/transport/transport.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ import (
2323
"fmt"
2424
"io/ioutil"
2525
"net/http"
26+
27+
utilnet "k8s.io/apimachinery/pkg/util/net"
28+
"k8s.io/klog"
2629
)
2730

2831
// New returns an http.RoundTripper that will provide the authentication
@@ -225,3 +228,17 @@ func (b *contextCanceller) RoundTrip(req *http.Request) (*http.Response, error)
225228
return b.rt.RoundTrip(req)
226229
}
227230
}
231+
232+
func tryCancelRequest(rt http.RoundTripper, req *http.Request) {
233+
type canceler interface {
234+
CancelRequest(*http.Request)
235+
}
236+
switch rt := rt.(type) {
237+
case canceler:
238+
rt.CancelRequest(req)
239+
case utilnet.RoundTripperWrapper:
240+
tryCancelRequest(rt.WrappedRoundTripper(), req)
241+
default:
242+
klog.Warningf("Unable to cancel request for %T", rt)
243+
}
244+
}

0 commit comments

Comments
 (0)