2018-09-06 21:42:41 +00:00
|
|
|
package lfshttp
|
2016-12-16 22:43:05 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
2017-01-09 20:49:54 +00:00
|
|
|
"strings"
|
2016-12-16 22:43:05 +00:00
|
|
|
|
2021-08-10 06:18:38 +00:00
|
|
|
"github.com/git-lfs/git-lfs/v2/errors"
|
2016-12-16 22:43:05 +00:00
|
|
|
)
|
|
|
|
|
2017-01-09 20:49:54 +00:00
|
|
|
type httpError interface {
|
|
|
|
Error() string
|
|
|
|
HTTPResponse() *http.Response
|
|
|
|
}
|
|
|
|
|
2017-01-09 22:30:50 +00:00
|
|
|
func IsHTTP(err error) (*http.Response, bool) {
|
2017-01-09 20:49:54 +00:00
|
|
|
if httpErr, ok := err.(httpError); ok {
|
|
|
|
return httpErr.HTTPResponse(), true
|
|
|
|
}
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
2016-12-16 22:43:05 +00:00
|
|
|
type ClientError struct {
|
|
|
|
Message string `json:"message"`
|
|
|
|
DocumentationUrl string `json:"documentation_url,omitempty"`
|
|
|
|
RequestId string `json:"request_id,omitempty"`
|
2017-01-09 20:49:54 +00:00
|
|
|
response *http.Response
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *ClientError) HTTPResponse() *http.Response {
|
|
|
|
return e.response
|
2016-12-16 22:43:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *ClientError) Error() string {
|
2017-02-27 21:16:19 +00:00
|
|
|
return e.Message
|
2016-12-16 22:43:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) handleResponse(res *http.Response) error {
|
|
|
|
if res.StatusCode < 400 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-01-09 20:49:54 +00:00
|
|
|
cliErr := &ClientError{response: res}
|
2016-12-22 22:31:48 +00:00
|
|
|
err := DecodeJSON(res, cliErr)
|
|
|
|
if IsDecodeTypeError(err) {
|
|
|
|
err = nil
|
|
|
|
}
|
|
|
|
|
2016-12-16 22:43:05 +00:00
|
|
|
if err == nil {
|
|
|
|
if len(cliErr.Message) == 0 {
|
|
|
|
err = defaultError(res)
|
|
|
|
} else {
|
2017-02-25 00:00:50 +00:00
|
|
|
err = cliErr
|
2016-12-16 22:43:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if res.StatusCode == 401 {
|
|
|
|
return errors.NewAuthError(err)
|
|
|
|
}
|
|
|
|
|
2018-07-31 18:23:16 +00:00
|
|
|
if res.StatusCode == 422 {
|
|
|
|
return errors.NewUnprocessableEntityError(err)
|
|
|
|
}
|
|
|
|
|
2018-12-26 15:30:53 +00:00
|
|
|
if res.StatusCode == 429 {
|
|
|
|
// The Retry-After header could be set, check to see if it exists.
|
|
|
|
h := res.Header.Get("Retry-After")
|
2019-01-07 09:27:02 +00:00
|
|
|
retLaterErr := errors.NewRetriableLaterError(err, h)
|
|
|
|
if retLaterErr != nil {
|
|
|
|
return retLaterErr
|
2018-12-26 15:30:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-16 22:43:05 +00:00
|
|
|
if res.StatusCode > 499 && res.StatusCode != 501 && res.StatusCode != 507 && res.StatusCode != 509 {
|
|
|
|
return errors.NewFatalError(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-01-09 20:49:54 +00:00
|
|
|
type statusCodeError struct {
|
|
|
|
response *http.Response
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewStatusCodeError(res *http.Response) error {
|
|
|
|
return &statusCodeError{response: res}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *statusCodeError) Error() string {
|
|
|
|
req := e.response.Request
|
|
|
|
return fmt.Sprintf("Invalid HTTP status for %s %s: %d",
|
|
|
|
req.Method,
|
|
|
|
strings.SplitN(req.URL.String(), "?", 2)[0],
|
|
|
|
e.response.StatusCode,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *statusCodeError) HTTPResponse() *http.Response {
|
|
|
|
return e.response
|
|
|
|
}
|
|
|
|
|
2016-12-16 22:43:05 +00:00
|
|
|
var (
|
|
|
|
defaultErrors = map[int]string{
|
|
|
|
400: "Client error: %s",
|
|
|
|
401: "Authorization error: %s\nCheck that you have proper access to the repository",
|
|
|
|
403: "Authorization error: %s\nCheck that you have proper access to the repository",
|
|
|
|
404: "Repository or object not found: %s\nCheck that it exists and that you have proper access to it",
|
2018-07-31 18:23:16 +00:00
|
|
|
422: "Unprocessable entity: %s",
|
2016-12-16 22:43:05 +00:00
|
|
|
429: "Rate limit exceeded: %s",
|
|
|
|
500: "Server error: %s",
|
|
|
|
501: "Not Implemented: %s",
|
|
|
|
507: "Insufficient server storage: %s",
|
|
|
|
509: "Bandwidth limit exceeded: %s",
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
func defaultError(res *http.Response) error {
|
|
|
|
var msgFmt string
|
|
|
|
|
|
|
|
if f, ok := defaultErrors[res.StatusCode]; ok {
|
|
|
|
msgFmt = f
|
|
|
|
} else if res.StatusCode < 500 {
|
|
|
|
msgFmt = defaultErrors[400] + fmt.Sprintf(" from HTTP %d", res.StatusCode)
|
|
|
|
} else {
|
|
|
|
msgFmt = defaultErrors[500] + fmt.Sprintf(" from HTTP %d", res.StatusCode)
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors.Errorf(msgFmt, res.Request.URL)
|
|
|
|
}
|