client.go raw

   1  package errutils
   2  
   3  import (
   4  	"bytes"
   5  	"fmt"
   6  	"io"
   7  	"net/http"
   8  	"os"
   9  	"strconv"
  10  )
  11  
  12  const legoDebugClientVerboseError = "LEGO_DEBUG_CLIENT_VERBOSE_ERROR"
  13  
  14  // HTTPDoError uses with `(http.Client).Do` error.
  15  type HTTPDoError struct {
  16  	req *http.Request
  17  	err error
  18  }
  19  
  20  // NewHTTPDoError creates a new HTTPDoError.
  21  func NewHTTPDoError(req *http.Request, err error) *HTTPDoError {
  22  	return &HTTPDoError{req: req, err: err}
  23  }
  24  
  25  func (h HTTPDoError) Error() string {
  26  	msg := "unable to communicate with the API server:"
  27  
  28  	if ok, _ := strconv.ParseBool(os.Getenv(legoDebugClientVerboseError)); ok {
  29  		msg += fmt.Sprintf(" [request: %s %s]", h.req.Method, h.req.URL)
  30  	}
  31  
  32  	if h.err == nil {
  33  		return msg
  34  	}
  35  
  36  	return msg + fmt.Sprintf(" error: %v", h.err)
  37  }
  38  
  39  func (h HTTPDoError) Unwrap() error {
  40  	return h.err
  41  }
  42  
  43  // ReadResponseError use with `io.ReadAll` when reading response body.
  44  type ReadResponseError struct {
  45  	req        *http.Request
  46  	StatusCode int
  47  	err        error
  48  }
  49  
  50  // NewReadResponseError creates a new ReadResponseError.
  51  func NewReadResponseError(req *http.Request, statusCode int, err error) *ReadResponseError {
  52  	return &ReadResponseError{req: req, StatusCode: statusCode, err: err}
  53  }
  54  
  55  func (r ReadResponseError) Error() string {
  56  	msg := "unable to read response body:"
  57  
  58  	if ok, _ := strconv.ParseBool(os.Getenv(legoDebugClientVerboseError)); ok {
  59  		msg += fmt.Sprintf(" [request: %s %s]", r.req.Method, r.req.URL)
  60  	}
  61  
  62  	msg += fmt.Sprintf(" [status code: %d]", r.StatusCode)
  63  
  64  	if r.err == nil {
  65  		return msg
  66  	}
  67  
  68  	return msg + fmt.Sprintf(" error: %v", r.err)
  69  }
  70  
  71  func (r ReadResponseError) Unwrap() error {
  72  	return r.err
  73  }
  74  
  75  // UnmarshalError uses with `json.Unmarshal` or `xml.Unmarshal` when reading response body.
  76  type UnmarshalError struct {
  77  	req        *http.Request
  78  	StatusCode int
  79  	Body       []byte
  80  	err        error
  81  }
  82  
  83  // NewUnmarshalError creates a new UnmarshalError.
  84  func NewUnmarshalError(req *http.Request, statusCode int, body []byte, err error) *UnmarshalError {
  85  	return &UnmarshalError{req: req, StatusCode: statusCode, Body: bytes.TrimSpace(body), err: err}
  86  }
  87  
  88  func (u UnmarshalError) Error() string {
  89  	msg := "unable to unmarshal response:"
  90  
  91  	if ok, _ := strconv.ParseBool(os.Getenv(legoDebugClientVerboseError)); ok {
  92  		msg += fmt.Sprintf(" [request: %s %s]", u.req.Method, u.req.URL)
  93  	}
  94  
  95  	msg += fmt.Sprintf(" [status code: %d] body: %s", u.StatusCode, string(u.Body))
  96  
  97  	if u.err == nil {
  98  		return msg
  99  	}
 100  
 101  	return msg + fmt.Sprintf(" error: %v", u.err)
 102  }
 103  
 104  func (u UnmarshalError) Unwrap() error {
 105  	return u.err
 106  }
 107  
 108  // UnexpectedStatusCodeError use when the status of the response is unexpected but there is no API error type.
 109  type UnexpectedStatusCodeError struct {
 110  	req        *http.Request
 111  	StatusCode int
 112  	Body       []byte
 113  }
 114  
 115  // NewUnexpectedStatusCodeError creates a new UnexpectedStatusCodeError.
 116  func NewUnexpectedStatusCodeError(req *http.Request, statusCode int, body []byte) *UnexpectedStatusCodeError {
 117  	return &UnexpectedStatusCodeError{req: req, StatusCode: statusCode, Body: bytes.TrimSpace(body)}
 118  }
 119  
 120  func NewUnexpectedResponseStatusCodeError(req *http.Request, resp *http.Response) *UnexpectedStatusCodeError {
 121  	raw, _ := io.ReadAll(resp.Body)
 122  	return &UnexpectedStatusCodeError{req: req, StatusCode: resp.StatusCode, Body: bytes.TrimSpace(raw)}
 123  }
 124  
 125  func (u UnexpectedStatusCodeError) Error() string {
 126  	msg := "unexpected status code:"
 127  
 128  	if ok, _ := strconv.ParseBool(os.Getenv(legoDebugClientVerboseError)); ok {
 129  		msg += fmt.Sprintf(" [request: %s %s]", u.req.Method, u.req.URL)
 130  	}
 131  
 132  	return msg + fmt.Sprintf(" [status code: %d] body: %s", u.StatusCode, string(u.Body))
 133  }
 134