@@ -100,32 +100,46 @@ func (req *Request) OperationValue(value interface{}) bool {
100
100
return req .values .get (value )
101
101
}
102
102
103
- // SetBody sets the specified ReadSeekCloser as the HTTP request body.
103
+ // SetBody sets the specified ReadSeekCloser as the HTTP request body, and sets Content-Type and Content-Length
104
+ // accordingly. If the ReadSeekCloser is nil or empty, Content-Length won't be set. If contentType is "",
105
+ // Content-Type won't be set.
104
106
func (req * Request ) SetBody (body io.ReadSeekCloser , contentType string ) error {
105
- // Set the body and content length.
106
- size , err := body .Seek (0 , io .SeekEnd ) // Seek to the end to get the stream's size
107
- if err != nil {
108
- return err
107
+ var err error
108
+ var size int64
109
+ if body != nil {
110
+ size , err = body .Seek (0 , io .SeekEnd ) // Seek to the end to get the stream's size
111
+ if err != nil {
112
+ return err
113
+ }
109
114
}
110
115
if size == 0 {
111
- body .Close ()
112
- return nil
113
- }
114
- _ , err = body .Seek (0 , io .SeekStart )
115
- if err != nil {
116
- return err
116
+ // treat an empty stream the same as a nil one: assign req a nil body
117
+ body = nil
118
+ // RFC 9110 specifies a client shouldn't set Content-Length on a request containing no content
119
+ // (Del is a no-op when the header has no value)
120
+ req .req .Header .Del (shared .HeaderContentLength )
121
+ } else {
122
+ _ , err = body .Seek (0 , io .SeekStart )
123
+ if err != nil {
124
+ return err
125
+ }
126
+ req .req .Header .Set (shared .HeaderContentLength , strconv .FormatInt (size , 10 ))
127
+ req .Raw ().GetBody = func () (io.ReadCloser , error ) {
128
+ _ , err := body .Seek (0 , io .SeekStart ) // Seek back to the beginning of the stream
129
+ return body , err
130
+ }
117
131
}
118
- req .Raw ().GetBody = func () (io.ReadCloser , error ) {
119
- _ , err := body .Seek (0 , io .SeekStart ) // Seek back to the beginning of the stream
120
- return body , err
121
- }
122
- // keep a copy of the original body. this is to handle cases
132
+ // keep a copy of the body argument. this is to handle cases
123
133
// where req.Body is replaced, e.g. httputil.DumpRequest and friends.
124
134
req .body = body
125
135
req .req .Body = body
126
136
req .req .ContentLength = size
127
- req .req .Header .Set (shared .HeaderContentType , contentType )
128
- req .req .Header .Set (shared .HeaderContentLength , strconv .FormatInt (size , 10 ))
137
+ if contentType == "" {
138
+ // Del is a no-op when the header has no value
139
+ req .req .Header .Del (shared .HeaderContentType )
140
+ } else {
141
+ req .req .Header .Set (shared .HeaderContentType , contentType )
142
+ }
129
143
return nil
130
144
}
131
145
0 commit comments