var (
mutex sync.Mutex
data = make(map[*http.Request]map[interface{}]interface{})
datat = make(map[*http.Request]int64)
)
// Set stores a value for a given key in a given request.
func Set(r *http.Request, key, val interface{}) {
mutex.Lock()
defer mutex.Unlock()
if data[r] == nil {
data[r] = make(map[interface{}]interface{})
datat[r] = time.Now().Unix()
}
data[r][key] = val
}
The map is needed because 'r' is not actually (and cannot be) modified, so for a later handler (for the same request) to access the data it must be stored in this map, so 'Get' can get it back. And the map is global, so you need a mutex.
It's definitely not unidiomatic (heck, one of the Go team members inspired gorilla/context). It's not ideal, but at the same time a context map a) is simple b) does not impart significant complexity on middleware and c) should still perform well, even with a fair bit of contention. Map access is pretty fast, and most of the time you're only storing small things in it.
I'd be curious to see/benchmark the results of a more complex, less map-reliant solution vs. a context map: my gut feel is that the map route wouldn't have any problems hitting 10,000req/s on a small 2GB VM. Most of the time, the context map won't be your bottleneck.
As a side note, my understanding is that it is not necessarily un-idiomatic to use a mutex in Go, particularly for shared state. See https://code.google.com/p/go-wiki/wiki/MutexOrChannel