github.com/npaton/distribution@v2.3.1-rc.0+incompatible/registry/handlers/context.go (about) 1 package handlers 2 3 import ( 4 "fmt" 5 "net/http" 6 "sync" 7 8 "github.com/docker/distribution" 9 ctxu "github.com/docker/distribution/context" 10 "github.com/docker/distribution/digest" 11 "github.com/docker/distribution/registry/api/errcode" 12 "github.com/docker/distribution/registry/api/v2" 13 "golang.org/x/net/context" 14 ) 15 16 // Context should contain the request specific context for use in across 17 // handlers. Resources that don't need to be shared across handlers should not 18 // be on this object. 19 type Context struct { 20 // App points to the application structure that created this context. 21 *App 22 context.Context 23 24 // Repository is the repository for the current request. All requests 25 // should be scoped to a single repository. This field may be nil. 26 Repository distribution.Repository 27 28 // Errors is a collection of errors encountered during the request to be 29 // returned to the client API. If errors are added to the collection, the 30 // handler *must not* start the response via http.ResponseWriter. 31 Errors errcode.Errors 32 33 urlBuilder *v2.URLBuilder 34 35 // TODO(stevvooe): The goal is too completely factor this context and 36 // dispatching out of the web application. Ideally, we should lean on 37 // context.Context for injection of these resources. 38 } 39 40 // Value overrides context.Context.Value to ensure that calls are routed to 41 // correct context. 42 func (ctx *Context) Value(key interface{}) interface{} { 43 return ctx.Context.Value(key) 44 } 45 46 func getName(ctx context.Context) (name string) { 47 return ctxu.GetStringValue(ctx, "vars.name") 48 } 49 50 func getReference(ctx context.Context) (reference string) { 51 return ctxu.GetStringValue(ctx, "vars.reference") 52 } 53 54 var errDigestNotAvailable = fmt.Errorf("digest not available in context") 55 56 func getDigest(ctx context.Context) (dgst digest.Digest, err error) { 57 dgstStr := ctxu.GetStringValue(ctx, "vars.digest") 58 59 if dgstStr == "" { 60 ctxu.GetLogger(ctx).Errorf("digest not available") 61 return "", errDigestNotAvailable 62 } 63 64 d, err := digest.ParseDigest(dgstStr) 65 if err != nil { 66 ctxu.GetLogger(ctx).Errorf("error parsing digest=%q: %v", dgstStr, err) 67 return "", err 68 } 69 70 return d, nil 71 } 72 73 func getUploadUUID(ctx context.Context) (uuid string) { 74 return ctxu.GetStringValue(ctx, "vars.uuid") 75 } 76 77 // getUserName attempts to resolve a username from the context and request. If 78 // a username cannot be resolved, the empty string is returned. 79 func getUserName(ctx context.Context, r *http.Request) string { 80 username := ctxu.GetStringValue(ctx, "auth.user.name") 81 82 // Fallback to request user with basic auth 83 if username == "" { 84 var ok bool 85 uname, _, ok := basicAuth(r) 86 if ok { 87 username = uname 88 } 89 } 90 91 return username 92 } 93 94 // contextManager allows us to associate net/context.Context instances with a 95 // request, based on the memory identity of http.Request. This prepares http- 96 // level context, which is not application specific. If this is called, 97 // (*contextManager).release must be called on the context when the request is 98 // completed. 99 // 100 // Providing this circumvents a lot of necessity for dispatchers with the 101 // benefit of instantiating the request context much earlier. 102 // 103 // TODO(stevvooe): Consider making this facility a part of the context package. 104 type contextManager struct { 105 contexts map[*http.Request]context.Context 106 mu sync.Mutex 107 } 108 109 // defaultContextManager is just a global instance to register request contexts. 110 var defaultContextManager = newContextManager() 111 112 func newContextManager() *contextManager { 113 return &contextManager{ 114 contexts: make(map[*http.Request]context.Context), 115 } 116 } 117 118 // context either returns a new context or looks it up in the manager. 119 func (cm *contextManager) context(parent context.Context, w http.ResponseWriter, r *http.Request) context.Context { 120 cm.mu.Lock() 121 defer cm.mu.Unlock() 122 123 ctx, ok := cm.contexts[r] 124 if ok { 125 return ctx 126 } 127 128 if parent == nil { 129 parent = ctxu.Background() 130 } 131 132 ctx = ctxu.WithRequest(parent, r) 133 ctx, w = ctxu.WithResponseWriter(ctx, w) 134 ctx = ctxu.WithLogger(ctx, ctxu.GetRequestLogger(ctx)) 135 cm.contexts[r] = ctx 136 137 return ctx 138 } 139 140 // releases frees any associated with resources from request. 141 func (cm *contextManager) release(ctx context.Context) { 142 cm.mu.Lock() 143 defer cm.mu.Unlock() 144 145 r, err := ctxu.GetRequest(ctx) 146 if err != nil { 147 ctxu.GetLogger(ctx).Errorf("no request found in context during release") 148 return 149 } 150 delete(cm.contexts, r) 151 }