// Copyright 2012 The Gorilla Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package context import ( "net/http" "sync" "time" ) var globalContext *Context = NewContext() type Context struct { mutex sync.RWMutex data map[*http.Request]map[interface{}]interface{} datat map[*http.Request]int64 } func NewContext() *Context { data := make(map[*http.Request]map[interface{}]interface{}) datat := make(map[*http.Request]int64) return &Context{data: data, datat: datat} } // Set stores a value for a given key in a given request. func Set(r *http.Request, key, val interface{}) { globalContext.Set(r, key, val) } func (c *Context) Set(r *http.Request, key, val interface{}) { c.mutex.Lock() defer c.mutex.Unlock() if c.data[r] == nil { c.data[r] = make(map[interface{}]interface{}) c.datat[r] = time.Now().Unix() } c.data[r][key] = val } // Get returns a value stored for a given key in a given request. func Get(r *http.Request, key interface{}) interface{} { return globalContext.Get(r, key) } func (c *Context) Get(r *http.Request, key interface{}) interface{} { c.mutex.RLock() defer c.mutex.RUnlock() if c.data[r] != nil { return c.data[r][key] } return nil } // Delete removes a value stored for a given key in a given request. func Delete(r *http.Request, key interface{}) { globalContext.Delete(r, key) } func (c *Context) Delete(r *http.Request, key interface{}) { c.mutex.Lock() defer c.mutex.Unlock() if c.data[r] != nil { delete(c.data[r], key) } } // Clear removes all values stored for a given request. // // This is usually called by a handler wrapper to clean up request // variables at the end of a request lifetime. See ClearHandler(). func Clear(r *http.Request) { globalContext.Clear(r) } func (c *Context) Clear(r *http.Request) { c.mutex.Lock() defer c.mutex.Unlock() c.clear(r) } // clear is Clear without the lock. func (c *Context) clear(r *http.Request) { delete(c.data, r) delete(c.datat, r) } // Purge removes request data stored for longer than maxAge, in seconds. // It returns the amount of requests removed. // // If maxAge <= 0, all request data is removed. // // This is only used for sanity check: in case context cleaning was not // properly set some request data can be kept forever, consuming an increasing // amount of memory. In case this is detected, Purge() must be called // periodically until the problem is fixed. func Purge(maxAge int) int { return globalContext.Purge(maxAge) } func (c *Context) Purge(maxAge int) int { c.mutex.Lock() defer c.mutex.Unlock() count := 0 if maxAge <= 0 { count = len(c.data) c.data = make(map[*http.Request]map[interface{}]interface{}) c.datat = make(map[*http.Request]int64) } else { min := time.Now().Unix() - int64(maxAge) for r, _ := range c.data { if c.datat[r] < min { c.clear(r) count++ } } } return count } // ClearHandler wraps an http.Handler and clears request values at the end // of a request lifetime. func ClearHandler(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer Clear(r) h.ServeHTTP(w, r) }) }