github.com/npaton/distribution@v2.3.1-rc.0+incompatible/registry/client/errors.go (about) 1 package client 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "net/http" 9 10 "github.com/docker/distribution/registry/api/errcode" 11 ) 12 13 // UnexpectedHTTPStatusError is returned when an unexpected HTTP status is 14 // returned when making a registry api call. 15 type UnexpectedHTTPStatusError struct { 16 Status string 17 } 18 19 func (e *UnexpectedHTTPStatusError) Error() string { 20 return fmt.Sprintf("Received unexpected HTTP status: %s", e.Status) 21 } 22 23 // UnexpectedHTTPResponseError is returned when an expected HTTP status code 24 // is returned, but the content was unexpected and failed to be parsed. 25 type UnexpectedHTTPResponseError struct { 26 ParseErr error 27 Response []byte 28 } 29 30 func (e *UnexpectedHTTPResponseError) Error() string { 31 return fmt.Sprintf("Error parsing HTTP response: %s: %q", e.ParseErr.Error(), string(e.Response)) 32 } 33 34 func parseHTTPErrorResponse(statusCode int, r io.Reader) error { 35 var errors errcode.Errors 36 body, err := ioutil.ReadAll(r) 37 if err != nil { 38 return err 39 } 40 41 // For backward compatibility, handle irregularly formatted 42 // messages that contain a "details" field. 43 var detailsErr struct { 44 Details string `json:"details"` 45 } 46 err = json.Unmarshal(body, &detailsErr) 47 if err == nil && detailsErr.Details != "" { 48 if statusCode == http.StatusUnauthorized { 49 return errcode.ErrorCodeUnauthorized.WithMessage(detailsErr.Details) 50 } 51 return errcode.ErrorCodeUnknown.WithMessage(detailsErr.Details) 52 } 53 54 if err := json.Unmarshal(body, &errors); err != nil { 55 return &UnexpectedHTTPResponseError{ 56 ParseErr: err, 57 Response: body, 58 } 59 } 60 return errors 61 } 62 63 // HandleErrorResponse returns error parsed from HTTP response for an 64 // unsuccessful HTTP response code (in the range 400 - 499 inclusive). An 65 // UnexpectedHTTPStatusError returned for response code outside of expected 66 // range. 67 func HandleErrorResponse(resp *http.Response) error { 68 if resp.StatusCode == 401 { 69 err := parseHTTPErrorResponse(resp.StatusCode, resp.Body) 70 if uErr, ok := err.(*UnexpectedHTTPResponseError); ok { 71 return errcode.ErrorCodeUnauthorized.WithDetail(uErr.Response) 72 } 73 return err 74 } 75 if resp.StatusCode >= 400 && resp.StatusCode < 500 { 76 return parseHTTPErrorResponse(resp.StatusCode, resp.Body) 77 } 78 return &UnexpectedHTTPStatusError{Status: resp.Status} 79 } 80 81 // SuccessStatus returns true if the argument is a successful HTTP response 82 // code (in the range 200 - 399 inclusive). 83 func SuccessStatus(status int) bool { 84 return status >= 200 && status <= 399 85 }