// dispatcher returns a handler that constructs a request specific context and // handler, using the dispatch factory function. func (app *App) dispatcher(dispatch dispatchFunc) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { for headerName, headerValues := range app.Config.HTTP.Headers { for _, value := range headerValues { w.Header().Add(headerName, value) } } context := app.context(w, r) if err := app.authorized(w, r, context); err != nil { ctxu.GetLogger(context).Warnf("error authorizing context: %v", err) return } // Add username to request logging context.Context = ctxu.WithLogger(context.Context, ctxu.GetLogger(context.Context, "auth.user.name")) if app.nameRequired(r) { repository, err := app.registry.Repository(context, getName(context)) if err != nil { ctxu.GetLogger(context).Errorf("error resolving repository: %v", err) switch err := err.(type) { case distribution.ErrRepositoryUnknown: context.Errors = append(context.Errors, v2.ErrorCodeNameUnknown.WithDetail(err)) case distribution.ErrRepositoryNameInvalid: context.Errors = append(context.Errors, v2.ErrorCodeNameInvalid.WithDetail(err)) } if err := errcode.ServeJSON(w, context.Errors); err != nil { ctxu.GetLogger(context).Errorf("error serving error json: %v (from %v)", err, context.Errors) } return } // assign and decorate the authorized repository with an event bridge. context.Repository = notifications.Listen( repository, app.eventBridge(context, r)) context.Repository, err = applyRepoMiddleware(context.Context, context.Repository, app.Config.Middleware["repository"]) if err != nil { ctxu.GetLogger(context).Errorf("error initializing repository middleware: %v", err) context.Errors = append(context.Errors, errcode.ErrorCodeUnknown.WithDetail(err)) if err := errcode.ServeJSON(w, context.Errors); err != nil { ctxu.GetLogger(context).Errorf("error serving error json: %v (from %v)", err, context.Errors) } return } } dispatch(context, r).ServeHTTP(w, r) // Automated error response handling here. Handlers may return their // own errors if they need different behavior (such as range errors // for layer upload). if context.Errors.Len() > 0 { if err := errcode.ServeJSON(w, context.Errors); err != nil { ctxu.GetLogger(context).Errorf("error serving error json: %v (from %v)", err, context.Errors) } app.logError(context, context.Errors) } }) }
// dispatcher returns a handler that constructs a request specific context and // handler, using the dispatch factory function. func (app *App) dispatcher(dispatch dispatchFunc, nameRequired nameRequiredFunc, accessRecords customAccessRecordsFunc) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { context := app.context(w, r) if err := app.authorized(w, r, context, nameRequired, accessRecords(r)); err != nil { ctxu.GetLogger(context).Errorf("error authorizing context: %v", err) return } // Add username to request logging context.Context = ctxu.WithLogger(context.Context, ctxu.GetLogger(context.Context, "auth.user.name")) if nameRequired(r) { repository, err := app.registry.Repository(context, getName(context)) if err != nil { ctxu.GetLogger(context).Errorf("error resolving repository: %v", err) switch err := err.(type) { case distribution.ErrRepositoryUnknown: context.Errors.Push(v2.ErrorCodeNameUnknown, err) case distribution.ErrRepositoryNameInvalid: context.Errors.Push(v2.ErrorCodeNameInvalid, err) } w.WriteHeader(http.StatusBadRequest) serveJSON(w, context.Errors) return } // assign and decorate the authorized repository with an event bridge. context.Repository = notifications.Listen( repository, app.eventBridge(context, r)) context.Repository, err = applyRepoMiddleware(context.Repository, app.Config.Middleware["repository"]) if err != nil { ctxu.GetLogger(context).Errorf("error initializing repository middleware: %v", err) context.Errors.Push(v2.ErrorCodeUnknown, err) w.WriteHeader(http.StatusInternalServerError) serveJSON(w, context.Errors) return } } dispatch(context, r).ServeHTTP(w, r) // Automated error response handling here. Handlers may return their // own errors if they need different behavior (such as range errors // for layer upload). if context.Errors.Len() > 0 { if context.Value("http.response.status") == 0 { // TODO(stevvooe): Getting this value from the context is a // bit of a hack. We can further address with some of our // future refactoring. w.WriteHeader(http.StatusBadRequest) } serveJSON(w, context.Errors) } }) }