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