Add HTTP error annotation.
This allows the status to be retrieved programmatically.
R=maruel@google.com
BUG=
Review-Url: https://codereview.chromium.org/2984913002
diff --git a/common/errors/tags.go b/common/errors/tags.go
index 74232b9..82c9af1 100644
--- a/common/errors/tags.go
+++ b/common/errors/tags.go
@@ -38,7 +38,7 @@
Value interface{}
}
- // A TagValueGenerator generates (TagKey, value) pairs, for use with Annoatator.Tag
+ // TagValueGenerator generates (TagKey, value) pairs, for use with Annoatator.Tag
// and New().
TagValueGenerator interface {
GenerateErrorTagValue() TagValue
diff --git a/common/lhttp/client.go b/common/lhttp/client.go
index 217adfe..4115746 100644
--- a/common/lhttp/client.go
+++ b/common/lhttp/client.go
@@ -54,6 +54,20 @@
// documentation.
type RequestGen func() (*http.Request, error)
+var httpTagKey = errors.NewTagKey("this is an HTTP error")
+
+func applyHTTPTag(err error, status int) error {
+ return errors.TagValue{Key: httpTagKey, Value: status}.Apply(err)
+}
+
+func IsHTTPError(err error) (status int, ok bool) {
+ d, ok := errors.TagValueIn(httpTagKey, err)
+ if ok {
+ status = d.(int)
+ }
+ return
+}
+
// NewRequest returns a retriable request.
//
// The handler func is responsible for closing the response Body before
@@ -117,10 +131,11 @@
return handler(resp)
}
+ err = applyHTTPTag(err, status)
return errorHandler(resp, err)
}, nil)
if err != nil {
- err = fmt.Errorf("%v (attempts: %d)", err, attempts)
+ err = errors.Annotate(err, "gave up after %d attempts", attempts).Err()
}
return status, err
}
diff --git a/common/lhttp/client_test.go b/common/lhttp/client_test.go
index 81f5a64..ae42629 100644
--- a/common/lhttp/client_test.go
+++ b/common/lhttp/client_test.go
@@ -140,7 +140,7 @@
}, nil)
status, err := clientReq()
- So(err.Error(), ShouldResemble, "http request failed: Internal Server Error (HTTP 500) (attempts: 4)")
+ So(err.Error(), ShouldResemble, "gave up after 4 attempts: http request failed: Internal Server Error (HTTP 500)")
So(status, ShouldResemble, 500)
})
}
@@ -187,7 +187,7 @@
actual := map[string]string{}
status, err := GetJSON(ctx, fast, http.DefaultClient, ts.URL, &actual)
- So(err.Error(), ShouldResemble, "bad response "+ts.URL+": invalid character 'y' looking for beginning of value (attempts: 4)")
+ So(err.Error(), ShouldResemble, "gave up after 4 attempts: bad response "+ts.URL+": invalid character 'y' looking for beginning of value")
So(status, ShouldResemble, 200)
So(actual, ShouldResemble, map[string]string{})
})
@@ -207,7 +207,7 @@
defer ts.Close()
status, err := GetJSON(ctx, fast, http.DefaultClient, ts.URL, nil)
- So(err.Error(), ShouldResemble, "bad response "+ts.URL+": invalid character 'y' looking for beginning of value (attempts: 4)")
+ So(err.Error(), ShouldResemble, "gave up after 4 attempts: bad response "+ts.URL+": invalid character 'y' looking for beginning of value")
So(status, ShouldResemble, 200)
})
}
@@ -223,7 +223,7 @@
defer ts.Close()
status, err := GetJSON(ctx, fast, http.DefaultClient, ts.URL, nil)
- So(err.Error(), ShouldResemble, "unexpected Content-Type, expected \"application/json\", got \"text/plain; charset=utf-8\" (attempts: 4)")
+ So(err.Error(), ShouldResemble, "gave up after 4 attempts: unexpected Content-Type, expected \"application/json\", got \"text/plain; charset=utf-8\"")
So(status, ShouldResemble, 200)
})
}