forked from TrueCloudLab/distribution
Merge pull request #705 from stevvooe/export-servejson-errors
Export ServeJSON for serving error codes
This commit is contained in:
commit
5c6c88196d
4 changed files with 65 additions and 44 deletions
|
@ -16,6 +16,8 @@ type ErrorCoder interface {
|
|||
// and the integer format may change and should *never* be exported.
|
||||
type ErrorCode int
|
||||
|
||||
var _ error = ErrorCode(0)
|
||||
|
||||
// ErrorCode just returns itself
|
||||
func (ec ErrorCode) ErrorCode() ErrorCode {
|
||||
return ec
|
||||
|
@ -93,6 +95,8 @@ type Error struct {
|
|||
// variable substitution right before showing the message to the user
|
||||
}
|
||||
|
||||
var _ error = Error{}
|
||||
|
||||
// ErrorCode returns the ID/Value of this Error
|
||||
func (e Error) ErrorCode() ErrorCode {
|
||||
return e.Code
|
||||
|
@ -163,6 +167,8 @@ func ParseErrorCode(value string) ErrorCode {
|
|||
// for use within the application.
|
||||
type Errors []error
|
||||
|
||||
var _ error = Errors{}
|
||||
|
||||
func (errs Errors) Error() string {
|
||||
switch len(errs) {
|
||||
case 0:
|
||||
|
|
44
docs/api/errcode/handler.go
Normal file
44
docs/api/errcode/handler.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
package errcode
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ServeJSON attempts to serve the errcode in a JSON envelope. It marshals err
|
||||
// and sets the content-type header to 'application/json'. It will handle
|
||||
// ErrorCoder and Errors, and if necessary will create an envelope.
|
||||
func ServeJSON(w http.ResponseWriter, err error) error {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
var sc int
|
||||
|
||||
switch errs := err.(type) {
|
||||
case Errors:
|
||||
if len(errs) < 1 {
|
||||
break
|
||||
}
|
||||
|
||||
if err, ok := errs[0].(ErrorCoder); ok {
|
||||
sc = err.ErrorCode().Descriptor().HTTPStatusCode
|
||||
}
|
||||
case ErrorCoder:
|
||||
sc = errs.ErrorCode().Descriptor().HTTPStatusCode
|
||||
err = Errors{err} // create an envelope.
|
||||
default:
|
||||
// We just have an unhandled error type, so just place in an envelope
|
||||
// and move along.
|
||||
err = Errors{err}
|
||||
}
|
||||
|
||||
if sc == 0 {
|
||||
sc = http.StatusInternalServerError
|
||||
}
|
||||
|
||||
w.WriteHeader(sc)
|
||||
|
||||
if err := json.NewEncoder(w).Encode(err); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -379,7 +379,9 @@ func (app *App) dispatcher(dispatch dispatchFunc) http.Handler {
|
|||
context.Errors = append(context.Errors, v2.ErrorCodeNameInvalid.WithDetail(err))
|
||||
}
|
||||
|
||||
serveJSON(w, context.Errors)
|
||||
if err := errcode.ServeJSON(w, context.Errors); err != nil {
|
||||
ctxu.GetLogger(context).Errorf("error serving error json: %v (from %v)", err, context.Errors)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -393,7 +395,9 @@ func (app *App) dispatcher(dispatch dispatchFunc) http.Handler {
|
|||
ctxu.GetLogger(context).Errorf("error initializing repository middleware: %v", err)
|
||||
context.Errors = append(context.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
|
||||
|
||||
serveJSON(w, context.Errors)
|
||||
if err := errcode.ServeJSON(w, context.Errors); err != nil {
|
||||
ctxu.GetLogger(context).Errorf("error serving error json: %v (from %v)", err, context.Errors)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -405,7 +409,9 @@ func (app *App) dispatcher(dispatch dispatchFunc) http.Handler {
|
|||
if context.Errors.Len() > 0 {
|
||||
app.logError(context, context.Errors)
|
||||
|
||||
serveJSON(w, context.Errors)
|
||||
if err := errcode.ServeJSON(w, context.Errors); err != nil {
|
||||
ctxu.GetLogger(context).Errorf("error serving error json: %v (from %v)", err, context.Errors)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -482,11 +488,9 @@ func (app *App) authorized(w http.ResponseWriter, r *http.Request, context *Cont
|
|||
// base route is accessed. This section prevents us from making
|
||||
// that mistake elsewhere in the code, allowing any operation to
|
||||
// proceed.
|
||||
|
||||
var errs errcode.Errors
|
||||
errs = append(errs, v2.ErrorCodeUnauthorized)
|
||||
|
||||
serveJSON(w, errs)
|
||||
if err := errcode.ServeJSON(w, v2.ErrorCodeUnauthorized); err != nil {
|
||||
ctxu.GetLogger(context).Errorf("error serving error json: %v (from %v)", err, context.Errors)
|
||||
}
|
||||
return fmt.Errorf("forbidden: no repository name")
|
||||
}
|
||||
}
|
||||
|
@ -498,9 +502,9 @@ func (app *App) authorized(w http.ResponseWriter, r *http.Request, context *Cont
|
|||
// Add the appropriate WWW-Auth header
|
||||
err.ServeHTTP(w, r)
|
||||
|
||||
var errs errcode.Errors
|
||||
errs = append(errs, v2.ErrorCodeUnauthorized.WithDetail(accessRecords))
|
||||
serveJSON(w, errs)
|
||||
if err := errcode.ServeJSON(w, v2.ErrorCodeUnauthorized.WithDetail(accessRecords)); err != nil {
|
||||
ctxu.GetLogger(context).Errorf("error serving error json: %v (from %v)", err, context.Errors)
|
||||
}
|
||||
default:
|
||||
// This condition is a potential security problem either in
|
||||
// the configuration or whatever is backing the access
|
||||
|
|
|
@ -1,43 +1,10 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
)
|
||||
|
||||
// serveJSON marshals v and sets the content-type header to
|
||||
// 'application/json'. If a different status code is required, call
|
||||
// ResponseWriter.WriteHeader before this function.
|
||||
func serveJSON(w http.ResponseWriter, v interface{}) error {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
sc := http.StatusInternalServerError
|
||||
|
||||
if errs, ok := v.(errcode.Errors); ok && len(errs) > 0 {
|
||||
if err, ok := errs[0].(errcode.ErrorCoder); ok {
|
||||
if sc2 := err.ErrorCode().Descriptor().HTTPStatusCode; sc2 != 0 {
|
||||
sc = sc2
|
||||
}
|
||||
}
|
||||
} else if err, ok := v.(errcode.ErrorCoder); ok {
|
||||
if sc2 := err.ErrorCode().Descriptor().HTTPStatusCode; sc2 != 0 {
|
||||
sc = sc2
|
||||
}
|
||||
}
|
||||
|
||||
w.WriteHeader(sc)
|
||||
|
||||
enc := json.NewEncoder(w)
|
||||
|
||||
if err := enc.Encode(v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// closeResources closes all the provided resources after running the target
|
||||
// handler.
|
||||
func closeResources(handler http.Handler, closers ...io.Closer) http.Handler {
|
||||
|
|
Loading…
Reference in a new issue