Merge commit '4aa2e2eaa01e04e05cfbcd765afb5059ff85b8e1' as 'third_party/src/github.com/gorilla/context'
commit
111b2639cd
@ -0,0 +1,7 @@ |
||||
language: go |
||||
|
||||
go: |
||||
- 1.0 |
||||
- 1.1 |
||||
- 1.2 |
||||
- tip |
@ -0,0 +1,27 @@ |
||||
Copyright (c) 2012 Rodrigo Moraes. All rights reserved. |
||||
|
||||
Redistribution and use in source and binary forms, with or without |
||||
modification, are permitted provided that the following conditions are |
||||
met: |
||||
|
||||
* Redistributions of source code must retain the above copyright |
||||
notice, this list of conditions and the following disclaimer. |
||||
* Redistributions in binary form must reproduce the above |
||||
copyright notice, this list of conditions and the following disclaimer |
||||
in the documentation and/or other materials provided with the |
||||
distribution. |
||||
* Neither the name of Google Inc. nor the names of its |
||||
contributors may be used to endorse or promote products derived from |
||||
this software without specific prior written permission. |
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,7 @@ |
||||
context |
||||
======= |
||||
[![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context) |
||||
|
||||
gorilla/context is a general purpose registry for global request variables. |
||||
|
||||
Read the full documentation here: http://www.gorillatoolkit.org/pkg/context |
@ -0,0 +1,143 @@ |
||||
// 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 ( |
||||
mutex sync.RWMutex |
||||
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() |
||||
if data[r] == nil { |
||||
data[r] = make(map[interface{}]interface{}) |
||||
datat[r] = time.Now().Unix() |
||||
} |
||||
data[r][key] = val |
||||
mutex.Unlock() |
||||
} |
||||
|
||||
// Get returns a value stored for a given key in a given request.
|
||||
func Get(r *http.Request, key interface{}) interface{} { |
||||
mutex.RLock() |
||||
if ctx := data[r]; ctx != nil { |
||||
value := ctx[key] |
||||
mutex.RUnlock() |
||||
return value |
||||
} |
||||
mutex.RUnlock() |
||||
return nil |
||||
} |
||||
|
||||
// GetOk returns stored value and presence state like multi-value return of map access.
|
||||
func GetOk(r *http.Request, key interface{}) (interface{}, bool) { |
||||
mutex.RLock() |
||||
if _, ok := data[r]; ok { |
||||
value, ok := data[r][key] |
||||
mutex.RUnlock() |
||||
return value, ok |
||||
} |
||||
mutex.RUnlock() |
||||
return nil, false |
||||
} |
||||
|
||||
// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests.
|
||||
func GetAll(r *http.Request) map[interface{}]interface{} { |
||||
mutex.RLock() |
||||
if context, ok := data[r]; ok { |
||||
result := make(map[interface{}]interface{}, len(context)) |
||||
for k, v := range context { |
||||
result[k] = v |
||||
} |
||||
mutex.RUnlock() |
||||
return result |
||||
} |
||||
mutex.RUnlock() |
||||
return nil |
||||
} |
||||
|
||||
// GetAllOk returns all stored values for the request as a map and a boolean value that indicates if
|
||||
// the request was registered.
|
||||
func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) { |
||||
mutex.RLock() |
||||
context, ok := data[r] |
||||
result := make(map[interface{}]interface{}, len(context)) |
||||
for k, v := range context { |
||||
result[k] = v |
||||
} |
||||
mutex.RUnlock() |
||||
return result, ok |
||||
} |
||||
|
||||
// Delete removes a value stored for a given key in a given request.
|
||||
func Delete(r *http.Request, key interface{}) { |
||||
mutex.Lock() |
||||
if data[r] != nil { |
||||
delete(data[r], key) |
||||
} |
||||
mutex.Unlock() |
||||
} |
||||
|
||||
// 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) { |
||||
mutex.Lock() |
||||
clear(r) |
||||
mutex.Unlock() |
||||
} |
||||
|
||||
// clear is Clear without the lock.
|
||||
func clear(r *http.Request) { |
||||
delete(data, r) |
||||
delete(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 { |
||||
mutex.Lock() |
||||
count := 0 |
||||
if maxAge <= 0 { |
||||
count = len(data) |
||||
data = make(map[*http.Request]map[interface{}]interface{}) |
||||
datat = make(map[*http.Request]int64) |
||||
} else { |
||||
min := time.Now().Unix() - int64(maxAge) |
||||
for r := range data { |
||||
if datat[r] < min { |
||||
clear(r) |
||||
count++ |
||||
} |
||||
} |
||||
} |
||||
mutex.Unlock() |
||||
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) |
||||
}) |
||||
} |
@ -0,0 +1,161 @@ |
||||
// 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" |
||||
"testing" |
||||
) |
||||
|
||||
type keyType int |
||||
|
||||
const ( |
||||
key1 keyType = iota |
||||
key2 |
||||
) |
||||
|
||||
func TestContext(t *testing.T) { |
||||
assertEqual := func(val interface{}, exp interface{}) { |
||||
if val != exp { |
||||
t.Errorf("Expected %v, got %v.", exp, val) |
||||
} |
||||
} |
||||
|
||||
r, _ := http.NewRequest("GET", "http://localhost:8080/", nil) |
||||
emptyR, _ := http.NewRequest("GET", "http://localhost:8080/", nil) |
||||
|
||||
// Get()
|
||||
assertEqual(Get(r, key1), nil) |
||||
|
||||
// Set()
|
||||
Set(r, key1, "1") |
||||
assertEqual(Get(r, key1), "1") |
||||
assertEqual(len(data[r]), 1) |
||||
|
||||
Set(r, key2, "2") |
||||
assertEqual(Get(r, key2), "2") |
||||
assertEqual(len(data[r]), 2) |
||||
|
||||
//GetOk
|
||||
value, ok := GetOk(r, key1) |
||||
assertEqual(value, "1") |
||||
assertEqual(ok, true) |
||||
|
||||
value, ok = GetOk(r, "not exists") |
||||
assertEqual(value, nil) |
||||
assertEqual(ok, false) |
||||
|
||||
Set(r, "nil value", nil) |
||||
value, ok = GetOk(r, "nil value") |
||||
assertEqual(value, nil) |
||||
assertEqual(ok, true) |
||||
|
||||
// GetAll()
|
||||
values := GetAll(r) |
||||
assertEqual(len(values), 3) |
||||
|
||||
// GetAll() for empty request
|
||||
values = GetAll(emptyR) |
||||
if values != nil { |
||||
t.Error("GetAll didn't return nil value for invalid request") |
||||
} |
||||
|
||||
// GetAllOk()
|
||||
values, ok = GetAllOk(r) |
||||
assertEqual(len(values), 3) |
||||
assertEqual(ok, true) |
||||
|
||||
// GetAllOk() for empty request
|
||||
values, ok = GetAllOk(emptyR) |
||||
assertEqual(value, nil) |
||||
assertEqual(ok, false) |
||||
|
||||
// Delete()
|
||||
Delete(r, key1) |
||||
assertEqual(Get(r, key1), nil) |
||||
assertEqual(len(data[r]), 2) |
||||
|
||||
Delete(r, key2) |
||||
assertEqual(Get(r, key2), nil) |
||||
assertEqual(len(data[r]), 1) |
||||
|
||||
// Clear()
|
||||
Clear(r) |
||||
assertEqual(len(data), 0) |
||||
} |
||||
|
||||
func parallelReader(r *http.Request, key string, iterations int, wait, done chan struct{}) { |
||||
<-wait |
||||
for i := 0; i < iterations; i++ { |
||||
Get(r, key) |
||||
} |
||||
done <- struct{}{} |
||||
|
||||
} |
||||
|
||||
func parallelWriter(r *http.Request, key, value string, iterations int, wait, done chan struct{}) { |
||||
<-wait |
||||
for i := 0; i < iterations; i++ { |
||||
Get(r, key) |
||||
} |
||||
done <- struct{}{} |
||||
|
||||
} |
||||
|
||||
func benchmarkMutex(b *testing.B, numReaders, numWriters, iterations int) { |
||||
|
||||
b.StopTimer() |
||||
r, _ := http.NewRequest("GET", "http://localhost:8080/", nil) |
||||
done := make(chan struct{}) |
||||
b.StartTimer() |
||||
|
||||
for i := 0; i < b.N; i++ { |
||||
wait := make(chan struct{}) |
||||
|
||||
for i := 0; i < numReaders; i++ { |
||||
go parallelReader(r, "test", iterations, wait, done) |
||||
} |
||||
|
||||
for i := 0; i < numWriters; i++ { |
||||
go parallelWriter(r, "test", "123", iterations, wait, done) |
||||
} |
||||
|
||||
close(wait) |
||||
|
||||
for i := 0; i < numReaders+numWriters; i++ { |
||||
<-done |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
func BenchmarkMutexSameReadWrite1(b *testing.B) { |
||||
benchmarkMutex(b, 1, 1, 32) |
||||
} |
||||
func BenchmarkMutexSameReadWrite2(b *testing.B) { |
||||
benchmarkMutex(b, 2, 2, 32) |
||||
} |
||||
func BenchmarkMutexSameReadWrite4(b *testing.B) { |
||||
benchmarkMutex(b, 4, 4, 32) |
||||
} |
||||
func BenchmarkMutex1(b *testing.B) { |
||||
benchmarkMutex(b, 2, 8, 32) |
||||
} |
||||
func BenchmarkMutex2(b *testing.B) { |
||||
benchmarkMutex(b, 16, 4, 64) |
||||
} |
||||
func BenchmarkMutex3(b *testing.B) { |
||||
benchmarkMutex(b, 1, 2, 128) |
||||
} |
||||
func BenchmarkMutex4(b *testing.B) { |
||||
benchmarkMutex(b, 128, 32, 256) |
||||
} |
||||
func BenchmarkMutex5(b *testing.B) { |
||||
benchmarkMutex(b, 1024, 2048, 64) |
||||
} |
||||
func BenchmarkMutex6(b *testing.B) { |
||||
benchmarkMutex(b, 2048, 1024, 512) |
||||
} |
@ -0,0 +1,82 @@ |
||||
// 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 stores values shared during a request lifetime. |
||||
|
||||
For example, a router can set variables extracted from the URL and later |
||||
application handlers can access those values, or it can be used to store |
||||
sessions values to be saved at the end of a request. There are several |
||||
others common uses. |
||||
|
||||
The idea was posted by Brad Fitzpatrick to the go-nuts mailing list: |
||||
|
||||
http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53
|
||||
|
||||
Here's the basic usage: first define the keys that you will need. The key |
||||
type is interface{} so a key can be of any type that supports equality. |
||||
Here we define a key using a custom int type to avoid name collisions: |
||||
|
||||
package foo |
||||
|
||||
import ( |
||||
"github.com/gorilla/context" |
||||
) |
||||
|
||||
type key int |
||||
|
||||
const MyKey key = 0 |
||||
|
||||
Then set a variable. Variables are bound to an http.Request object, so you |
||||
need a request instance to set a value: |
||||
|
||||
context.Set(r, MyKey, "bar") |
||||
|
||||
The application can later access the variable using the same key you provided: |
||||
|
||||
func MyHandler(w http.ResponseWriter, r *http.Request) { |
||||
// val is "bar".
|
||||
val := context.Get(r, foo.MyKey) |
||||
|
||||
// returns ("bar", true)
|
||||
val, ok := context.GetOk(r, foo.MyKey) |
||||
// ...
|
||||
} |
||||
|
||||
And that's all about the basic usage. We discuss some other ideas below. |
||||
|
||||
Any type can be stored in the context. To enforce a given type, make the key |
||||
private and wrap Get() and Set() to accept and return values of a specific |
||||
type: |
||||
|
||||
type key int |
||||
|
||||
const mykey key = 0 |
||||
|
||||
// GetMyKey returns a value for this package from the request values.
|
||||
func GetMyKey(r *http.Request) SomeType { |
||||
if rv := context.Get(r, mykey); rv != nil { |
||||
return rv.(SomeType) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// SetMyKey sets a value for this package in the request values.
|
||||
func SetMyKey(r *http.Request, val SomeType) { |
||||
context.Set(r, mykey, val) |
||||
} |
||||
|
||||
Variables must be cleared at the end of a request, to remove all values |
||||
that were stored. This can be done in an http.Handler, after a request was |
||||
served. Just call Clear() passing the request: |
||||
|
||||
context.Clear(r) |
||||
|
||||
...or use ClearHandler(), which conveniently wraps an http.Handler to clear |
||||
variables at the end of a request lifetime. |
||||
|
||||
The Routers from the packages gorilla/mux and gorilla/pat call Clear() |
||||
so if you are using either of them you don't need to clear the context manually. |
||||
*/ |
||||
package context |
Loading…
Reference in new issue