github.com/go-playground/pkg/v5@v5.29.1/net/http/helpers_go1.18.go (about) 1 //go:build go1.18 2 // +build go1.18 3 4 package httpext 5 6 import ( 7 "errors" 8 "net/http" 9 "strconv" 10 "strings" 11 "time" 12 13 asciiext "github.com/go-playground/pkg/v5/ascii" 14 bytesext "github.com/go-playground/pkg/v5/bytes" 15 . "github.com/go-playground/pkg/v5/values/option" 16 ) 17 18 // DecodeResponse takes the response and attempts to discover its content type via 19 // the http headers and then decode the request body into the provided type. 20 // 21 // Example if header was "application/json" would decode using 22 // json.NewDecoder(ioext.LimitReader(r.Body, maxBytes)).Decode(v). 23 func DecodeResponse[T any](r *http.Response, maxMemory bytesext.Bytes) (result T, err error) { 24 typ := r.Header.Get(ContentType) 25 if idx := strings.Index(typ, ";"); idx != -1 { 26 typ = typ[:idx] 27 } 28 switch typ { 29 case nakedApplicationJSON: 30 err = decodeJSON(r.Header, r.Body, NoQueryParams, nil, maxMemory, &result) 31 case nakedApplicationXML: 32 err = decodeXML(r.Header, r.Body, NoQueryParams, nil, maxMemory, &result) 33 default: 34 err = errors.New("unsupported content type") 35 } 36 return 37 } 38 39 // HasRetryAfter parses the Retry-After header and returns the duration if possible. 40 func HasRetryAfter(headers http.Header) Option[time.Duration] { 41 if ra := headers.Get(RetryAfter); ra != "" { 42 if asciiext.IsDigit(ra[0]) { 43 if n, err := strconv.ParseInt(ra, 10, 64); err == nil { 44 return Some(time.Duration(n) * time.Second) 45 } 46 } else { 47 // not a number so must be a date in the future 48 if t, err := http.ParseTime(ra); err == nil { 49 return Some(time.Until(t)) 50 } 51 } 52 } 53 return None[time.Duration]() 54 }