mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-04 07:49:35 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			116 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			116 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2014 Google Inc. All rights reserved.
 | 
						|
 | 
						|
Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
you may not use this file except in compliance with the License.
 | 
						|
You may obtain a copy of the License at
 | 
						|
 | 
						|
    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
Unless required by applicable law or agreed to in writing, software
 | 
						|
distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
See the License for the specific language governing permissions and
 | 
						|
limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package api
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"net/http"
 | 
						|
	"sync"
 | 
						|
)
 | 
						|
 | 
						|
// RequestContextMapper keeps track of the context associated with a particular request
 | 
						|
type RequestContextMapper interface {
 | 
						|
	// 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.
 | 
						|
	Get(req *http.Request) (Context, bool)
 | 
						|
	// Update maps the request to the given context. If no context was previously associated with the request, an error is returned.
 | 
						|
	// Update should only be called with a descendant context of the previously associated context.
 | 
						|
	// Updating to an unrelated context may return an error in the future.
 | 
						|
	// The context associated with a request should only be updated by a limited set of callers.
 | 
						|
	// Valid examples include the authentication layer, or an audit/tracing layer.
 | 
						|
	Update(req *http.Request, context Context) error
 | 
						|
}
 | 
						|
 | 
						|
type requestContextMap struct {
 | 
						|
	contexts map[*http.Request]Context
 | 
						|
	lock     sync.Mutex
 | 
						|
}
 | 
						|
 | 
						|
// NewRequestContextMapper returns a new RequestContextMapper.
 | 
						|
// The returned mapper must be added as a request filter using NewRequestContextFilter.
 | 
						|
func NewRequestContextMapper() RequestContextMapper {
 | 
						|
	return &requestContextMap{
 | 
						|
		contexts: make(map[*http.Request]Context),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// 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.
 | 
						|
// Get will only return a valid context when called from inside the filter chain set up by NewRequestContextFilter()
 | 
						|
func (c *requestContextMap) Get(req *http.Request) (Context, bool) {
 | 
						|
	c.lock.Lock()
 | 
						|
	defer c.lock.Unlock()
 | 
						|
	context, ok := c.contexts[req]
 | 
						|
	return context, ok
 | 
						|
}
 | 
						|
 | 
						|
// Update maps the request to the given context.
 | 
						|
// If no context was previously associated with the request, an error is returned and the context is ignored.
 | 
						|
func (c *requestContextMap) Update(req *http.Request, context Context) error {
 | 
						|
	c.lock.Lock()
 | 
						|
	defer c.lock.Unlock()
 | 
						|
	if _, ok := c.contexts[req]; !ok {
 | 
						|
		return errors.New("No context associated")
 | 
						|
	}
 | 
						|
	// TODO: ensure the new context is a descendant of the existing one
 | 
						|
	c.contexts[req] = context
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// init maps the request to the given context and returns true if there was no context associated with the request already.
 | 
						|
// if a context was already associated with the request, it ignores the given context and returns false.
 | 
						|
// init is intentionally unexported to ensure that all init calls are paired with a remove after a request is handled
 | 
						|
func (c *requestContextMap) init(req *http.Request, context Context) bool {
 | 
						|
	c.lock.Lock()
 | 
						|
	defer c.lock.Unlock()
 | 
						|
	if _, exists := c.contexts[req]; exists {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	c.contexts[req] = context
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
// remove is intentionally unexported to ensure that the context is not removed until a request is handled
 | 
						|
func (c *requestContextMap) remove(req *http.Request) {
 | 
						|
	c.lock.Lock()
 | 
						|
	defer c.lock.Unlock()
 | 
						|
	delete(c.contexts, req)
 | 
						|
}
 | 
						|
 | 
						|
// NewRequestContextFilter ensures there is a Context object associated with the request before calling the passed handler.
 | 
						|
// After the passed handler runs, the context is cleaned up.
 | 
						|
func NewRequestContextFilter(mapper RequestContextMapper, handler http.Handler) (http.Handler, error) {
 | 
						|
	if mapper, ok := mapper.(*requestContextMap); ok {
 | 
						|
		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
						|
			if mapper.init(req, NewContext()) {
 | 
						|
				// If we were the ones to successfully initialize, pair with a remove
 | 
						|
				defer mapper.remove(req)
 | 
						|
			}
 | 
						|
			handler.ServeHTTP(w, req)
 | 
						|
		}), nil
 | 
						|
	} else {
 | 
						|
		return handler, errors.New("Unknown RequestContextMapper implementation.")
 | 
						|
	}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
// IsEmpty returns true if there are no contexts registered, or an error if it could not be determined. Intended for use by tests.
 | 
						|
func IsEmpty(requestsToContexts RequestContextMapper) (bool, error) {
 | 
						|
	if requestsToContexts, ok := requestsToContexts.(*requestContextMap); ok {
 | 
						|
		return len(requestsToContexts.contexts) == 0, nil
 | 
						|
	}
 | 
						|
	return true, errors.New("Unknown RequestContextMapper implementation")
 | 
						|
}
 |