git-subtree-dir: vendor/github.com/gorilla/context git-subtree-mainline:a37c567e40git-subtree-split:a85d2e53ba
| @ -0,0 +1,19 @@ | |||||
| language: go | |||||
| sudo: false | |||||
| matrix: | |||||
| include: | |||||
| - go: 1.3 | |||||
| - go: 1.4 | |||||
| - go: 1.5 | |||||
| - go: 1.6 | |||||
| - go: tip | |||||
| install: | |||||
| - go get golang.org/x/tools/cmd/vet | |||||
| script: | |||||
| - go get -t -v ./... | |||||
| - diff -u <(echo -n) <(gofmt -d .) | |||||
| - go tool vet . | |||||
| - go test -v -race ./... | |||||
| @ -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 | |||||
| ======= | |||||
| [](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++ { | |||||
| Set(r, key, value) | |||||
| } | |||||
| 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 | |||||