package httpx import ( "bytes" "encoding/json" "encoding/xml" "fmt" "io" "net/http" ) // Response wraps http.Response with convenience methods. type Response struct { *http.Response body []byte read bool } func newResponse(resp *http.Response) *Response { return &Response{Response: resp} } // Bytes reads and returns the entire response body. // The body is cached so subsequent calls return the same data. func (r *Response) Bytes() ([]byte, error) { if r.read { return r.body, nil } defer r.Body.Close() b, err := io.ReadAll(r.Body) if err != nil { return nil, err } r.body = b r.read = true return b, nil } // String reads the response body and returns it as a string. func (r *Response) String() (string, error) { b, err := r.Bytes() if err != nil { return "", err } return string(b), nil } // JSON decodes the response body as JSON into v. func (r *Response) JSON(v any) error { b, err := r.Bytes() if err != nil { return fmt.Errorf("httpx: reading response body: %w", err) } if err := json.Unmarshal(b, v); err != nil { return fmt.Errorf("httpx: decoding JSON: %w", err) } return nil } // XML decodes the response body as XML into v. func (r *Response) XML(v any) error { b, err := r.Bytes() if err != nil { return fmt.Errorf("httpx: reading response body: %w", err) } if err := xml.Unmarshal(b, v); err != nil { return fmt.Errorf("httpx: decoding XML: %w", err) } return nil } // IsSuccess returns true if the status code is in the 2xx range. func (r *Response) IsSuccess() bool { return r.StatusCode >= 200 && r.StatusCode < 300 } // IsError returns true if the status code is 4xx or 5xx. func (r *Response) IsError() bool { return r.StatusCode >= 400 } // Close drains and closes the response body. func (r *Response) Close() error { if r.read { return nil } _, _ = io.Copy(io.Discard, r.Body) return r.Body.Close() } // BodyReader returns a reader for the response body. // If the body has already been read via Bytes/String/JSON/XML, // returns a reader over the cached bytes. func (r *Response) BodyReader() io.Reader { if r.read { return bytes.NewReader(r.body) } return r.Body }