From 652e24b8c8576ce9c689616f02f62ba6dd7acff3 Mon Sep 17 00:00:00 2001 From: rodrigo moraes Date: Tue, 2 Oct 2012 14:41:51 -0700 Subject: [PATCH 01/33] Initial commit --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..7883672 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +context +======= \ No newline at end of file From 11ae2122d92bb0b626705bccf3795d3e51ffb1de Mon Sep 17 00:00:00 2001 From: moraes Date: Wed, 3 Oct 2012 01:41:46 -0300 Subject: [PATCH 02/33] Initial files. --- LICENSE | 27 +++++++++++++ context.go | 101 ++++++++++++++++++++++++++++++++++++++++++++++++ context_test.go | 52 +++++++++++++++++++++++++ doc.go | 80 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 260 insertions(+) create mode 100644 LICENSE create mode 100644 context.go create mode 100644 context_test.go create mode 100644 doc.go diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0e5fb87 --- /dev/null +++ b/LICENSE @@ -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. diff --git a/context.go b/context.go new file mode 100644 index 0000000..fe5b6ad --- /dev/null +++ b/context.go @@ -0,0 +1,101 @@ +// 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.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 +} + +// Get returns a value stored for a given key in a given request. +func Get(r *http.Request, key interface{}) interface{} { + mutex.Lock() + defer mutex.Unlock() + if data[r] != nil { + return 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{}) { + mutex.Lock() + defer mutex.Unlock() + if data[r] != nil { + delete(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) { + mutex.Lock() + defer mutex.Unlock() + clear(r) +} + +// 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() + defer mutex.Unlock() + 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++ + } + } + } + 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) + }) +} diff --git a/context_test.go b/context_test.go new file mode 100644 index 0000000..314a0b6 --- /dev/null +++ b/context_test.go @@ -0,0 +1,52 @@ +// 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) + + // 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) + + // Delete() + Delete(r, key1) + assertEqual(Get(r, key1), nil) + assertEqual(len(data[r]), 1) + + Delete(r, key2) + assertEqual(Get(r, key2), nil) + assertEqual(len(data[r]), 0) + + // Clear() + Clear(r) + assertEqual(len(data), 0) +} diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..a4cfd50 --- /dev/null +++ b/doc.go @@ -0,0 +1,80 @@ +// 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 gorilla/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) + + // ... + } + +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 Router from the package gorilla/mux calls Clear(), so if you are using it +you don't need to clear the context manually. +*/ +package context From bbd3ed3168245e982981e22ebc2de0e3739ab8a5 Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Wed, 19 Dec 2012 13:15:39 -0800 Subject: [PATCH 03/33] Added a Context type for non-global contexts. Implemented the current globalContext using the new type. --- context.go | 94 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 33 deletions(-) diff --git a/context.go b/context.go index fe5b6ad..3db3b90 100644 --- a/context.go +++ b/context.go @@ -10,39 +10,59 @@ import ( "time" ) -var ( - mutex sync.Mutex - data = make(map[*http.Request]map[interface{}]interface{}) - datat = make(map[*http.Request]int64) -) +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{}) { - mutex.Lock() - defer mutex.Unlock() - if data[r] == nil { - data[r] = make(map[interface{}]interface{}) - datat[r] = time.Now().Unix() + 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() } - data[r][key] = val + 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{} { - mutex.Lock() - defer mutex.Unlock() - if data[r] != nil { - return data[r][key] + 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{}) { - mutex.Lock() - defer mutex.Unlock() - if data[r] != nil { - delete(data[r], key) + 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) } } @@ -51,15 +71,19 @@ func Delete(r *http.Request, key interface{}) { // 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() - defer mutex.Unlock() - clear(r) + 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 clear(r *http.Request) { - delete(data, r) - delete(datat, r) +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. @@ -72,18 +96,22 @@ func clear(r *http.Request) { // 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() - defer mutex.Unlock() + 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(data) - data = make(map[*http.Request]map[interface{}]interface{}) - datat = make(map[*http.Request]int64) + 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 data { - if datat[r] < min { - clear(r) + for r, _ := range c.data { + if c.datat[r] < min { + c.clear(r) count++ } } From cdee84d5cb0250831da215a94c474d7accc6de4d Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Wed, 20 Mar 2013 09:28:31 -0700 Subject: [PATCH 04/33] Documented that gorilla/pat also clears the context as of 9ff7555 --- doc.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc.go b/doc.go index a4cfd50..bc7a880 100644 --- a/doc.go +++ b/doc.go @@ -74,7 +74,7 @@ served. Just call Clear() passing the request: ...or use ClearHandler(), which conveniently wraps an http.Handler to clear variables at the end of a request lifetime. -The Router from the package gorilla/mux calls Clear(), so if you are using it -you don't need to clear the context manually. +The Routers from the package gorilla/mux and gorilla/pat calls Clear(), +so if you are using either of them you don't need to clear the context manually. */ package context From c6c09957da62d92039441decb0984771e76640d2 Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Wed, 20 Mar 2013 09:30:46 -0700 Subject: [PATCH 05/33] Revert "Added a Context type for non-global contexts." This reverts commit bbd3ed3168245e982981e22ebc2de0e3739ab8a5. --- context.go | 94 +++++++++++++++++++----------------------------------- 1 file changed, 33 insertions(+), 61 deletions(-) diff --git a/context.go b/context.go index 3db3b90..fe5b6ad 100644 --- a/context.go +++ b/context.go @@ -10,59 +10,39 @@ import ( "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} -} +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{}) { - 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() + mutex.Lock() + defer mutex.Unlock() + if data[r] == nil { + data[r] = make(map[interface{}]interface{}) + datat[r] = time.Now().Unix() } - c.data[r][key] = val + 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] + mutex.Lock() + defer mutex.Unlock() + if data[r] != nil { + return 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) + mutex.Lock() + defer mutex.Unlock() + if data[r] != nil { + delete(data[r], key) } } @@ -71,19 +51,15 @@ func (c *Context) Delete(r *http.Request, key interface{}) { // 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) + mutex.Lock() + defer mutex.Unlock() + clear(r) } // clear is Clear without the lock. -func (c *Context) clear(r *http.Request) { - delete(c.data, r) - delete(c.datat, r) +func clear(r *http.Request) { + delete(data, r) + delete(datat, r) } // Purge removes request data stored for longer than maxAge, in seconds. @@ -96,22 +72,18 @@ func (c *Context) clear(r *http.Request) { // 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() + mutex.Lock() + defer 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) + 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 c.data { - if c.datat[r] < min { - c.clear(r) + for r, _ := range data { + if datat[r] < min { + clear(r) count++ } } From 12bc3a0632e8421e8b18c318640471a4023cb536 Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Wed, 20 Mar 2013 09:31:41 -0700 Subject: [PATCH 06/33] Fix up grammar in docs. --- doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc.go b/doc.go index bc7a880..2eca20a 100644 --- a/doc.go +++ b/doc.go @@ -74,7 +74,7 @@ served. Just call Clear() passing the request: ...or use ClearHandler(), which conveniently wraps an http.Handler to clear variables at the end of a request lifetime. -The Routers from the package gorilla/mux and gorilla/pat calls Clear(), +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 From a8662172a384296ff59d28f837e151643c662985 Mon Sep 17 00:00:00 2001 From: rodrigo moraes Date: Tue, 9 Apr 2013 07:54:50 -0300 Subject: [PATCH 07/33] Update README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7883672..8ee62b4 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,6 @@ context -======= \ No newline at end of file +======= + +gorilla/context is a general purpose registry for global request variables. + +Read the full documentation here: http://www.gorillatoolkit.org/pkg/context From 3968bf33e768f06bc4fc140e7952cbe3d8453b0c Mon Sep 17 00:00:00 2001 From: Dmitry Bochkarev Date: Thu, 30 May 2013 20:34:35 +0600 Subject: [PATCH 08/33] GetOk(r, key) (value, ok) --- context.go | 11 +++++++++++ context_test.go | 18 ++++++++++++++++-- doc.go | 4 +++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/context.go b/context.go index fe5b6ad..35d6556 100644 --- a/context.go +++ b/context.go @@ -37,6 +37,17 @@ func Get(r *http.Request, key interface{}) interface{} { 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.Lock() + defer mutex.Unlock() + if _, ok := data[r]; ok { + value, ok := data[r][key] + return value, ok + } + return nil, false +} + // Delete removes a value stored for a given key in a given request. func Delete(r *http.Request, key interface{}) { mutex.Lock() diff --git a/context_test.go b/context_test.go index 314a0b6..ff9e2ad 100644 --- a/context_test.go +++ b/context_test.go @@ -37,14 +37,28 @@ func TestContext(t *testing.T) { 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) + // Delete() Delete(r, key1) assertEqual(Get(r, key1), nil) - assertEqual(len(data[r]), 1) + assertEqual(len(data[r]), 2) Delete(r, key2) assertEqual(Get(r, key2), nil) - assertEqual(len(data[r]), 0) + assertEqual(len(data[r]), 1) // Clear() Clear(r) diff --git a/doc.go b/doc.go index 2eca20a..c807928 100644 --- a/doc.go +++ b/doc.go @@ -37,8 +37,10 @@ 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) + val := context.Get(r, foo.MyKey) + // returns true + ok := context.Has(r, foo.MyKey) // ... } From 989863b11f7f9b455d4d0f63ba2fa9c67b621981 Mon Sep 17 00:00:00 2001 From: Dmitry Bochkarev Date: Thu, 30 May 2013 20:38:23 +0600 Subject: [PATCH 09/33] fix doc.go --- doc.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc.go b/doc.go index c807928..2976064 100644 --- a/doc.go +++ b/doc.go @@ -39,8 +39,8 @@ The application can later access the variable using the same key you provided: // val is "bar". val := context.Get(r, foo.MyKey) - // returns true - ok := context.Has(r, foo.MyKey) + // returns ("bar", true) + val, ok := context.GetOk(r, foo.MyKey) // ... } From b8a1d5a34aa03cd9ef7976a3bbe2f3ad3f9bd41c Mon Sep 17 00:00:00 2001 From: Andrews Medina Date: Fri, 29 Nov 2013 22:25:14 -0200 Subject: [PATCH 10/33] go fmt. result of `gofmt -s -w .`. --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index 35d6556..12accb1 100644 --- a/context.go +++ b/context.go @@ -92,7 +92,7 @@ func Purge(maxAge int) int { datat = make(map[*http.Request]int64) } else { min := time.Now().Unix() - int64(maxAge) - for r, _ := range data { + for r := range data { if datat[r] < min { clear(r) count++ From 257e3549c53c64994caf0fe919e877bc1406422d Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Sat, 30 Nov 2013 13:20:06 -0800 Subject: [PATCH 11/33] Add travis.yml --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..70e012b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +language: go + +go: + - 1.0 + - 1.1 + - tip From bb1bb4d8798c7062d4b0a31c6f307ace50e95736 Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Sat, 30 Nov 2013 13:23:44 -0800 Subject: [PATCH 12/33] Add Travis badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8ee62b4..c60a31b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ 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. From a08edd30ad9e104612741163dc087a613829a23c Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Wed, 4 Dec 2013 23:16:03 -0800 Subject: [PATCH 13/33] Add go 1.2 to travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 70e012b..d87d465 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,4 +3,5 @@ language: go go: - 1.0 - 1.1 + - 1.2 - tip From da513c322191274b2443deafded9feb1bef1f1f4 Mon Sep 17 00:00:00 2001 From: pjvds Date: Sat, 13 Jul 2013 20:43:52 +0200 Subject: [PATCH 14/33] Adds GetAll method GetAll returns all stored values for the request as a map. --- context.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/context.go b/context.go index 12accb1..4547242 100644 --- a/context.go +++ b/context.go @@ -48,6 +48,16 @@ func GetOk(r *http.Request, key interface{}) (interface{}, bool) { return nil, false } +// GetAll returns all stored values for the request as a map. +func GetAll(r *http.Request) (map[interface{}]interface{}, bool) { + mutex.Lock() + defer mutex.Unlock() + if all, ok := data[r]; ok { + return all, ok + } + return nil, false +} + // Delete removes a value stored for a given key in a given request. func Delete(r *http.Request, key interface{}) { mutex.Lock() From fac5866e50388a49ba64cdbf9706d343514ad93b Mon Sep 17 00:00:00 2001 From: Pieter Joost van de Sande Date: Sat, 13 Jul 2013 21:04:36 +0200 Subject: [PATCH 15/33] Adds test for GetAll for existing request --- context_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/context_test.go b/context_test.go index ff9e2ad..b1b433b 100644 --- a/context_test.go +++ b/context_test.go @@ -51,6 +51,11 @@ func TestContext(t *testing.T) { assertEqual(value, nil) assertEqual(ok, true) + // GetAll() + values, ok := GetAll(r) + assertEqual(ok, true) + assertEqual(len(values), 3) + // Delete() Delete(r, key1) assertEqual(Get(r, key1), nil) From 6405b672ffe015cd83adf52c56bc95fab7854dce Mon Sep 17 00:00:00 2001 From: Pieter Joost van de Sande Date: Sat, 13 Jul 2013 21:06:50 +0200 Subject: [PATCH 16/33] Adds test for GetAll for empty request --- context_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/context_test.go b/context_test.go index b1b433b..817a749 100644 --- a/context_test.go +++ b/context_test.go @@ -24,6 +24,7 @@ func TestContext(t *testing.T) { } r, _ := http.NewRequest("GET", "http://localhost:8080/", nil) + emptyR, _ := http.NewRequest("GET", "http://localhost:8080/", nil) // Get() assertEqual(Get(r, key1), nil) @@ -56,6 +57,10 @@ func TestContext(t *testing.T) { assertEqual(ok, true) assertEqual(len(values), 3) + // GetAll() for empty request + values, ok = GetAll(emptyR) + assertEqual(ok, false) + // Delete() Delete(r, key1) assertEqual(Get(r, key1), nil) From 3e810b0ccc2a40ffaae9b422318df29732c84c2e Mon Sep 17 00:00:00 2001 From: Pieter Joost van de Sande Date: Sat, 13 Jul 2013 21:17:06 +0200 Subject: [PATCH 17/33] Return empty map when there are no values --- context.go | 9 ++++----- context_test.go | 7 +++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/context.go b/context.go index 4547242..095b16d 100644 --- a/context.go +++ b/context.go @@ -49,13 +49,12 @@ func GetOk(r *http.Request, key interface{}) (interface{}, bool) { } // GetAll returns all stored values for the request as a map. -func GetAll(r *http.Request) (map[interface{}]interface{}, bool) { +func GetAll(r *http.Request) map[interface{}]interface{} { mutex.Lock() defer mutex.Unlock() - if all, ok := data[r]; ok { - return all, ok - } - return nil, false + + context, _ := data[r] + return context } // Delete removes a value stored for a given key in a given request. diff --git a/context_test.go b/context_test.go index 817a749..ddbf3c7 100644 --- a/context_test.go +++ b/context_test.go @@ -53,13 +53,12 @@ func TestContext(t *testing.T) { assertEqual(ok, true) // GetAll() - values, ok := GetAll(r) - assertEqual(ok, true) + values := GetAll(r) assertEqual(len(values), 3) // GetAll() for empty request - values, ok = GetAll(emptyR) - assertEqual(ok, false) + values = GetAll(emptyR) + assertEqual(len(values), 0) // Delete() Delete(r, key1) From 77bafb62fdfb308442391933e5a0577f1f14d4f4 Mon Sep 17 00:00:00 2001 From: Pieter Joost van de Sande Date: Sun, 14 Jul 2013 10:51:35 +0200 Subject: [PATCH 18/33] Adds GetAllOk --- context.go | 10 ++++++++++ context_test.go | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/context.go b/context.go index 095b16d..8908d3f 100644 --- a/context.go +++ b/context.go @@ -57,6 +57,16 @@ func GetAll(r *http.Request) map[interface{}]interface{} { return context } +// GetAll returns all stored values for the request as a map. It returns not ok +// if the request was never registered. +func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) { + mutex.Lock() + defer mutex.Unlock() + + context, ok := data[r] + return context, ok +} + // Delete removes a value stored for a given key in a given request. func Delete(r *http.Request, key interface{}) { mutex.Lock() diff --git a/context_test.go b/context_test.go index ddbf3c7..9f05974 100644 --- a/context_test.go +++ b/context_test.go @@ -60,6 +60,16 @@ func TestContext(t *testing.T) { values = GetAll(emptyR) assertEqual(len(values), 0) + // GetAllOk() + values, ok = GetAllOk(r) + assertEqual(ok, true) + assertEqual(len(values), 3) + + // GetAllOk() for empty request + values, ok = GetAllOk(emptyR) + assertEqual(ok, false) + assertEqual(len(values), 0) + // Delete() Delete(r, key1) assertEqual(Get(r, key1), nil) From 08e2e8a9bc63e56764888f6519b58c2485b13b47 Mon Sep 17 00:00:00 2001 From: Pieter Joost van de Sande Date: Mon, 15 Jul 2013 17:28:51 +0200 Subject: [PATCH 19/33] Remove GetAllOk method and GetAll now returns nil for invalid request --- context.go | 10 ++++++---- context_test.go | 16 ++++------------ 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/context.go b/context.go index 8908d3f..f6f0117 100644 --- a/context.go +++ b/context.go @@ -48,13 +48,15 @@ func GetOk(r *http.Request, key interface{}) (interface{}, bool) { return nil, false } -// GetAll returns all stored values for the request as a map. -func GetAll(r *http.Request) map[interface{}]interface{} { +// 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.Lock() defer mutex.Unlock() - context, _ := data[r] - return context + if context, ok := data[r]; ok { + return &context + } + return nil } // GetAll returns all stored values for the request as a map. It returns not ok diff --git a/context_test.go b/context_test.go index 9f05974..0742c81 100644 --- a/context_test.go +++ b/context_test.go @@ -54,21 +54,13 @@ func TestContext(t *testing.T) { // GetAll() values := GetAll(r) - assertEqual(len(values), 3) + assertEqual(len(*values), 3) // GetAll() for empty request values = GetAll(emptyR) - assertEqual(len(values), 0) - - // GetAllOk() - values, ok = GetAllOk(r) - assertEqual(ok, true) - assertEqual(len(values), 3) - - // GetAllOk() for empty request - values, ok = GetAllOk(emptyR) - assertEqual(ok, false) - assertEqual(len(values), 0) + if values != nil { + t.Error("GetAll didn't return nil value for invalid request") + } // Delete() Delete(r, key1) From b680aaae68ec431913ded5e13f81c7929acc6615 Mon Sep 17 00:00:00 2001 From: Pieter Joost van de Sande Date: Mon, 15 Jul 2013 19:29:56 +0200 Subject: [PATCH 20/33] Return map instead of pointer value As kisielk just [told me](https://github.com/pjvds/context/commit/709e005f84167f4206737b8ba6c560d1ebb18b85#commitcomment-3636470), maps can be nil --- context.go | 4 ++-- context_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/context.go b/context.go index f6f0117..e5374db 100644 --- a/context.go +++ b/context.go @@ -49,12 +49,12 @@ func GetOk(r *http.Request, key interface{}) (interface{}, bool) { } // 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{} { +func GetAll(r *http.Request) map[interface{}]interface{} { mutex.Lock() defer mutex.Unlock() if context, ok := data[r]; ok { - return &context + return context } return nil } diff --git a/context_test.go b/context_test.go index 0742c81..68f25e9 100644 --- a/context_test.go +++ b/context_test.go @@ -54,7 +54,7 @@ func TestContext(t *testing.T) { // GetAll() values := GetAll(r) - assertEqual(len(*values), 3) + assertEqual(len(values), 3) // GetAll() for empty request values = GetAll(emptyR) From 8b602e76415dbfe4d6ef80cdbc49aaecc7155be4 Mon Sep 17 00:00:00 2001 From: Adam Mckaig Date: Thu, 6 Mar 2014 12:27:42 -0500 Subject: [PATCH 21/33] Fix docstring --- context.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/context.go b/context.go index e5374db..bcc3faa 100644 --- a/context.go +++ b/context.go @@ -59,8 +59,8 @@ func GetAll(r *http.Request) map[interface{}]interface{} { return nil } -// GetAll returns all stored values for the request as a map. It returns not ok -// if the request was never registered. +// GetAllOk returns all stored values for the request as a map. It returns not +// ok if the request was never registered. func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) { mutex.Lock() defer mutex.Unlock() From 116fc839c218f1d5177797304d62acb8dcaa9e52 Mon Sep 17 00:00:00 2001 From: Adam Mckaig Date: Thu, 6 Mar 2014 12:31:25 -0500 Subject: [PATCH 22/33] Add tests for GetAllOk --- context_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/context_test.go b/context_test.go index 68f25e9..e1dca3b 100644 --- a/context_test.go +++ b/context_test.go @@ -62,6 +62,16 @@ func TestContext(t *testing.T) { 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) From cffbf44e851f1a2068ae6273eb8795031c3b6fad Mon Sep 17 00:00:00 2001 From: Tyler Bunnell Date: Mon, 19 May 2014 20:55:34 -0600 Subject: [PATCH 23/33] Add mutex benchmark --- context_test.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/context_test.go b/context_test.go index e1dca3b..36975d7 100644 --- a/context_test.go +++ b/context_test.go @@ -85,3 +85,52 @@ func TestContext(t *testing.T) { 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) { + + b.StopTimer() + r, _ := http.NewRequest("GET", "http://localhost:8080/", nil) + done := make(chan struct{}) + numWriters := 64 + numReaders := numWriters * 8 + iterations := 128 + 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 + } + + } + +} From dad32f953222ceaeacbcb3f9354e397118a679b1 Mon Sep 17 00:00:00 2001 From: Tyler Bunnell Date: Mon, 19 May 2014 21:00:24 -0600 Subject: [PATCH 24/33] Switch to RWMutex --- context.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/context.go b/context.go index bcc3faa..3c55c5f 100644 --- a/context.go +++ b/context.go @@ -11,7 +11,7 @@ import ( ) var ( - mutex sync.Mutex + mutex sync.RWMutex data = make(map[*http.Request]map[interface{}]interface{}) datat = make(map[*http.Request]int64) ) @@ -29,8 +29,8 @@ func Set(r *http.Request, key, val interface{}) { // Get returns a value stored for a given key in a given request. func Get(r *http.Request, key interface{}) interface{} { - mutex.Lock() - defer mutex.Unlock() + mutex.RLock() + defer mutex.RUnlock() if data[r] != nil { return data[r][key] } @@ -39,8 +39,8 @@ func Get(r *http.Request, key interface{}) interface{} { // GetOk returns stored value and presence state like multi-value return of map access. func GetOk(r *http.Request, key interface{}) (interface{}, bool) { - mutex.Lock() - defer mutex.Unlock() + mutex.RLock() + defer mutex.RUnlock() if _, ok := data[r]; ok { value, ok := data[r][key] return value, ok @@ -50,8 +50,8 @@ func GetOk(r *http.Request, key interface{}) (interface{}, bool) { // 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.Lock() - defer mutex.Unlock() + mutex.RLock() + defer mutex.RUnlock() if context, ok := data[r]; ok { return context @@ -62,8 +62,8 @@ func GetAll(r *http.Request) map[interface{}]interface{} { // GetAllOk returns all stored values for the request as a map. It returns not // ok if the request was never registered. func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) { - mutex.Lock() - defer mutex.Unlock() + mutex.RLock() + defer mutex.RUnlock() context, ok := data[r] return context, ok From 6e5f0a5765dc2423d064383f33ccb9306267deaf Mon Sep 17 00:00:00 2001 From: Tyler Bunnell Date: Mon, 19 May 2014 21:02:29 -0600 Subject: [PATCH 25/33] Remove defer defer has a significant overhead. --- context.go | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/context.go b/context.go index 3c55c5f..a7f7d85 100644 --- a/context.go +++ b/context.go @@ -19,43 +19,45 @@ var ( // 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 + 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() - defer mutex.RUnlock() if data[r] != nil { + mutex.RUnlock() return data[r][key] } + 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() - defer mutex.RUnlock() 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() - defer mutex.RUnlock() - if context, ok := data[r]; ok { + mutex.RUnlock() return context } + mutex.RUnlock() return nil } @@ -63,19 +65,18 @@ func GetAll(r *http.Request) map[interface{}]interface{} { // ok if the request was never registered. func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) { mutex.RLock() - defer mutex.RUnlock() - context, ok := data[r] + mutex.RUnlock() return context, ok } // Delete removes a value stored for a given key in a given request. func Delete(r *http.Request, key interface{}) { mutex.Lock() - defer mutex.Unlock() if data[r] != nil { delete(data[r], key) } + mutex.Unlock() } // Clear removes all values stored for a given request. @@ -84,8 +85,8 @@ func Delete(r *http.Request, key interface{}) { // variables at the end of a request lifetime. See ClearHandler(). func Clear(r *http.Request) { mutex.Lock() - defer mutex.Unlock() clear(r) + mutex.Unlock() } // clear is Clear without the lock. @@ -105,7 +106,6 @@ func clear(r *http.Request) { // periodically until the problem is fixed. func Purge(maxAge int) int { mutex.Lock() - defer mutex.Unlock() count := 0 if maxAge <= 0 { count = len(data) @@ -120,6 +120,7 @@ func Purge(maxAge int) int { } } } + mutex.Unlock() return count } From ee79a2c21bb78dd6d095137ddea5862ff15439e5 Mon Sep 17 00:00:00 2001 From: Tyler Bunnell Date: Mon, 19 May 2014 21:04:23 -0600 Subject: [PATCH 26/33] Fix golint warning --- doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc.go b/doc.go index 2976064..73c7400 100644 --- a/doc.go +++ b/doc.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. /* -Package gorilla/context stores values shared during a request lifetime. +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 From 13ce75261f2d4eda931f3de7b8c14dc7a56843a3 Mon Sep 17 00:00:00 2001 From: Tyler Bunnell Date: Tue, 20 May 2014 08:28:38 -0600 Subject: [PATCH 27/33] More comprehensive benchmarking --- context_test.go | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/context_test.go b/context_test.go index 36975d7..6ada8ec 100644 --- a/context_test.go +++ b/context_test.go @@ -104,14 +104,11 @@ func parallelWriter(r *http.Request, key, value string, iterations int, wait, do } -func BenchmarkMutex(b *testing.B) { +func benchmarkMutex(b *testing.B, numReaders, numWriters, iterations int) { b.StopTimer() r, _ := http.NewRequest("GET", "http://localhost:8080/", nil) done := make(chan struct{}) - numWriters := 64 - numReaders := numWriters * 8 - iterations := 128 b.StartTimer() for i := 0; i < b.N; i++ { @@ -134,3 +131,31 @@ func BenchmarkMutex(b *testing.B) { } } + +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) +} From 1f3e8a46c5bb4c29971d9867ede6e198ca5eddce Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Thu, 22 May 2014 15:03:43 -0700 Subject: [PATCH 28/33] Return a shallow copy of the result in GetAll and GetAllOk. Fixes #13 --- context.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/context.go b/context.go index a7f7d85..2a7357f 100644 --- a/context.go +++ b/context.go @@ -54,20 +54,28 @@ func GetOk(r *http.Request, key interface{}) (interface{}, bool) { 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 context + return result } mutex.RUnlock() return nil } -// GetAllOk returns all stored values for the request as a map. It returns not -// ok if the request was never registered. +// 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 context, ok + return result, ok } // Delete removes a value stored for a given key in a given request. From 9af56363a72d2e9f29d444682583074e4d05a213 Mon Sep 17 00:00:00 2001 From: Alexandr Morozov Date: Wed, 4 Jun 2014 18:45:55 +0400 Subject: [PATCH 29/33] Fix racy code in Get --- context.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/context.go b/context.go index 2a7357f..81cb128 100644 --- a/context.go +++ b/context.go @@ -30,9 +30,10 @@ func Set(r *http.Request, key, val interface{}) { // Get returns a value stored for a given key in a given request. func Get(r *http.Request, key interface{}) interface{} { mutex.RLock() - if data[r] != nil { + if ctx := data[r]; ctx != nil { + value := ctx[key] mutex.RUnlock() - return data[r][key] + return value } mutex.RUnlock() return nil From 50c25fb3b2b3b3cc724e9b6ac75fb44b3bccd0da Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Wed, 26 Nov 2014 08:33:37 -0800 Subject: [PATCH 30/33] benchmark: fix parallelWriter. Fixes #18 --- context_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context_test.go b/context_test.go index 6ada8ec..9814c50 100644 --- a/context_test.go +++ b/context_test.go @@ -98,7 +98,7 @@ func parallelReader(r *http.Request, key string, iterations int, wait, done chan func parallelWriter(r *http.Request, key, value string, iterations int, wait, done chan struct{}) { <-wait for i := 0; i < iterations; i++ { - Get(r, key) + Set(r, key, value) } done <- struct{}{} From ecbca1cc2472d276035546fbff15710a7144d421 Mon Sep 17 00:00:00 2001 From: areski Date: Wed, 17 Dec 2014 13:41:42 +0100 Subject: [PATCH 31/33] Add Travis-ci tests build on Go version 1.3 / 1.4 --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index d87d465..6796581 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,4 +4,6 @@ go: - 1.0 - 1.1 - 1.2 + - 1.3 + - 1.4 - tip From 1c83b3eabd45b6d76072b66b746c20815fb2872d Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Wed, 19 Aug 2015 22:12:45 -0700 Subject: [PATCH 32/33] Update .travis.yml --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6796581..f983b60 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,8 @@ language: go +sudo: false go: - - 1.0 - - 1.1 - - 1.2 - 1.3 - 1.4 + - 1.5 - tip From b53bb424a1b622348fb7d129fa8c11f303a2256c Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Fri, 26 Feb 2016 13:28:16 -0800 Subject: [PATCH 33/33] Update .travis.yml to build Go 1.6 --- .travis.yml | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index f983b60..faca4da 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,19 @@ language: go sudo: false -go: - - 1.3 - - 1.4 - - 1.5 - - tip +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 ./...