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  }