@@ -34,6 +34,7 @@ import (
3434 "time"
3535
3636 "golang.org/x/net/http2"
37+
3738 "k8s.io/apimachinery/pkg/api/errors"
3839 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3940 "k8s.io/apimachinery/pkg/runtime"
@@ -116,8 +117,11 @@ type Request struct {
116117 subresource string
117118
118119 // output
119- err error
120- body io.Reader
120+ err error
121+
122+ // only one of body / bodyBytes may be set. requests using body are not retriable.
123+ body io.Reader
124+ bodyBytes []byte
121125
122126 retryFn requestRetryFunc
123127}
@@ -443,12 +447,15 @@ func (r *Request) Body(obj interface{}) *Request {
443447 return r
444448 }
445449 glogBody ("Request Body" , data )
446- r .body = bytes .NewReader (data )
450+ r .body = nil
451+ r .bodyBytes = data
447452 case []byte :
448453 glogBody ("Request Body" , t )
449- r .body = bytes .NewReader (t )
454+ r .body = nil
455+ r .bodyBytes = t
450456 case io.Reader :
451457 r .body = t
458+ r .bodyBytes = nil
452459 case runtime.Object :
453460 // callers may pass typed interface pointers, therefore we must check nil with reflection
454461 if reflect .ValueOf (t ).IsNil () {
@@ -465,7 +472,8 @@ func (r *Request) Body(obj interface{}) *Request {
465472 return r
466473 }
467474 glogBody ("Request Body" , data )
468- r .body = bytes .NewReader (data )
475+ r .body = nil
476+ r .bodyBytes = data
469477 r .SetHeader ("Content-Type" , r .c .content .ContentType )
470478 default :
471479 r .err = fmt .Errorf ("unknown type used for body: %+v" , obj )
@@ -825,9 +833,6 @@ func (r *Request) Stream(ctx context.Context) (io.ReadCloser, error) {
825833 if err != nil {
826834 return nil , err
827835 }
828- if r .body != nil {
829- req .Body = io .NopCloser (r .body )
830- }
831836 resp , err := client .Do (req )
832837 updateURLMetrics (ctx , r , resp , err )
833838 retry .After (ctx , r , resp , err )
@@ -889,8 +894,20 @@ func (r *Request) requestPreflightCheck() error {
889894}
890895
891896func (r * Request ) newHTTPRequest (ctx context.Context ) (* http.Request , error ) {
897+ var body io.Reader
898+ switch {
899+ case r .body != nil && r .bodyBytes != nil :
900+ return nil , fmt .Errorf ("cannot set both body and bodyBytes" )
901+ case r .body != nil :
902+ body = r .body
903+ case r .bodyBytes != nil :
904+ // Create a new reader specifically for this request.
905+ // Giving each request a dedicated reader allows retries to avoid races resetting the request body.
906+ body = bytes .NewReader (r .bodyBytes )
907+ }
908+
892909 url := r .URL ().String ()
893- req , err := http .NewRequest (r .verb , url , r . body )
910+ req , err := http .NewRequest (r .verb , url , body )
894911 if err != nil {
895912 return nil , err
896913 }
0 commit comments