Merge pull request #705 from stevvooe/export-servejson-errors
Export ServeJSON for serving error codesmaster
						commit
						5c6c88196d
					
				| 
						 | 
				
			
			@ -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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 New Issue