github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/requestcontext.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors All rights reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package api
    18  
    19  import (
    20  	"errors"
    21  	"net/http"
    22  	"sync"
    23  )
    24  
    25  // RequestContextMapper keeps track of the context associated with a particular request
    26  type RequestContextMapper interface {
    27  	// Get returns the context associated with the given request (if any), and true if the request has an associated context, and false if it does not.
    28  	Get(req *http.Request) (Context, bool)
    29  	// Update maps the request to the given context. If no context was previously associated with the request, an error is returned.
    30  	// Update should only be called with a descendant context of the previously associated context.
    31  	// Updating to an unrelated context may return an error in the future.
    32  	// The context associated with a request should only be updated by a limited set of callers.
    33  	// Valid examples include the authentication layer, or an audit/tracing layer.
    34  	Update(req *http.Request, context Context) error
    35  }
    36  
    37  type requestContextMap struct {
    38  	contexts map[*http.Request]Context
    39  	lock     sync.Mutex
    40  }
    41  
    42  // NewRequestContextMapper returns a new RequestContextMapper.
    43  // The returned mapper must be added as a request filter using NewRequestContextFilter.
    44  func NewRequestContextMapper() RequestContextMapper {
    45  	return &requestContextMap{
    46  		contexts: make(map[*http.Request]Context),
    47  	}
    48  }
    49  
    50  // Get returns the context associated with the given request (if any), and true if the request has an associated context, and false if it does not.
    51  // Get will only return a valid context when called from inside the filter chain set up by NewRequestContextFilter()
    52  func (c *requestContextMap) Get(req *http.Request) (Context, bool) {
    53  	c.lock.Lock()
    54  	defer c.lock.Unlock()
    55  	context, ok := c.contexts[req]
    56  	return context, ok
    57  }
    58  
    59  // Update maps the request to the given context.
    60  // If no context was previously associated with the request, an error is returned and the context is ignored.
    61  func (c *requestContextMap) Update(req *http.Request, context Context) error {
    62  	c.lock.Lock()
    63  	defer c.lock.Unlock()
    64  	if _, ok := c.contexts[req]; !ok {
    65  		return errors.New("No context associated")
    66  	}
    67  	// TODO: ensure the new context is a descendant of the existing one
    68  	c.contexts[req] = context
    69  	return nil
    70  }
    71  
    72  // init maps the request to the given context and returns true if there was no context associated with the request already.
    73  // if a context was already associated with the request, it ignores the given context and returns false.
    74  // init is intentionally unexported to ensure that all init calls are paired with a remove after a request is handled
    75  func (c *requestContextMap) init(req *http.Request, context Context) bool {
    76  	c.lock.Lock()
    77  	defer c.lock.Unlock()
    78  	if _, exists := c.contexts[req]; exists {
    79  		return false
    80  	}
    81  	c.contexts[req] = context
    82  	return true
    83  }
    84  
    85  // remove is intentionally unexported to ensure that the context is not removed until a request is handled
    86  func (c *requestContextMap) remove(req *http.Request) {
    87  	c.lock.Lock()
    88  	defer c.lock.Unlock()
    89  	delete(c.contexts, req)
    90  }
    91  
    92  // NewRequestContextFilter ensures there is a Context object associated with the request before calling the passed handler.
    93  // After the passed handler runs, the context is cleaned up.
    94  func NewRequestContextFilter(mapper RequestContextMapper, handler http.Handler) (http.Handler, error) {
    95  	if mapper, ok := mapper.(*requestContextMap); ok {
    96  		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
    97  			if mapper.init(req, NewContext()) {
    98  				// If we were the ones to successfully initialize, pair with a remove
    99  				defer mapper.remove(req)
   100  			}
   101  			handler.ServeHTTP(w, req)
   102  		}), nil
   103  	} else {
   104  		return handler, errors.New("Unknown RequestContextMapper implementation.")
   105  	}
   106  
   107  }
   108  
   109  // IsEmpty returns true if there are no contexts registered, or an error if it could not be determined. Intended for use by tests.
   110  func IsEmpty(requestsToContexts RequestContextMapper) (bool, error) {
   111  	if requestsToContexts, ok := requestsToContexts.(*requestContextMap); ok {
   112  		return len(requestsToContexts.contexts) == 0, nil
   113  	}
   114  	return true, errors.New("Unknown RequestContextMapper implementation")
   115  }