123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- package client
- import (
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "io/ioutil"
- "net/http"
- "github.com/docker/distribution/registry/api/errcode"
- )
- // ErrNoErrorsInBody is returned when a HTTP response body parses to an empty
- // errcode.Errors slice.
- var ErrNoErrorsInBody = errors.New("no error details found in HTTP response body")
- // UnexpectedHTTPStatusError is returned when an unexpected HTTP status is
- // returned when making a registry api call.
- type UnexpectedHTTPStatusError struct {
- Status string
- }
- func (e *UnexpectedHTTPStatusError) Error() string {
- return fmt.Sprintf("received unexpected HTTP status: %s", e.Status)
- }
- // UnexpectedHTTPResponseError is returned when an expected HTTP status code
- // is returned, but the content was unexpected and failed to be parsed.
- type UnexpectedHTTPResponseError struct {
- ParseErr error
- StatusCode int
- Response []byte
- }
- func (e *UnexpectedHTTPResponseError) Error() string {
- return fmt.Sprintf("error parsing HTTP %d response body: %s: %q", e.StatusCode, e.ParseErr.Error(), string(e.Response))
- }
- func parseHTTPErrorResponse(statusCode int, r io.Reader) error {
- var errors errcode.Errors
- body, err := ioutil.ReadAll(r)
- if err != nil {
- return err
- }
- // For backward compatibility, handle irregularly formatted
- // messages that contain a "details" field.
- var detailsErr struct {
- Details string `json:"details"`
- }
- err = json.Unmarshal(body, &detailsErr)
- if err == nil && detailsErr.Details != "" {
- if statusCode == http.StatusUnauthorized {
- return errcode.ErrorCodeUnauthorized.WithMessage(detailsErr.Details)
- }
- return errcode.ErrorCodeUnknown.WithMessage(detailsErr.Details)
- }
- if err := json.Unmarshal(body, &errors); err != nil {
- return &UnexpectedHTTPResponseError{
- ParseErr: err,
- StatusCode: statusCode,
- Response: body,
- }
- }
- if len(errors) == 0 {
- // If there was no error specified in the body, return
- // UnexpectedHTTPResponseError.
- return &UnexpectedHTTPResponseError{
- ParseErr: ErrNoErrorsInBody,
- StatusCode: statusCode,
- Response: body,
- }
- }
- return errors
- }
- // HandleErrorResponse returns error parsed from HTTP response for an
- // unsuccessful HTTP response code (in the range 400 - 499 inclusive). An
- // UnexpectedHTTPStatusError returned for response code outside of expected
- // range.
- func HandleErrorResponse(resp *http.Response) error {
- if resp.StatusCode == 401 {
- err := parseHTTPErrorResponse(resp.StatusCode, resp.Body)
- if uErr, ok := err.(*UnexpectedHTTPResponseError); ok {
- return errcode.ErrorCodeUnauthorized.WithDetail(uErr.Response)
- }
- return err
- }
- if resp.StatusCode >= 400 && resp.StatusCode < 500 {
- return parseHTTPErrorResponse(resp.StatusCode, resp.Body)
- }
- return &UnexpectedHTTPStatusError{Status: resp.Status}
- }
- // SuccessStatus returns true if the argument is a successful HTTP response
- // code (in the range 200 - 399 inclusive).
- func SuccessStatus(status int) bool {
- return status >= 200 && status <= 399
- }
|