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.
 | 
					// and the integer format may change and should *never* be exported.
 | 
				
			||||||
type ErrorCode int
 | 
					type ErrorCode int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ error = ErrorCode(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ErrorCode just returns itself
 | 
					// ErrorCode just returns itself
 | 
				
			||||||
func (ec ErrorCode) ErrorCode() ErrorCode {
 | 
					func (ec ErrorCode) ErrorCode() ErrorCode {
 | 
				
			||||||
	return ec
 | 
						return ec
 | 
				
			||||||
| 
						 | 
					@ -93,6 +95,8 @@ type Error struct {
 | 
				
			||||||
	// variable substitution right before showing the message to the user
 | 
						// variable substitution right before showing the message to the user
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ error = Error{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ErrorCode returns the ID/Value of this Error
 | 
					// ErrorCode returns the ID/Value of this Error
 | 
				
			||||||
func (e Error) ErrorCode() ErrorCode {
 | 
					func (e Error) ErrorCode() ErrorCode {
 | 
				
			||||||
	return e.Code
 | 
						return e.Code
 | 
				
			||||||
| 
						 | 
					@ -163,6 +167,8 @@ func ParseErrorCode(value string) ErrorCode {
 | 
				
			||||||
// for use within the application.
 | 
					// for use within the application.
 | 
				
			||||||
type Errors []error
 | 
					type Errors []error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ error = Errors{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (errs Errors) Error() string {
 | 
					func (errs Errors) Error() string {
 | 
				
			||||||
	switch len(errs) {
 | 
						switch len(errs) {
 | 
				
			||||||
	case 0:
 | 
						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))
 | 
										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
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -393,7 +395,9 @@ func (app *App) dispatcher(dispatch dispatchFunc) http.Handler {
 | 
				
			||||||
				ctxu.GetLogger(context).Errorf("error initializing repository middleware: %v", err)
 | 
									ctxu.GetLogger(context).Errorf("error initializing repository middleware: %v", err)
 | 
				
			||||||
				context.Errors = append(context.Errors, errcode.ErrorCodeUnknown.WithDetail(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
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -405,7 +409,9 @@ func (app *App) dispatcher(dispatch dispatchFunc) http.Handler {
 | 
				
			||||||
		if context.Errors.Len() > 0 {
 | 
							if context.Errors.Len() > 0 {
 | 
				
			||||||
			app.logError(context, context.Errors)
 | 
								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
 | 
								// base route is accessed. This section prevents us from making
 | 
				
			||||||
			// that mistake elsewhere in the code, allowing any operation to
 | 
								// that mistake elsewhere in the code, allowing any operation to
 | 
				
			||||||
			// proceed.
 | 
								// proceed.
 | 
				
			||||||
 | 
								if err := errcode.ServeJSON(w, v2.ErrorCodeUnauthorized); err != nil {
 | 
				
			||||||
			var errs errcode.Errors
 | 
									ctxu.GetLogger(context).Errorf("error serving error json: %v (from %v)", err, context.Errors)
 | 
				
			||||||
			errs = append(errs, v2.ErrorCodeUnauthorized)
 | 
								}
 | 
				
			||||||
 | 
					 | 
				
			||||||
			serveJSON(w, errs)
 | 
					 | 
				
			||||||
			return fmt.Errorf("forbidden: no repository name")
 | 
								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
 | 
								// Add the appropriate WWW-Auth header
 | 
				
			||||||
			err.ServeHTTP(w, r)
 | 
								err.ServeHTTP(w, r)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			var errs errcode.Errors
 | 
								if err := errcode.ServeJSON(w, v2.ErrorCodeUnauthorized.WithDetail(accessRecords)); err != nil {
 | 
				
			||||||
			errs = append(errs, v2.ErrorCodeUnauthorized.WithDetail(accessRecords))
 | 
									ctxu.GetLogger(context).Errorf("error serving error json: %v (from %v)", err, context.Errors)
 | 
				
			||||||
			serveJSON(w, errs)
 | 
								}
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			// This condition is a potential security problem either in
 | 
								// This condition is a potential security problem either in
 | 
				
			||||||
			// the configuration or whatever is backing the access
 | 
								// the configuration or whatever is backing the access
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,43 +1,10 @@
 | 
				
			||||||
package handlers
 | 
					package handlers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"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
 | 
					// closeResources closes all the provided resources after running the target
 | 
				
			||||||
// handler.
 | 
					// handler.
 | 
				
			||||||
func closeResources(handler http.Handler, closers ...io.Closer) http.Handler {
 | 
					func closeResources(handler http.Handler, closers ...io.Closer) http.Handler {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue