Skip to content

Commit

Permalink
Handle HTTP errors (liamg#3)
Browse files Browse the repository at this point in the history
* Handle HTTP errors

* Improve error handling
  • Loading branch information
nonsleepr authored Jan 25, 2024
1 parent 2ed2876 commit e7bf5fe
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 16 deletions.
11 changes: 9 additions & 2 deletions pkg/api/error.go → pkg/api/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ type Error struct {
} `json:"source"`
}

func (e *Error) Error() string {
return fmt.Sprintf("%s: %s", e.Title, e.Detail)
type Errors struct {
Errors []Error `json:"errors"`
}

func (e *Errors) Error() error {
if len(e.Errors) > 0 {
return fmt.Errorf("%d: %s", e.Errors[0].Status, e.Errors[0].Detail)
}
return fmt.Errorf("No errors")
}
27 changes: 27 additions & 0 deletions pkg/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net/http/httptest"
"testing"

"github.com/liamg/hackerone/pkg/api"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -68,6 +69,32 @@ func Test_Get(t *testing.T) {
assert.Equal(t, expected, actual)
}

func Test_Get_404(t *testing.T) {
mux := http.NewServeMux()
server := httptest.NewServer(mux)
defer server.Close()

returned := api.Errors{
Errors: []api.Error{
{
Status: 404,
Detail: "Not Found",
},
},
}

mux.HandleFunc("/404", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusNotFound)
_ = json.NewEncoder(w).Encode(returned)
})

client := New("", "", OptionWithBaseURL(server.URL))
var actual example
err := client.Get(context.TODO(), "/404", &actual)
require.Error(t, err)
}

func Test_Post(t *testing.T) {
mux := http.NewServeMux()
server := httptest.NewServer(mux)
Expand Down
30 changes: 16 additions & 14 deletions pkg/client/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,37 @@ type transport struct {
apiKey string
}

func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
var response *http.Response
func (t *transport) RoundTrip(req *http.Request) (response *http.Response, err error) {
req.SetBasicAuth(t.username, t.apiKey)
options := []retry.Option{
retry.Delay(time.Millisecond * 100),
retry.DelayType(retry.BackOffDelay),
retry.Attempts(10),
retry.Attempts(5),
retry.LastErrorOnly(true),
}
if err := retry.Do(func() error {
err = retry.Do(func() error {
resp, err := t.underlying.RoundTrip(req)
if err != nil {
return err
}
if resp.StatusCode >= 400 {
defer func() { _ = resp.Body.Close() }()
var apiError api.Error
if err := json.NewDecoder(resp.Body).Decode(&apiError); err != nil || apiError.Status == 0 {
return fmt.Errorf("server error: status %d", resp.StatusCode)
defer resp.Body.Close()
var apiErrors api.Errors
err = json.NewDecoder(resp.Body).Decode(&apiErrors)
if err != nil {
err = fmt.Errorf("server error: status %d, %s", resp.StatusCode, resp.Body)
} else {
err = apiErrors.Error()
}
if resp.StatusCode < 500 {
return retry.Unrecoverable(&apiError)
// Client error
return retry.Unrecoverable(err)
}
return &apiError
// Server error
return err
}
response = resp
return nil
}, options...); err != nil {
return nil, err
}
return response, nil
}, options...)
return
}

0 comments on commit e7bf5fe

Please sign in to comment.