123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- /*
- Copyright 2014 The Kubernetes Authors.
- 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")
- }
|