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