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  }