This commit is contained in:
Mathias Gebbe 2025-05-23 12:20:46 +02:00 committed by GitHub
commit 22a55d9387
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 45 additions and 12 deletions

View File

@ -44,7 +44,6 @@ func parseHTTPErrorResponse(resp *http.Response) error {
if err != nil {
return err
}
statusCode := resp.StatusCode
// A HEAD request for example validly does not contain any body, while
@ -53,6 +52,10 @@ func parseHTTPErrorResponse(resp *http.Response) error {
return makeError(statusCode, "")
}
if statusCode == 401 {
return makeError(statusCode, string(body))
}
ctHeader := resp.Header.Get("Content-Type")
if ctHeader == "" {
return makeError(statusCode, string(body))
@ -101,13 +104,13 @@ func parseHTTPErrorResponse(resp *http.Response) error {
func makeError(statusCode int, details string) error {
switch statusCode {
case http.StatusUnauthorized:
return errcode.ErrorCodeUnauthorized.WithMessage(details)
return errcode.ErrorCodeUnauthorized.WithMessageStatusCode(details, statusCode)
case http.StatusForbidden:
return errcode.ErrorCodeDenied.WithMessage(details)
return errcode.ErrorCodeDenied.WithMessageStatusCode(details, statusCode)
case http.StatusTooManyRequests:
return errcode.ErrorCodeTooManyRequests.WithMessage(details)
return errcode.ErrorCodeTooManyRequests.WithMessageStatusCode(details, statusCode)
default:
return errcode.ErrorCodeUnknown.WithMessage(details)
return errcode.ErrorCodeUnknown.WithMessageStatusCode(details, statusCode)
}
}
@ -138,6 +141,7 @@ func HandleHTTPResponseError(resp *http.Response) error {
for _, c := range challenge.ResponseChallenges(resp) {
if c.Scheme == "bearer" {
var err errcode.Error
err.StatusCode = resp.StatusCode
// codes defined at https://tools.ietf.org/html/rfc6750#section-3.1
switch c.Parameters["error"] {
case "invalid_token":

View File

@ -78,6 +78,16 @@ func (ec ErrorCode) WithMessage(message string) Error {
}
}
// WithMessage creates a new Error struct based on the passed-in info and
// overrides the Message property.
func (ec ErrorCode) WithMessageStatusCode(message string, statusCode int) Error {
return Error{
Code: ec,
Message: message,
StatusCode: statusCode,
}
}
// WithDetail creates a new Error struct based on the passed-in info and
// set the Detail property appropriately
func (ec ErrorCode) WithDetail(detail interface{}) Error {
@ -97,10 +107,10 @@ func (ec ErrorCode) WithArgs(args ...interface{}) Error {
// Error provides a wrapper around ErrorCode with extra Details provided.
type Error struct {
Code ErrorCode `json:"code"`
Message string `json:"message"`
Detail interface{} `json:"detail,omitempty"`
Code ErrorCode `json:"code"`
Message string `json:"message"`
Detail interface{} `json:"detail,omitempty"`
StatusCode int `json:"statusCode"`
// TODO(duglin): See if we need an "args" property so we can do the
// variable substitution right before showing the message to the user
}
@ -121,9 +131,10 @@ func (e Error) Error() string {
// some Detail info added
func (e Error) WithDetail(detail interface{}) Error {
return Error{
Code: e.Code,
Message: e.Message,
Detail: detail,
Code: e.Code,
StatusCode: e.StatusCode,
Message: e.Message,
Detail: detail,
}
}

View File

@ -2,6 +2,7 @@ package handlers
import (
"bytes"
"errors"
"fmt"
"mime"
"net/http"
@ -142,6 +143,8 @@ func (imh *manifestHandler) GetManifest(w http.ResponseWriter, r *http.Request)
if err != nil {
if _, ok := err.(distribution.ErrManifestUnknownRevision); ok {
imh.Errors = append(imh.Errors, errcode.ErrorCodeManifestUnknown.WithDetail(err))
} else if checkAllErrorsStatusCode(err, 401) {
imh.Errors = append(imh.Errors, errcode.ErrorCodeUnauthorized.WithDetail(err))
} else {
imh.Errors = append(imh.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
}
@ -225,6 +228,21 @@ func (imh *manifestHandler) GetManifest(w http.ResponseWriter, r *http.Request)
}
}
func checkAllErrorsStatusCode(err error, statusCode int) bool {
var errs errcode.Errors
if !errors.As(err, &errs) {
return false
}
for _, e := range errs {
var specificErr errcode.Error
ok := errors.As(e, &specificErr)
if !ok || specificErr.StatusCode != statusCode {
return false
}
}
return true
}
func etagMatch(r *http.Request, etag string) bool {
for _, headerVal := range r.Header["If-None-Match"] {
if headerVal == etag || headerVal == fmt.Sprintf(`"%s"`, etag) { // allow quoted or unquoted