Skip to content

Commit 4604150

Browse files
authored
Add Benchmarks and Unit tests for parseRequestBody and improve it (#714)
* Unit tests and Benchmarks for parseRequestBody function ```shell % go test -benchmem -bench=. -run=^Benchmark goos: darwin goarch: amd64 pkg: github.com/go-resty/resty/v2 cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz Benchmark_parseRequestBody_string-16 2194669 550.2 ns/op 128 B/op 3 allocs/op Benchmark_parseRequestBody_byte-16 2260675 531.5 ns/op 128 B/op 3 allocs/op Benchmark_parseRequestBody_reader_with_SetContentLength-16 8393974 141.4 ns/op 16 B/op 1 allocs/op Benchmark_parseRequestBody_reader_without_SetContentLength-16 14253069 84.30 ns/op 0 B/op 0 allocs/op Benchmark_parseRequestBody_struct-16 880549 1307 ns/op 155 B/op 5 allocs/op Benchmark_parseRequestBody_struct_xml-16 424707 2886 ns/op 4762 B/op 13 allocs/op Benchmark_parseRequestBody_map-16 547629 2152 ns/op 569 B/op 15 allocs/op Benchmark_parseRequestBody_slice-16 959576 1264 ns/op 146 B/op 4 allocs/op Benchmark_parseRequestBody_FormData-16 973964 1243 ns/op 304 B/op 14 allocs/op Benchmark_parseRequestBody_MultiPart-16 98246 12320 ns/op 8746 B/op 131 allocs/op ``` * improve handleFormData Benchmarks: ``` Old: Benchmark_parseRequestBody_FormData-16 954213 1266 ns/op 304 B/op 14 allocs/op New: Benchmark_parseRequestBody_FormData-16 968466 1248 ns/op 280 B/op 10 allocs/op ``` * improve handleRequestBody Benchmarks: ``` Old: Benchmark_parseRequestBody_string-16 2199196 550.3 ns/op 128 B/op 3 allocs/op Benchmark_parseRequestBody_byte-16 2264421 532.9 ns/op 128 B/op 3 allocs/op Benchmark_parseRequestBody_reader-16 8307141 141.8 ns/op 16 B/op 1 allocs/op Benchmark_parseRequestBody_struct-16 931632 1317 ns/op 156 B/op 5 allocs/op Benchmark_parseRequestBody_struct_xml-16 409074 2921 ns/op 4765 B/op 13 allocs/op Benchmark_parseRequestBody_map-16 566750 2158 ns/op 570 B/op 15 allocs/op Benchmark_parseRequestBody_slice-16 957828 1279 ns/op 146 B/op 4 allocs/op New: Benchmark_parseRequestBody_string-16 5084247 237.0 ns/op 16 B/op 1 allocs/op Benchmark_parseRequestBody_byte-16 5298362 218.0 ns/op 16 B/op 1 allocs/op Benchmark_parseRequestBody_reader-16 8402954 141.3 ns/op 16 B/op 1 allocs/op Benchmark_parseRequestBody_struct-16 1000000 1066 ns/op 42 B/op 3 allocs/op Benchmark_parseRequestBody_struct_xml-16 452389 2575 ns/op 4648 B/op 11 allocs/op Benchmark_parseRequestBody_map-16 620391 1913 ns/op 457 B/op 13 allocs/op Benchmark_parseRequestBody_slice-16 1207551 1203 ns/op 32 B/op 2 allocs/op ``` * improve parseRequesBody and add additional benchmarks Final benchmarks: ```shell % go test -benchmem -bench=. -run=^Benchmark goos: darwin goarch: amd64 pkg: github.com/go-resty/resty/v2 cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz Benchmark_parseRequestBody_string-16 7623501 155.3 ns/op 16 B/op 1 allocs/op Benchmark_parseRequestBody_byte-16 8421992 141.6 ns/op 16 B/op 1 allocs/op Benchmark_parseRequestBody_reader_with_SetContentLength-16 17632350 67.37 ns/op 16 B/op 1 allocs/op Benchmark_parseRequestBody_reader_without_SetContentLength-16 26575016 45.34 ns/op 0 B/op 0 allocs/op Benchmark_parseRequestBody_struct-16 1243986 953.8 ns/op 40 B/op 2 allocs/op Benchmark_parseRequestBody_struct_xml-16 495250 2458 ns/op 4647 B/op 10 allocs/op Benchmark_parseRequestBody_map-16 694786 1761 ns/op 454 B/op 12 allocs/op Benchmark_parseRequestBody_slice-16 1304724 913.1 ns/op 32 B/op 2 allocs/op Benchmark_parseRequestBody_FormData-16 1000000 1128 ns/op 272 B/op 9 allocs/op Benchmark_parseRequestBody_MultiPart-16 93248 12583 ns/op 8738 B/op 130 allocs/op ``` * add error test cases * use r.FormData inatead of creating new variable
1 parent 6310546 commit 4604150

File tree

2 files changed

+681
-83
lines changed

2 files changed

+681
-83
lines changed

middleware.go

Lines changed: 53 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"os"
1616
"path/filepath"
1717
"reflect"
18+
"strconv"
1819
"strings"
1920
"time"
2021
)
@@ -134,45 +135,34 @@ func parseRequestHeader(c *Client, r *Request) error {
134135
return nil
135136
}
136137

137-
func parseRequestBody(c *Client, r *Request) (err error) {
138+
func parseRequestBody(c *Client, r *Request) error {
138139
if isPayloadSupported(r.Method, c.AllowGetMethodPayload) {
139-
// Handling Multipart
140-
if r.isMultiPart {
141-
if err = handleMultipart(c, r); err != nil {
142-
return
140+
switch {
141+
case r.isMultiPart: // Handling Multipart
142+
if err := handleMultipart(c, r); err != nil {
143+
return err
143144
}
144-
145-
goto CL
146-
}
147-
148-
// Handling Form Data
149-
if len(c.FormData) > 0 || len(r.FormData) > 0 {
145+
case len(c.FormData) > 0 || len(r.FormData) > 0: // Handling Form Data
150146
handleFormData(c, r)
151-
152-
goto CL
153-
}
154-
155-
// Handling Request body
156-
if r.Body != nil {
147+
case r.Body != nil: // Handling Request body
157148
handleContentType(c, r)
158149

159-
if err = handleRequestBody(c, r); err != nil {
160-
return
150+
if err := handleRequestBody(c, r); err != nil {
151+
return err
161152
}
162153
}
163154
}
164155

165-
CL:
166156
// by default resty won't set content length, you can if you want to :)
167157
if c.setContentLength || r.setContentLength {
168158
if r.bodyBuf == nil {
169159
r.Header.Set(hdrContentLengthKey, "0")
170160
} else {
171-
r.Header.Set(hdrContentLengthKey, fmt.Sprintf("%d", r.bodyBuf.Len()))
161+
r.Header.Set(hdrContentLengthKey, strconv.Itoa(r.bodyBuf.Len()))
172162
}
173163
}
174164

175-
return
165+
return nil
176166
}
177167

178168
func createHTTPRequest(c *Client, r *Request) (err error) {
@@ -370,13 +360,13 @@ func parseResponseBody(c *Client, res *Response) (err error) {
370360
return
371361
}
372362

373-
func handleMultipart(c *Client, r *Request) (err error) {
363+
func handleMultipart(c *Client, r *Request) error {
374364
r.bodyBuf = acquireBuffer()
375365
w := multipart.NewWriter(r.bodyBuf)
376366

377367
for k, v := range c.FormData {
378368
for _, iv := range v {
379-
if err = w.WriteField(k, iv); err != nil {
369+
if err := w.WriteField(k, iv); err != nil {
380370
return err
381371
}
382372
}
@@ -385,63 +375,45 @@ func handleMultipart(c *Client, r *Request) (err error) {
385375
for k, v := range r.FormData {
386376
for _, iv := range v {
387377
if strings.HasPrefix(k, "@") { // file
388-
err = addFile(w, k[1:], iv)
389-
if err != nil {
390-
return
378+
if err := addFile(w, k[1:], iv); err != nil {
379+
return err
391380
}
392381
} else { // form value
393-
if err = w.WriteField(k, iv); err != nil {
382+
if err := w.WriteField(k, iv); err != nil {
394383
return err
395384
}
396385
}
397386
}
398387
}
399388

400389
// #21 - adding io.Reader support
401-
if len(r.multipartFiles) > 0 {
402-
for _, f := range r.multipartFiles {
403-
err = addFileReader(w, f)
404-
if err != nil {
405-
return
406-
}
390+
for _, f := range r.multipartFiles {
391+
if err := addFileReader(w, f); err != nil {
392+
return err
407393
}
408394
}
409395

410396
// GitHub #130 adding multipart field support with content type
411-
if len(r.multipartFields) > 0 {
412-
for _, mf := range r.multipartFields {
413-
if err = addMultipartFormField(w, mf); err != nil {
414-
return
415-
}
397+
for _, mf := range r.multipartFields {
398+
if err := addMultipartFormField(w, mf); err != nil {
399+
return err
416400
}
417401
}
418402

419403
r.Header.Set(hdrContentTypeKey, w.FormDataContentType())
420-
err = w.Close()
421-
422-
return
404+
return w.Close()
423405
}
424406

425407
func handleFormData(c *Client, r *Request) {
426-
formData := url.Values{}
427-
428408
for k, v := range c.FormData {
429-
for _, iv := range v {
430-
formData.Add(k, iv)
431-
}
432-
}
433-
434-
for k, v := range r.FormData {
435-
// remove form data field from client level by key
436-
// since overrides happens for that key in the request
437-
formData.Del(k)
438-
439-
for _, iv := range v {
440-
formData.Add(k, iv)
409+
if _, ok := r.FormData[k]; ok {
410+
continue
441411
}
412+
r.FormData[k] = v[:]
442413
}
443414

444-
r.bodyBuf = bytes.NewBuffer([]byte(formData.Encode()))
415+
r.bodyBuf = acquireBuffer()
416+
r.bodyBuf.WriteString(r.FormData.Encode())
445417
r.Header.Set(hdrContentTypeKey, formContentType)
446418
r.isFormData = true
447419
}
@@ -454,45 +426,43 @@ func handleContentType(c *Client, r *Request) {
454426
}
455427
}
456428

457-
func handleRequestBody(c *Client, r *Request) (err error) {
429+
func handleRequestBody(c *Client, r *Request) error {
458430
var bodyBytes []byte
459-
contentType := r.Header.Get(hdrContentTypeKey)
460-
kind := kindOf(r.Body)
431+
releaseBuffer(r.bodyBuf)
461432
r.bodyBuf = nil
462433

463-
if reader, ok := r.Body.(io.Reader); ok {
434+
switch body := r.Body.(type) {
435+
case io.Reader:
464436
if c.setContentLength || r.setContentLength { // keep backward compatibility
465437
r.bodyBuf = acquireBuffer()
466-
_, err = r.bodyBuf.ReadFrom(reader)
438+
if _, err := r.bodyBuf.ReadFrom(body); err != nil {
439+
return err
440+
}
467441
r.Body = nil
468442
} else {
469443
// Otherwise buffer less processing for `io.Reader`, sounds good.
470-
return
471-
}
472-
} else if b, ok := r.Body.([]byte); ok {
473-
bodyBytes = b
474-
} else if s, ok := r.Body.(string); ok {
475-
bodyBytes = []byte(s)
476-
} else if IsJSONType(contentType) &&
477-
(kind == reflect.Struct || kind == reflect.Map || kind == reflect.Slice) {
478-
r.bodyBuf, err = jsonMarshal(c, r, r.Body)
479-
if err != nil {
480-
return
444+
return nil
445+
}
446+
case []byte:
447+
bodyBytes = body
448+
case string:
449+
bodyBytes = []byte(body)
450+
default:
451+
contentType := r.Header.Get(hdrContentTypeKey)
452+
kind := kindOf(r.Body)
453+
var err error
454+
if IsJSONType(contentType) && (kind == reflect.Struct || kind == reflect.Map || kind == reflect.Slice) {
455+
r.bodyBuf, err = jsonMarshal(c, r, r.Body)
456+
} else if IsXMLType(contentType) && (kind == reflect.Struct) {
457+
bodyBytes, err = c.XMLMarshal(r.Body)
481458
}
482-
} else if IsXMLType(contentType) && (kind == reflect.Struct) {
483-
bodyBytes, err = c.XMLMarshal(r.Body)
484459
if err != nil {
485-
return
460+
return err
486461
}
487462
}
488463

489464
if bodyBytes == nil && r.bodyBuf == nil {
490-
err = errors.New("unsupported 'Body' type/value")
491-
}
492-
493-
// if any errors during body bytes handling, return it
494-
if err != nil {
495-
return
465+
return errors.New("unsupported 'Body' type/value")
496466
}
497467

498468
// []byte into Buffer
@@ -501,7 +471,7 @@ func handleRequestBody(c *Client, r *Request) (err error) {
501471
_, _ = r.bodyBuf.Write(bodyBytes)
502472
}
503473

504-
return
474+
return nil
505475
}
506476

507477
func saveResponseIntoFile(c *Client, res *Response) error {

0 commit comments

Comments
 (0)