summaryrefslogtreecommitdiffstats
path: root/vendor/gopkg.in
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2016-09-23 10:17:51 -0400
committerGitHub <noreply@github.com>2016-09-23 10:17:51 -0400
commit2ca0e8f9a0f9863555a26e984cde15efff9ef8f8 (patch)
treedaae1ee67b14a3d0a84424f2a304885d9e75ce2b /vendor/gopkg.in
parent6d62d65b2dc85855aabea036cbd44f6059e19d13 (diff)
downloadchat-2ca0e8f9a0f9863555a26e984cde15efff9ef8f8.tar.gz
chat-2ca0e8f9a0f9863555a26e984cde15efff9ef8f8.tar.bz2
chat-2ca0e8f9a0f9863555a26e984cde15efff9ef8f8.zip
Updating golang dependancies (#4075)
Diffstat (limited to 'vendor/gopkg.in')
-rw-r--r--vendor/gopkg.in/fsnotify.v1/AUTHORS1
-rw-r--r--vendor/gopkg.in/fsnotify.v1/CHANGELOG.md4
-rw-r--r--vendor/gopkg.in/fsnotify.v1/windows.go8
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/.gitignore10
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/.travis.yml10
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/README.md82
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/common_test.go65
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/delayer.go109
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/delayer_test.go65
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/doc.go77
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/examples/README.md12
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/examples/custom/main.go90
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/examples/interval-many/main.go79
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/examples/interval-vary/main.go74
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/examples/interval-vary/siege-urls4
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/examples/interval/main.go69
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/examples/memstats/main.go97
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/examples/memstats/test-filebin65536 -> 0 bytes
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/examples/rate-limit/main.go101
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/interval.go164
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/interval_test.go114
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/memstats.go214
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/memstats_test.go64
-rwxr-xr-xvendor/gopkg.in/throttled/throttled.v1/misc/pre-commit38
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/rate.go116
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/rate_test.go101
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/store.go31
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/store/doc.go2
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/store/mem.go90
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/store/mem_test.go43
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/store/redis.go85
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/store/redis_test.go66
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/throttler.go86
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/.gitignore5
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/.travis.yml24
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/LICENSE (renamed from vendor/gopkg.in/throttled/throttled.v1/LICENSE)0
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/Makefile31
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/README.md85
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/deprecated.go73
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/deprecated_test.go59
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/doc.go3
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/example_test.go103
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/http.go110
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/http_test.go99
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/rate.go239
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/rate_test.go128
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/store.go34
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/store/deprecated.go32
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/store/memstore/memstore.go127
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/store/memstore/memstore_test.go40
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/store/redigostore/redigostore.go156
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/store/redigostore/redisstore_test.go85
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/store/storetest/doc.go2
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/store/storetest/storetest.go176
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/varyby.go (renamed from vendor/gopkg.in/throttled/throttled.v1/varyby.go)2
-rw-r--r--vendor/gopkg.in/throttled/throttled.v2/varyby_test.go (renamed from vendor/gopkg.in/throttled/throttled.v1/varyby_test.go)18
-rw-r--r--vendor/gopkg.in/yaml.v2/LICENSE195
-rw-r--r--vendor/gopkg.in/yaml.v2/README.md2
58 files changed, 1642 insertions, 2357 deletions
diff --git a/vendor/gopkg.in/fsnotify.v1/AUTHORS b/vendor/gopkg.in/fsnotify.v1/AUTHORS
index 6438bb31c..71c47ce89 100644
--- a/vendor/gopkg.in/fsnotify.v1/AUTHORS
+++ b/vendor/gopkg.in/fsnotify.v1/AUTHORS
@@ -11,6 +11,7 @@
Adrien Bustany <adrien@bustany.org>
Amit Krishnan <amit.krishnan@oracle.com>
Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
+Bruno Bigras <bigras.bruno@gmail.com>
Caleb Spare <cespare@gmail.com>
Case Nelson <case@teammating.com>
Chris Howey <chris@howey.me> <howeyc@gmail.com>
diff --git a/vendor/gopkg.in/fsnotify.v1/CHANGELOG.md b/vendor/gopkg.in/fsnotify.v1/CHANGELOG.md
index 675fab9eb..f6c7c485c 100644
--- a/vendor/gopkg.in/fsnotify.v1/CHANGELOG.md
+++ b/vendor/gopkg.in/fsnotify.v1/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## v1.3.1 / 2016-06-28
+
+* windows: fix for double backslash when watching the root of a drive [#151](https://github.com/fsnotify/fsnotify/issues/151) (thanks @brunoqc)
+
## v1.3.0 / 2016-04-19
* Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135)
diff --git a/vendor/gopkg.in/fsnotify.v1/windows.go b/vendor/gopkg.in/fsnotify.v1/windows.go
index c836bdb3d..09436f31d 100644
--- a/vendor/gopkg.in/fsnotify.v1/windows.go
+++ b/vendor/gopkg.in/fsnotify.v1/windows.go
@@ -306,7 +306,7 @@ func (w *Watcher) remWatch(pathname string) error {
watch.mask = 0
} else {
name := filepath.Base(pathname)
- w.sendEvent(watch.path+"\\"+name, watch.names[name]&sysFSIGNORED)
+ w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED)
delete(watch.names, name)
}
return w.startRead(watch)
@@ -316,7 +316,7 @@ func (w *Watcher) remWatch(pathname string) error {
func (w *Watcher) deleteWatch(watch *watch) {
for name, mask := range watch.names {
if mask&provisional == 0 {
- w.sendEvent(watch.path+"\\"+name, mask&sysFSIGNORED)
+ w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED)
}
delete(watch.names, name)
}
@@ -453,7 +453,7 @@ func (w *Watcher) readEvents() {
raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName))
name := syscall.UTF16ToString(buf[:raw.FileNameLength/2])
- fullname := watch.path + "\\" + name
+ fullname := filepath.Join(watch.path, name)
var mask uint64
switch raw.Action {
@@ -491,7 +491,7 @@ func (w *Watcher) readEvents() {
}
}
if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME {
- fullname = watch.path + "\\" + watch.rename
+ fullname = filepath.Join(watch.path, watch.rename)
sendNameEvent()
}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/.gitignore b/vendor/gopkg.in/throttled/throttled.v1/.gitignore
deleted file mode 100644
index c2a6499b4..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-.DS_Store
-*.swp
-*.swo
-*.test
-examples/interval/interval
-examples/interval-vary/interval-vary
-examples/interval-many/interval-many
-examples/memstats/memstats
-examples/rate-limit/rate-limit
-examples/custom/custom
diff --git a/vendor/gopkg.in/throttled/throttled.v1/.travis.yml b/vendor/gopkg.in/throttled/throttled.v1/.travis.yml
deleted file mode 100644
index 1b2427202..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/.travis.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-sudo: false
-language: go
-
-go:
- - 1.2
- - tip
-
-install: go get -t ./...
-
-script: go test -v -short ./...
diff --git a/vendor/gopkg.in/throttled/throttled.v1/README.md b/vendor/gopkg.in/throttled/throttled.v1/README.md
deleted file mode 100644
index fbb4a01fc..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/README.md
+++ /dev/null
@@ -1,82 +0,0 @@
-# Throttled [![build status](https://secure.travis-ci.org/throttled/throttled.png)](http://travis-ci.org/throttled/throttled) [![GoDoc](https://godoc.org/gopkg.in/throttled/throttled.v1?status.png)](http://godoc.org/gopkg.in/throttled/throttled.v1)
-
-Package throttled implements different throttling strategies for controlling
-access to HTTP handlers.
-
-*As of July 27, 2015, the package is now located under its own GitHub
- organization and uses gopkg.in for versioning, please adjust your
- imports to `gopkg.in/throttled/throttled.v1`.*
-
-## Installation
-
-`go get gopkg.in/throttled/throttled.v1/...`
-
-## Interval
-
-The Interval function creates a throttler that allows requests to go through at
-a controlled, constant interval. The interval may be applied to all requests
-(vary argument == nil) or independently based on vary-by criteria.
-
-For example:
-
- th := throttled.Interval(throttled.PerSec(10), 100, &throttled.VaryBy{Path: true}, 50)
- h := th.Throttle(myHandler)
- http.ListenAndServe(":9000", h)
-
-Creates a throttler that will allow a request each 100ms (10 requests per second), with
-a buffer of 100 exceeding requests before dropping requests with a status code 429 (by
-default, configurable using th.DeniedHandler or the package-global DefaultDeniedHandler
-variable). Different paths will be throttled independently, so that /path_a and /path_b
-both can serve 10 requests per second. The last argument, 50, indicates the maximum number
-of keys that the throttler will keep in memory.
-
-## MemStats
-
-The MemStats function creates a throttler that allows requests to go through only if
-the memory statistics of the current process are below specified thresholds.
-
-For example:
-
- th := throttled.MemStats(throttled.MemThresholds(&runtime.MemStats{NumGC: 10}, 10*time.Millisecond)
- h := th.Throttle(myHandler)
- http.ListenAndServe(":9000", h)
-
-Creates a throttler that will allow requests to go through until the number of garbage
-collections reaches the initial number + 10 (the MemThresholds function creates absolute
-memory stats thresholds from offsets). The second argument, 10ms, indicates the refresh
-rate of the memory stats.
-
-## RateLimit
-
-The RateLimit function creates a throttler that allows a certain number of requests in
-a given time window, as is often implemented in public RESTful APIs.
-
-For example:
-
- th := throttled.RateLimit(throttled.PerMin(30), &throttled.VaryBy{RemoteAddr: true}, store.NewMemStore(1000))
- h := th.Throttle(myHandler)
- http.ListenAndServe(":9000", h)
-
-Creates a throttler that will limit requests to 30 per minute, based on the remote address
-of the client, and will store the counter and remaining time of the current window in the
-provided memory store, limiting the number of keys to keep in memory to 1000. The store
-sub-package also provides a Redis-based Store implementations.
-
-The RateLimit throttler sets the expected X-RateLimit-* headers on the response, and
-also sets a Retry-After header when the limit is exceeded.
-
-## Documentation
-
-The API documentation is available as usual on [godoc.org][doc].
-
-There is also a [blog post explaining the package's usage on 0value.com][blog].
-
-Finally, many examples are provided in the /examples sub-folder of the repository.
-
-## License
-
-The [BSD 3-clause license][bsd]. Copyright (c) 2014 Martin Angers and Contributors.
-
-[doc]: http://godoc.org/gopkg.in/throttled/throttled.v1
-[blog]: http://0value.com/throttled--guardian-of-the-web-server
-[bsd]: http://opensource.org/licenses/BSD-3-Clause
diff --git a/vendor/gopkg.in/throttled/throttled.v1/common_test.go b/vendor/gopkg.in/throttled/throttled.v1/common_test.go
deleted file mode 100644
index ddb57fb1c..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/common_test.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package throttled
-
-import (
- "fmt"
- "net/http"
- "net/http/httptest"
- "sync"
- "time"
-
- "github.com/PuerkitoBio/boom/commands"
-)
-
-type stats struct {
- sync.Mutex
- ok int
- dropped int
- ts []time.Time
-
- body func()
-}
-
-func (s *stats) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- if s.body != nil {
- s.body()
- }
- s.Lock()
- defer s.Unlock()
- s.ts = append(s.ts, time.Now())
- s.ok++
- w.WriteHeader(200)
-}
-
-func (s *stats) DeniedHTTP(w http.ResponseWriter, r *http.Request) {
- s.Lock()
- defer s.Unlock()
- s.dropped++
- w.WriteHeader(deniedStatus)
-}
-
-func (s *stats) Stats() (int, int, []time.Time) {
- s.Lock()
- defer s.Unlock()
- return s.ok, s.dropped, s.ts
-}
-
-func runTest(h http.Handler, b ...commands.Boom) []*commands.Report {
- srv := httptest.NewServer(h)
- defer srv.Close()
-
- var rpts []*commands.Report
- var wg sync.WaitGroup
- var mu sync.Mutex
- wg.Add(len(b))
- for i, bo := range b {
- bo.Req.Url = srv.URL + fmt.Sprintf("/%d", i)
- go func(bo commands.Boom) {
- mu.Lock()
- defer mu.Unlock()
- rpts = append(rpts, bo.Run())
- wg.Done()
- }(bo)
- }
- wg.Wait()
- return rpts
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/delayer.go b/vendor/gopkg.in/throttled/throttled.v1/delayer.go
deleted file mode 100644
index e62ec9e86..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/delayer.go
+++ /dev/null
@@ -1,109 +0,0 @@
-package throttled
-
-import "time"
-
-// The Quota interface defines the method to implement to describe
-// a time-window quota, as required by the RateLimit throttler.
-type Quota interface {
- // Quota returns a number of requests allowed, and a duration.
- Quota() (int, time.Duration)
-}
-
-// The Delayer interface defines the method to implement to describe
-// a delay as required by the Interval throttler.
-type Delayer interface {
- // Delay returns a duration.
- Delay() time.Duration
-}
-
-// PerSec represents a number of requests per second.
-type PerSec int
-
-// Delay returns the duration to wait before the next request can go through,
-// so that PerSec(n) == n requests per second at regular intervals.
-func (ps PerSec) Delay() time.Duration {
- if ps <= 0 {
- return 0
- }
- return time.Duration(1.0 / float64(ps) * float64(time.Second))
-}
-
-// Quota returns the number of requests allowed in a 1 second time window,
-// so that PerSec(n) == n requests allowed per second.
-func (ps PerSec) Quota() (int, time.Duration) {
- return int(ps), time.Second
-}
-
-// PerMin represents a number of requests per minute.
-type PerMin int
-
-// Delay returns the duration to wait before the next request can go through,
-// so that PerMin(n) == n requests per minute at regular intervals.
-func (pm PerMin) Delay() time.Duration {
- if pm <= 0 {
- return 0
- }
- return time.Duration(1.0 / float64(pm) * float64(time.Minute))
-}
-
-// Quota returns the number of requests allowed in a 1 minute time window,
-// so that PerMin(n) == n requests allowed per minute.
-func (pm PerMin) Quota() (int, time.Duration) {
- return int(pm), time.Minute
-}
-
-// PerHour represents a number of requests per hour.
-type PerHour int
-
-// Delay returns the duration to wait before the next request can go through,
-// so that PerHour(n) == n requests per hour at regular intervals.
-func (ph PerHour) Delay() time.Duration {
- if ph <= 0 {
- return 0
- }
- return time.Duration(1.0 / float64(ph) * float64(time.Hour))
-}
-
-// Quota returns the number of requests allowed in a 1 hour time window,
-// so that PerHour(n) == n requests allowed per hour.
-func (ph PerHour) Quota() (int, time.Duration) {
- return int(ph), time.Hour
-}
-
-// PerDay represents a number of requests per day.
-type PerDay int
-
-// Delay returns the duration to wait before the next request can go through,
-// so that PerDay(n) == n requests per day at regular intervals.
-func (pd PerDay) Delay() time.Duration {
- if pd <= 0 {
- return 0
- }
- return time.Duration(1.0 / float64(pd) * float64(24*time.Hour))
-}
-
-// Quota returns the number of requests allowed in a 1 day time window,
-// so that PerDay(n) == n requests allowed per day.
-func (pd PerDay) Quota() (int, time.Duration) {
- return int(pd), 24 * time.Hour
-}
-
-// D represents a custom delay.
-type D time.Duration
-
-// Delay returns the duration to wait before the next request can go through,
-// which is the custom duration represented by the D value.
-func (d D) Delay() time.Duration {
- return time.Duration(d)
-}
-
-// Q represents a custom quota.
-type Q struct {
- Requests int
- Window time.Duration
-}
-
-// Quota returns the number of requests allowed and the custom time window.
-func (q Q) Quota() (int, time.Duration) {
- return q.Requests, q.Window
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/delayer_test.go b/vendor/gopkg.in/throttled/throttled.v1/delayer_test.go
deleted file mode 100644
index 822978e5d..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/delayer_test.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package throttled
-
-import (
- "testing"
- "time"
-)
-
-func TestDelayer(t *testing.T) {
- cases := []struct {
- in Delayer
- out time.Duration
- }{
- 0: {PerSec(1), time.Second},
- 1: {PerSec(2), 500 * time.Millisecond},
- 2: {PerSec(4), 250 * time.Millisecond},
- 3: {PerSec(5), 200 * time.Millisecond},
- 4: {PerSec(10), 100 * time.Millisecond},
- 5: {PerSec(100), 10 * time.Millisecond},
- 6: {PerSec(3), 333333333 * time.Nanosecond},
- 7: {PerMin(1), time.Minute},
- 8: {PerMin(2), 30 * time.Second},
- 9: {PerMin(4), 15 * time.Second},
- 10: {PerMin(5), 12 * time.Second},
- 11: {PerMin(10), 6 * time.Second},
- 12: {PerMin(60), time.Second},
- 13: {PerHour(1), time.Hour},
- 14: {PerHour(2), 30 * time.Minute},
- 15: {PerHour(4), 15 * time.Minute},
- 16: {PerHour(60), time.Minute},
- 17: {PerHour(120), 30 * time.Second},
- 18: {D(time.Second), time.Second},
- 19: {D(5 * time.Minute), 5 * time.Minute},
- 20: {PerSec(200), 5 * time.Millisecond},
- 21: {PerDay(24), time.Hour},
- }
- for i, c := range cases {
- got := c.in.Delay()
- if got != c.out {
- t.Errorf("%d: expected %s, got %s", i, c.out, got)
- }
- }
-}
-
-func TestQuota(t *testing.T) {
- cases := []struct {
- q Quota
- reqs int
- win time.Duration
- }{
- 0: {PerSec(10), 10, time.Second},
- 1: {PerMin(30), 30, time.Minute},
- 2: {PerHour(124), 124, time.Hour},
- 3: {PerDay(1), 1, 24 * time.Hour},
- 4: {Q{148, 17 * time.Second}, 148, 17 * time.Second},
- }
- for i, c := range cases {
- r, w := c.q.Quota()
- if r != c.reqs {
- t.Errorf("%d: expected %d requests, got %d", i, c.reqs, r)
- }
- if w != c.win {
- t.Errorf("%d: expected %s window, got %s", i, c.win, w)
- }
- }
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/doc.go b/vendor/gopkg.in/throttled/throttled.v1/doc.go
deleted file mode 100644
index a2c8d4c75..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/doc.go
+++ /dev/null
@@ -1,77 +0,0 @@
-// Package throttled implements different throttling strategies for controlling
-// access to HTTP handlers.
-//
-// Installation
-//
-// go get gopkg.in/throttled/throttled.v1/...
-//
-// Inverval
-//
-// The Interval function creates a throttler that allows requests to go through at
-// a controlled, constant interval. The interval may be applied to all requests
-// (vary argument == nil) or independently based on vary-by criteria.
-//
-// For example:
-//
-// th := throttled.Interval(throttled.PerSec(10), 100, &throttled.VaryBy{Path: true}, 50)
-// h := th.Throttle(myHandler)
-// http.ListenAndServe(":9000", h)
-//
-// Creates a throttler that will allow a request each 100ms (10 requests per second), with
-// a buffer of 100 exceeding requests before dropping requests with a status code 429 (by
-// default, configurable using th.DeniedHandler or the package-global DefaultDeniedHandler
-// variable). Different paths will be throttled independently, so that /path_a and /path_b
-// both can serve 10 requests per second. The last argument, 50, indicates the maximum number
-// of keys that the throttler will keep in memory.
-//
-// MemStats
-//
-// The MemStats function creates a throttler that allows requests to go through only if
-// the memory statistics of the current process are below specified thresholds.
-//
-// For example:
-//
-// th := throttled.MemStats(throttled.MemThresholds(&runtime.MemStats{NumGC: 10}, 10*time.Millisecond)
-// h := th.Throttle(myHandler)
-// http.ListenAndServe(":9000", h)
-//
-// Creates a throttler that will allow requests to go through until the number of garbage
-// collections reaches the initial number + 10 (the MemThresholds function creates absolute
-// memory stats thresholds from offsets). The second argument, 10ms, indicates the refresh
-// rate of the memory stats.
-//
-// RateLimit
-//
-// The RateLimit function creates a throttler that allows a certain number of requests in
-// a given time window, as is often implemented in public RESTful APIs.
-//
-// For example:
-//
-// th := throttled.RateLimit(throttled.PerMin(30), &throttled.VaryBy{RemoteAddr: true}, store.NewMemStore(1000))
-// h := th.Throttle(myHandler)
-// http.ListenAndServe(":9000", h)
-//
-// Creates a throttler that will limit requests to 30 per minute, based on the remote address
-// of the client, and will store the counter and remaining time of the current window in the
-// provided memory store, limiting the number of keys to keep in memory to 1000. The store
-// sub-package also provides a Redis-based Store implementations.
-//
-// The RateLimit throttler sets the expected X-RateLimit-* headers on the response, and
-// also sets a Retry-After header when the limit is exceeded.
-//
-// Documentation
-//
-// The API documentation is available as usual on godoc.org:
-// http://godoc.org/gopkg.in/throttled/throttled.v1
-//
-// There is also a blog post explaining the package's usage on 0value.com:
-// http://0value.com/throttled--guardian-of-the-web-server
-//
-// Finally, many examples are provided in the /examples sub-folder of the repository.
-//
-// License
-//
-// The BSD 3-clause license. Copyright (c) 2014 Martin Angers and Contributors.
-// http://opensource.org/licenses/BSD-3-Clause
-//
-package throttled // import "gopkg.in/throttled/throttled.v1"
diff --git a/vendor/gopkg.in/throttled/throttled.v1/examples/README.md b/vendor/gopkg.in/throttled/throttled.v1/examples/README.md
deleted file mode 100644
index 6b12dad20..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/examples/README.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# Examples
-
-This directory contains examples for all the throttlers implemented by the throttled package, as well as an example of a custom limiter.
-
-* custom/ : implements a custom limiter that allows requests to path /a on even seconds, and on path /b on odd seconds.
-* interval-many/ : implements a common interval throttler to control two different handlers, one for path /a and another for path /b, so that requests to any one of the handlers go through at the specified interval.
-* interval-vary/ : implements an interval throttler that varies by path, so that requests to each different path goes through at the specified interval.
-* interval/ : implements an interval throttler so that any request goes through at the specified interval, regardless of path or any other criteria.
-* memstats/ : implements a memory-usage throttler that limits access based on current memory statistics.
-* rate-limit/ : implements a rate-limiter throttler that varies by path, so that the number of requests allowed are counted based on the requested path.
-
-Each example app supports a number of command-line flags. Run the example with the -h flag to display usage and defaults.
diff --git a/vendor/gopkg.in/throttled/throttled.v1/examples/custom/main.go b/vendor/gopkg.in/throttled/throttled.v1/examples/custom/main.go
deleted file mode 100644
index b3fe993e8..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/examples/custom/main.go
+++ /dev/null
@@ -1,90 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
- "log"
- "math/rand"
- "net/http"
- "sync"
- "time"
-
- "gopkg.in/throttled/throttled.v1"
-)
-
-var (
- delayRes = flag.Duration("delay-response", 0, "delay the response by a random duration between 0 and this value")
- output = flag.String("output", "v", "type of output, one of `v`erbose, `q`uiet, `ok`-only, `ko`-only")
-)
-
-// Custom limiter: allow requests to the /a path on even seconds only, and
-// allow access to the /b path on odd seconds only.
-//
-// Yes this is absurd. A more realistic case could be to allow requests to some
-// contest page only during a limited time window.
-type customLimiter struct {
-}
-
-func (c *customLimiter) Start() {
- // No-op
-}
-
-func (c *customLimiter) Limit(w http.ResponseWriter, r *http.Request) (<-chan bool, error) {
- s := time.Now().Second()
- ch := make(chan bool, 1)
- ok := (r.URL.Path == "/a" && s%2 == 0) || (r.URL.Path == "/b" && s%2 != 0)
- ch <- ok
- if *output == "v" {
- log.Printf("Custom Limiter: Path=%s, Second=%d; ok? %v", r.URL.Path, s, ok)
- }
- return ch, nil
-}
-
-func main() {
- flag.Parse()
-
- var h http.Handler
- var ok, ko int
- var mu sync.Mutex
-
- // Keep the start time to print since-time
- start := time.Now()
- // Create the custom throttler using our custom limiter
- t := throttled.Custom(&customLimiter{})
- // Set its denied handler
- t.DeniedHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if *output == "v" || *output == "ko" {
- log.Printf("KO: %s", time.Since(start))
- }
- throttled.DefaultDeniedHandler.ServeHTTP(w, r)
- mu.Lock()
- defer mu.Unlock()
- ko++
- })
- // Throttle the OK handler
- rand.Seed(time.Now().Unix())
- h = t.Throttle(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if *output == "v" || *output == "ok" {
- log.Printf("ok: %s", time.Since(start))
- }
- if *delayRes > 0 {
- wait := time.Duration(rand.Intn(int(*delayRes)))
- time.Sleep(wait)
- }
- w.WriteHeader(200)
- mu.Lock()
- defer mu.Unlock()
- ok++
- }))
-
- // Print stats once in a while
- go func() {
- for _ = range time.Tick(10 * time.Second) {
- mu.Lock()
- log.Printf("ok: %d, ko: %d", ok, ko)
- mu.Unlock()
- }
- }()
- fmt.Println("server listening on port 9000")
- http.ListenAndServe(":9000", h)
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/examples/interval-many/main.go b/vendor/gopkg.in/throttled/throttled.v1/examples/interval-many/main.go
deleted file mode 100644
index 51a4ca023..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/examples/interval-many/main.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
- "log"
- "math/rand"
- "net/http"
- "sync"
- "time"
-
- "gopkg.in/throttled/throttled.v1"
-)
-
-var (
- delay = flag.Duration("delay", 200*time.Millisecond, "delay between calls")
- bursts = flag.Int("bursts", 10, "number of bursts allowed")
- delayRes = flag.Duration("delay-response", 0, "delay the response by a random duration between 0 and this value")
- output = flag.String("output", "v", "type of output, one of `v`erbose, `q`uiet, `ok`-only, `ko`-only")
-)
-
-func main() {
- flag.Parse()
-
- var ok, ko int
- var mu sync.Mutex
-
- // Keep start time to log since-time
- start := time.Now()
-
- // Create the interval throttle
- t := throttled.Interval(throttled.D(*delay), *bursts, nil, 0)
- // Set its denied handler
- t.DeniedHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if *output == "v" || *output == "ko" {
- log.Printf("%s: KO: %s", r.URL.Path, time.Since(start))
- }
- throttled.DefaultDeniedHandler.ServeHTTP(w, r)
- mu.Lock()
- defer mu.Unlock()
- ko++
- })
- // Create OK handlers
- rand.Seed(time.Now().Unix())
- makeHandler := func(ix int) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if *output == "v" || *output == "ok" {
- log.Printf("handler %d: %s: ok: %s", ix, r.URL.Path, time.Since(start))
- }
- if *delayRes > 0 {
- wait := time.Duration(rand.Intn(int(*delayRes)))
- time.Sleep(wait)
- }
- w.WriteHeader(200)
- mu.Lock()
- defer mu.Unlock()
- ok++
- })
- }
- // Throttle them using the same interval throttler
- h1 := t.Throttle(makeHandler(1))
- h2 := t.Throttle(makeHandler(2))
-
- // Handle two paths
- mux := http.NewServeMux()
- mux.Handle("/a", h1)
- mux.Handle("/b", h2)
-
- // Print stats once in a while
- go func() {
- for _ = range time.Tick(10 * time.Second) {
- mu.Lock()
- log.Printf("ok: %d, ko: %d", ok, ko)
- mu.Unlock()
- }
- }()
- fmt.Println("server listening on port 9000")
- http.ListenAndServe(":9000", mux)
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/examples/interval-vary/main.go b/vendor/gopkg.in/throttled/throttled.v1/examples/interval-vary/main.go
deleted file mode 100644
index f43cdc122..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/examples/interval-vary/main.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
- "log"
- "math/rand"
- "net/http"
- "sync"
- "time"
-
- "gopkg.in/throttled/throttled.v1"
-)
-
-var (
- delay = flag.Duration("delay", 200*time.Millisecond, "delay between calls")
- bursts = flag.Int("bursts", 10, "number of bursts allowed")
- maxkeys = flag.Int("max-keys", 1000, "maximum number of keys")
- delayRes = flag.Duration("delay-response", 0, "delay the response by a random duration between 0 and this value")
- output = flag.String("output", "v", "type of output, one of `v`erbose, `q`uiet, `ok`-only, `ko`-only")
-)
-
-func main() {
- flag.Parse()
-
- var h http.Handler
- var ok, ko int
- var mu sync.Mutex
-
- // Keep the start time to print since-time
- start := time.Now()
-
- // Create the interval throttler
- t := throttled.Interval(throttled.D(*delay), *bursts, &throttled.VaryBy{
- Path: true,
- }, *maxkeys)
- // Set the denied handler
- t.DeniedHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if *output == "v" || *output == "ko" {
- log.Printf("KO: %s", time.Since(start))
- }
- throttled.DefaultDeniedHandler.ServeHTTP(w, r)
- mu.Lock()
- defer mu.Unlock()
- ko++
- })
-
- // Throttle the OK handler
- rand.Seed(time.Now().Unix())
- h = t.Throttle(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if *output == "v" || *output == "ok" {
- log.Printf("%s: ok: %s", r.URL.Path, time.Since(start))
- }
- if *delayRes > 0 {
- wait := time.Duration(rand.Intn(int(*delayRes)))
- time.Sleep(wait)
- }
- w.WriteHeader(200)
- mu.Lock()
- defer mu.Unlock()
- ok++
- }))
-
- // Print stats once in a while
- go func() {
- for _ = range time.Tick(10 * time.Second) {
- mu.Lock()
- log.Printf("ok: %d, ko: %d", ok, ko)
- mu.Unlock()
- }
- }()
- fmt.Println("server listening on port 9000")
- http.ListenAndServe(":9000", h)
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/examples/interval-vary/siege-urls b/vendor/gopkg.in/throttled/throttled.v1/examples/interval-vary/siege-urls
deleted file mode 100644
index 9a2d0d312..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/examples/interval-vary/siege-urls
+++ /dev/null
@@ -1,4 +0,0 @@
-http://localhost:9000/a
-http://localhost:9000/b
-http://localhost:9000/c
-
diff --git a/vendor/gopkg.in/throttled/throttled.v1/examples/interval/main.go b/vendor/gopkg.in/throttled/throttled.v1/examples/interval/main.go
deleted file mode 100644
index ef8ee2cb8..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/examples/interval/main.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
- "log"
- "math/rand"
- "net/http"
- "sync"
- "time"
-
- "gopkg.in/throttled/throttled.v1"
-)
-
-var (
- delay = flag.Duration("delay", 200*time.Millisecond, "delay between calls")
- bursts = flag.Int("bursts", 10, "number of bursts allowed")
- delayRes = flag.Duration("delay-response", 0, "delay the response by a random duration between 0 and this value")
- output = flag.String("output", "v", "type of output, one of `v`erbose, `q`uiet, `ok`-only, `ko`-only")
-)
-
-func main() {
- flag.Parse()
-
- var h http.Handler
- var ok, ko int
- var mu sync.Mutex
-
- // Keep the start time to print since-time
- start := time.Now()
- // Create the interval throttler
- t := throttled.Interval(throttled.D(*delay), *bursts, nil, 0)
- // Set its denied handler
- t.DeniedHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if *output == "v" || *output == "ko" {
- log.Printf("KO: %s", time.Since(start))
- }
- throttled.DefaultDeniedHandler.ServeHTTP(w, r)
- mu.Lock()
- defer mu.Unlock()
- ko++
- })
- // Throttle the OK handler
- rand.Seed(time.Now().Unix())
- h = t.Throttle(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if *output == "v" || *output == "ok" {
- log.Printf("ok: %s", time.Since(start))
- }
- if *delayRes > 0 {
- wait := time.Duration(rand.Intn(int(*delayRes)))
- time.Sleep(wait)
- }
- w.WriteHeader(200)
- mu.Lock()
- defer mu.Unlock()
- ok++
- }))
-
- // Print stats once in a while
- go func() {
- for _ = range time.Tick(10 * time.Second) {
- mu.Lock()
- log.Printf("ok: %d, ko: %d", ok, ko)
- mu.Unlock()
- }
- }()
- fmt.Println("server listening on port 9000")
- http.ListenAndServe(":9000", h)
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/examples/memstats/main.go b/vendor/gopkg.in/throttled/throttled.v1/examples/memstats/main.go
deleted file mode 100644
index 50d4cc69b..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/examples/memstats/main.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
- "io/ioutil"
- "log"
- "math/rand"
- "net/http"
- "runtime"
- "sync"
- "time"
-
- "gopkg.in/throttled/throttled.v1"
-)
-
-var (
- numgc = flag.Int("gc", 0, "number of GC runs")
- mallocs = flag.Int("mallocs", 0, "number of mallocs")
- total = flag.Int("total", 0, "total number of bytes allocated")
- allocs = flag.Int("allocs", 0, "number of bytes allocated")
- refrate = flag.Duration("refresh", 0, "refresh rate of the memory stats")
- delayRes = flag.Duration("delay-response", 0, "delay the response by a random duration between 0 and this value")
- output = flag.String("output", "v", "type of output, one of `v`erbose, `q`uiet, `ok`-only, `ko`-only")
-)
-
-func main() {
- flag.Parse()
-
- var h http.Handler
- var ok, ko int
- var mu sync.Mutex
-
- // Keep the start time to print since-time
- start := time.Now()
- // Create the thresholds struct
- thresh := throttled.MemThresholds(&runtime.MemStats{
- NumGC: uint32(*numgc),
- Mallocs: uint64(*mallocs),
- TotalAlloc: uint64(*total),
- Alloc: uint64(*allocs),
- })
- if *output != "q" {
- log.Printf("thresholds: NumGC: %d, Mallocs: %d, Alloc: %dKb, Total: %dKb", thresh.NumGC, thresh.Mallocs, thresh.Alloc/1024, thresh.TotalAlloc/1024)
- }
- // Create the MemStats throttler
- t := throttled.MemStats(thresh, *refrate)
- // Set its denied handler
- t.DeniedHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if *output == "v" || *output == "ko" {
- log.Printf("KO: %s", time.Since(start))
- }
- throttled.DefaultDeniedHandler.ServeHTTP(w, r)
- mu.Lock()
- defer mu.Unlock()
- ko++
- })
-
- // Throttle the OK handler
- rand.Seed(time.Now().Unix())
- h = t.Throttle(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if *output == "v" || *output == "ok" {
- log.Printf("ok: %s", time.Since(start))
- }
- if *delayRes > 0 {
- wait := time.Duration(rand.Intn(int(*delayRes)))
- time.Sleep(wait)
- }
- // Read the whole file in memory, to actually use 64Kb (instead of streaming to w)
- b, err := ioutil.ReadFile("test-file")
- if err != nil {
- throttled.Error(w, r, err)
- return
- }
- _, err = w.Write(b)
- if err != nil {
- throttled.Error(w, r, err)
- }
- mu.Lock()
- defer mu.Unlock()
- ok++
- }))
-
- // Print stats once in a while
- go func() {
- var mem runtime.MemStats
- for _ = range time.Tick(10 * time.Second) {
- mu.Lock()
- runtime.ReadMemStats(&mem)
- log.Printf("ok: %d, ko: %d", ok, ko)
- log.Printf("TotalAllocs: %d Kb, Allocs: %d Kb, Mallocs: %d, NumGC: %d", mem.TotalAlloc/1024, mem.Alloc/1024, mem.Mallocs, mem.NumGC)
- mu.Unlock()
- }
- }()
- fmt.Println("server listening on port 9000")
- http.ListenAndServe(":9000", h)
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/examples/memstats/test-file b/vendor/gopkg.in/throttled/throttled.v1/examples/memstats/test-file
deleted file mode 100644
index c97c12f9b..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/examples/memstats/test-file
+++ /dev/null
Binary files differ
diff --git a/vendor/gopkg.in/throttled/throttled.v1/examples/rate-limit/main.go b/vendor/gopkg.in/throttled/throttled.v1/examples/rate-limit/main.go
deleted file mode 100644
index b00119f63..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/examples/rate-limit/main.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
- "log"
- "math/rand"
- "net/http"
- "sync"
- "time"
-
- "github.com/garyburd/redigo/redis"
- "gopkg.in/throttled/throttled.v1"
- "gopkg.in/throttled/throttled.v1/store"
-)
-
-var (
- requests = flag.Int("requests", 10, "number of requests allowed in the time window")
- window = flag.Duration("window", time.Minute, "time window for the limit of requests")
- storeType = flag.String("store", "mem", "store to use, one of `mem` or `redis` (on default localhost port)")
- delayRes = flag.Duration("delay-response", 0, "delay the response by a random duration between 0 and this value")
- output = flag.String("output", "v", "type of output, one of `v`erbose, `q`uiet, `ok`-only, `ko`-only")
-)
-
-func main() {
- flag.Parse()
-
- var h http.Handler
- var ok, ko int
- var mu sync.Mutex
- var st throttled.Store
-
- // Keep the start time to print since-time
- start := time.Now()
- // Create the rate-limit store
- switch *storeType {
- case "mem":
- st = store.NewMemStore(0)
- case "redis":
- st = store.NewRedisStore(setupRedis(), "throttled:", 0)
- default:
- log.Fatalf("unsupported store: %s", *storeType)
- }
- // Create the rate-limit throttler, varying on path
- t := throttled.RateLimit(throttled.Q{Requests: *requests, Window: *window}, &throttled.VaryBy{
- Path: true,
- }, st)
-
- // Set its denied handler
- t.DeniedHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if *output == "v" || *output == "ko" {
- log.Printf("KO: %s", time.Since(start))
- }
- throttled.DefaultDeniedHandler.ServeHTTP(w, r)
- mu.Lock()
- defer mu.Unlock()
- ko++
- })
-
- // Throttle the OK handler
- rand.Seed(time.Now().Unix())
- h = t.Throttle(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if *output == "v" || *output == "ok" {
- log.Printf("ok: %s", time.Since(start))
- }
- if *delayRes > 0 {
- wait := time.Duration(rand.Intn(int(*delayRes)))
- time.Sleep(wait)
- }
- w.WriteHeader(200)
- mu.Lock()
- defer mu.Unlock()
- ok++
- }))
-
- // Print stats once in a while
- go func() {
- for _ = range time.Tick(10 * time.Second) {
- mu.Lock()
- log.Printf("ok: %d, ko: %d", ok, ko)
- mu.Unlock()
- }
- }()
- fmt.Println("server listening on port 9000")
- http.ListenAndServe(":9000", h)
-}
-
-func setupRedis() *redis.Pool {
- pool := &redis.Pool{
- MaxIdle: 3,
- IdleTimeout: 30 * time.Second,
- Dial: func() (redis.Conn, error) {
- return redis.Dial("tcp", ":6379")
- },
- TestOnBorrow: func(c redis.Conn, t time.Time) error {
- _, err := c.Do("PING")
- return err
- },
- }
- return pool
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/interval.go b/vendor/gopkg.in/throttled/throttled.v1/interval.go
deleted file mode 100644
index 628a5593e..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/interval.go
+++ /dev/null
@@ -1,164 +0,0 @@
-package throttled
-
-import (
- "net/http"
- "sync"
- "time"
-
- "github.com/golang/groupcache/lru"
-)
-
-// Static check to ensure that the interval limiters implement the Limiter interface.
-var _ Limiter = (*intervalVaryByLimiter)(nil)
-var _ Limiter = (*intervalLimiter)(nil)
-
-// Interval creates a throttler that controls the requests so that they
-// go through at a constant interval. The interval is specified by the
-// delay argument, and convenience types such as PerSec can be used to
-// express the interval in a more expressive way, i.e. PerSec(10) means
-// 10 requests per second or one request each 100ms, PerMin(30) means
-// 30 requests per minute or on request each 2s, etc.
-//
-// The bursts argument indicates the number of exceeding requests that may
-// be queued up waiting to be processed. Requests that overflow the queue
-// are dropped and go through the denied handler, which may be specified
-// on the Throttler and that defaults to the package-global variable
-// DefaultDeniedHandler.
-//
-// The vary argument indicates the criteria to use to group the requests,
-// so that the interval applies to the requests in the same group (e.g. based on
-// the path, or the remote IP address, etc.). If this argument is nil, the
-// interval applies to all requests going through this throttler.
-//
-// The maxKeys indicates the maximum number of keys to keep in memory to apply the interval,
-// when a vary argument is specified. A LRU algorithm is used to remove older keys.
-//
-func Interval(delay Delayer, bursts int, vary *VaryBy, maxKeys int) *Throttler {
- var l Limiter
- if vary != nil {
- if maxKeys < 1 {
- maxKeys = 1
- }
- l = &intervalVaryByLimiter{
- delay: delay.Delay(),
- bursts: bursts,
- vary: vary,
- maxKeys: maxKeys,
- }
- } else {
- l = &intervalLimiter{
- delay: delay.Delay(),
- bursts: bursts,
- }
- }
- return &Throttler{
- limiter: l,
- }
-}
-
-// The intervalLimiter struct implements an interval limiter with no vary-by
-// criteria.
-type intervalLimiter struct {
- delay time.Duration
- bursts int
-
- bucket chan chan bool
-}
-
-// Start initializes the limiter for execution.
-func (il *intervalLimiter) Start() {
- if il.bursts < 0 {
- il.bursts = 0
- }
- il.bucket = make(chan chan bool, il.bursts)
- go process(il.bucket, il.delay)
-}
-
-// Limit is called for each request to the throttled handler. It tries to
-// queue the request to allow it to run at the given interval, but if the
-// queue is full, the request is denied access.
-func (il *intervalLimiter) Limit(w http.ResponseWriter, r *http.Request) (<-chan bool, error) {
- ch := make(chan bool, 1)
- select {
- case il.bucket <- ch:
- return ch, nil
- default:
- ch <- false
- return ch, nil
- }
-}
-
-// The intervalVaryByLimiter struct implements an interval limiter with a vary-by
-// criteria.
-type intervalVaryByLimiter struct {
- delay time.Duration
- bursts int
- vary *VaryBy
-
- lock sync.RWMutex
- keys *lru.Cache
- maxKeys int
-}
-
-// Start initializes the limiter for execution.
-func (il *intervalVaryByLimiter) Start() {
- if il.bursts < 0 {
- il.bursts = 0
- }
- il.keys = lru.New(il.maxKeys)
- il.keys.OnEvicted = il.stopProcess
-}
-
-// Limit is called for each request to the throttled handler. It tries to
-// queue the request for the vary-by key to allow it to run at the given interval,
-// but if the queue is full, the request is denied access.
-func (il *intervalVaryByLimiter) Limit(w http.ResponseWriter, r *http.Request) (<-chan bool, error) {
- ch := make(chan bool, 1)
- key := il.vary.Key(r)
-
- il.lock.RLock()
- item, ok := il.keys.Get(key)
- if !ok {
- // Create the key, bucket, start goroutine
- // First release the read lock and acquire a write lock
- il.lock.RUnlock()
- il.lock.Lock()
- // Create the bucket, add the key
- bucket := make(chan chan bool, il.bursts)
- il.keys.Add(key, bucket)
- // Start the goroutine to process this bucket
- go process(bucket, il.delay)
- item = bucket
- // Release the write lock, acquire the read lock
- il.lock.Unlock()
- il.lock.RLock()
- }
- defer il.lock.RUnlock()
- bucket := item.(chan chan bool)
- select {
- case bucket <- ch:
- return ch, nil
- default:
- ch <- false
- return ch, nil
- }
-}
-
-// process loops through the queued requests for a key's bucket, and sends
-// requests through at the given interval.
-func process(bucket chan chan bool, delay time.Duration) {
- after := time.After(0)
- for v := range bucket {
- <-after
- // Let the request go through
- v <- true
- // Wait the required duration
- after = time.After(delay)
- }
-}
-
-// stopProcess is called when a key is removed from the LRU cache so that its
-// accompanying goroutine is correctly released.
-func (il *intervalVaryByLimiter) stopProcess(key lru.Key, value interface{}) {
- close(value.(chan chan bool))
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/interval_test.go b/vendor/gopkg.in/throttled/throttled.v1/interval_test.go
deleted file mode 100644
index bc584e134..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/interval_test.go
+++ /dev/null
@@ -1,114 +0,0 @@
-package throttled
-
-import (
- "net/http"
- "testing"
-
- "github.com/PuerkitoBio/boom/commands"
-)
-
-func TestInterval(t *testing.T) {
- if testing.Short() {
- t.Skip()
- }
- cases := []struct {
- n int
- c int
- rps int
- bursts int
- }{
- 0: {60, 10, 20, 100},
- 1: {300, 20, 100, 100},
- 2: {10, 10, 1, 10},
- 3: {1000, 100, 1000, 100},
- }
- for i, c := range cases {
- // Setup the stats handler
- st := &stats{}
- // Create the throttler
- th := Interval(PerSec(c.rps), c.bursts, nil, 0)
- th.DeniedHandler = http.HandlerFunc(st.DeniedHTTP)
- b := commands.Boom{
- Req: &commands.ReqOpts{},
- N: c.n,
- C: c.c,
- Output: "quiet",
- }
- // Run the test
- rpts := runTest(th.Throttle(st), b)
- // Assert results
- for _, rpt := range rpts {
- assertRPS(t, i, c.rps, rpt)
- }
- assertStats(t, i, st, rpts)
- }
-}
-
-func TestIntervalVary(t *testing.T) {
- if testing.Short() {
- t.Skip()
- }
- cases := []struct {
- n int
- c int
- urls int
- rps int
- bursts int
- }{
- 0: {60, 10, 3, 20, 100},
- 1: {300, 20, 3, 100, 100},
- 2: {10, 10, 3, 1, 10},
- 3: {500, 10, 2, 1000, 100},
- }
- for i, c := range cases {
- // Setup the stats handler
- st := &stats{}
- // Create the throttler
- th := Interval(PerSec(c.rps), c.bursts, nil, 0)
- th.DeniedHandler = http.HandlerFunc(st.DeniedHTTP)
- var booms []commands.Boom
- for j := 0; j < c.urls; j++ {
- booms = append(booms, commands.Boom{
- Req: &commands.ReqOpts{},
- N: c.n,
- C: c.c,
- Output: "quiet",
- })
- }
- // Run the test
- rpts := runTest(th.Throttle(st), booms...)
- // Assert results
- for _, rpt := range rpts {
- assertRPS(t, i, c.rps, rpt)
- }
- assertStats(t, i, st, rpts)
- }
-}
-
-func assertRPS(t *testing.T, ix int, exp int, rpt *commands.Report) {
- wigglef := 0.2 * float64(exp)
- if rpt.SuccessRPS < float64(exp)-wigglef || rpt.SuccessRPS > float64(exp)+wigglef {
- t.Errorf("%d: expected RPS to be around %d, got %f", ix, exp, rpt.SuccessRPS)
- }
-}
-
-func assertStats(t *testing.T, ix int, st *stats, rpts []*commands.Report) {
- ok, ko, _ := st.Stats()
- var twos, fives, max int
- for _, rpt := range rpts {
- twos += rpt.StatusCodeDist[200]
- fives += rpt.StatusCodeDist[deniedStatus]
- if len(rpt.StatusCodeDist) > max {
- max = len(rpt.StatusCodeDist)
- }
- }
- if ok != twos {
- t.Errorf("%d: expected %d status 200, got %d", ix, twos, ok)
- }
- if ko != fives {
- t.Errorf("%d: expected %d status 429, got %d", ix, fives, ok)
- }
- if max > 2 {
- t.Errorf("%d: expected at most 2 different status codes, got %d", ix, max)
- }
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/memstats.go b/vendor/gopkg.in/throttled/throttled.v1/memstats.go
deleted file mode 100644
index bd2765630..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/memstats.go
+++ /dev/null
@@ -1,214 +0,0 @@
-package throttled
-
-import (
- "net/http"
- "runtime"
- "sync"
- "time"
-)
-
-// Static check to ensure that memStatsLimiter implements Limiter.
-var _ Limiter = (*memStatsLimiter)(nil)
-
-// The memStatsLimiter struct implements a limiter based on the memory statistics
-// of the current process.
-type memStatsLimiter struct {
- thresholds *runtime.MemStats
- refreshRate time.Duration
-
- lockStats sync.RWMutex
- stats runtime.MemStats
-}
-
-// MemStats creates a Throttler based on the memory statistics of the current process.
-// Any combination of any (non-array) integer field of Go's runtime.MemStats structure
-// can be used as thresholds to deny a request.
-//
-// As soon as one threshold value is reached, the access is denied. If the value can
-// decrease, access will be allowed again once it gets back under the threshold value.
-// Denied requests go through the denied handler, which may be specified on the Throttler
-// and that defaults to the package-global variable DefaultDeniedHandler.
-//
-// Thresholds must be specified in absolute numbers (i.e. NumGC = 10 means stop once the
-// NumGC reaches 10, not when the current value increments by 10), and zero values are
-// ignored.
-//
-// The refreshRate indicates the frequency at which the process' memory stats are refreshed,
-// and 0 means on each request.
-//
-func MemStats(thresholds *runtime.MemStats, refreshRate time.Duration) *Throttler {
- return &Throttler{
- limiter: &memStatsLimiter{
- thresholds: thresholds,
- refreshRate: refreshRate,
- },
- }
-}
-
-// Start initialized the limiter for execution.
-func (m *memStatsLimiter) Start() {
- // Make sure there is an initial MemStats reading
- runtime.ReadMemStats(&m.stats)
- if m.refreshRate > 0 {
- go m.refresh()
- }
-}
-
-// refresh runs in a separate goroutine and refreshes the memory statistics
-// at regular intervals.
-func (m *memStatsLimiter) refresh() {
- c := time.Tick(m.refreshRate)
- for _ = range c {
- m.lockStats.Lock()
- runtime.ReadMemStats(&m.stats)
- m.lockStats.Unlock()
- }
-}
-
-// Limit is called for each request to the throttled handler. It checks if
-// the request can go through by checking the memory thresholds, and signals it
-// via the returned channel.
-func (m *memStatsLimiter) Limit(w http.ResponseWriter, r *http.Request) (<-chan bool, error) {
- ch := make(chan bool, 1)
- // Check if memory thresholds are reached
- ch <- m.allow()
- return ch, nil
-}
-
-// allow compares the current memory stats with the thresholds, and returns
-// false if any threshold is reached.
-func (m *memStatsLimiter) allow() bool {
- m.lockStats.RLock()
- mem := m.stats
- m.lockStats.RUnlock()
- // If refreshRate == 0, then read on every request.
- if m.refreshRate == 0 {
- runtime.ReadMemStats(&mem)
- }
- ok := true
- checkStat(m.thresholds.Alloc, mem.Alloc, &ok)
- checkStat(m.thresholds.BuckHashSys, mem.BuckHashSys, &ok)
- checkStat(m.thresholds.Frees, mem.Frees, &ok)
- checkStat(m.thresholds.GCSys, mem.GCSys, &ok)
- checkStat(m.thresholds.HeapAlloc, mem.HeapAlloc, &ok)
- checkStat(m.thresholds.HeapIdle, mem.HeapIdle, &ok)
- checkStat(m.thresholds.HeapInuse, mem.HeapInuse, &ok)
- checkStat(m.thresholds.HeapObjects, mem.HeapObjects, &ok)
- checkStat(m.thresholds.HeapReleased, mem.HeapReleased, &ok)
- checkStat(m.thresholds.HeapSys, mem.HeapSys, &ok)
- checkStat(m.thresholds.LastGC, mem.LastGC, &ok)
- checkStat(m.thresholds.Lookups, mem.Lookups, &ok)
- checkStat(m.thresholds.MCacheInuse, mem.MCacheInuse, &ok)
- checkStat(m.thresholds.MCacheSys, mem.MCacheSys, &ok)
- checkStat(m.thresholds.MSpanInuse, mem.MSpanInuse, &ok)
- checkStat(m.thresholds.MSpanSys, mem.MSpanSys, &ok)
- checkStat(m.thresholds.Mallocs, mem.Mallocs, &ok)
- checkStat(m.thresholds.NextGC, mem.NextGC, &ok)
- checkStat(uint64(m.thresholds.NumGC), uint64(mem.NumGC), &ok)
- checkStat(m.thresholds.OtherSys, mem.OtherSys, &ok)
- checkStat(m.thresholds.PauseTotalNs, mem.PauseTotalNs, &ok)
- checkStat(m.thresholds.StackInuse, mem.StackInuse, &ok)
- checkStat(m.thresholds.StackSys, mem.StackSys, &ok)
- checkStat(m.thresholds.Sys, mem.Sys, &ok)
- checkStat(m.thresholds.TotalAlloc, mem.TotalAlloc, &ok)
- return ok
-}
-
-// Checks the threshold value against the actual value, and assigns false
-// to the boolean pointer if the threshold is reached.
-func checkStat(threshold, actual uint64, ok *bool) {
- if !*ok {
- return
- }
- if threshold > 0 {
- if actual >= threshold {
- *ok = false
- }
- }
-}
-
-// MemThresholds is a convenience function to create a thresholds memory stats from
-// offsets to apply to the current memory stats. Zero values in the offset stats
-// are left to 0 in the resulting thresholds memory stats value.
-//
-// The return value may be used as thresholds argument to the MemStats function.
-func MemThresholds(offset *runtime.MemStats) *runtime.MemStats {
- var mem, thr runtime.MemStats
- runtime.ReadMemStats(&mem)
- if offset.Alloc > 0 {
- thr.Alloc = mem.Alloc + offset.Alloc
- }
- if offset.BuckHashSys > 0 {
- thr.BuckHashSys = mem.BuckHashSys + offset.BuckHashSys
- }
- if offset.Frees > 0 {
- thr.Frees = mem.Frees + offset.Frees
- }
- if offset.GCSys > 0 {
- thr.GCSys = mem.GCSys + offset.GCSys
- }
- if offset.HeapAlloc > 0 {
- thr.HeapAlloc = mem.HeapAlloc + offset.HeapAlloc
- }
- if offset.HeapIdle > 0 {
- thr.HeapIdle = mem.HeapIdle + offset.HeapIdle
- }
- if offset.HeapInuse > 0 {
- thr.HeapInuse = mem.HeapInuse + offset.HeapInuse
- }
- if offset.HeapObjects > 0 {
- thr.HeapObjects = mem.HeapObjects + offset.HeapObjects
- }
- if offset.HeapReleased > 0 {
- thr.HeapReleased = mem.HeapReleased + offset.HeapReleased
- }
- if offset.HeapSys > 0 {
- thr.HeapSys = mem.HeapSys + offset.HeapSys
- }
- if offset.LastGC > 0 {
- thr.LastGC = mem.LastGC + offset.LastGC
- }
- if offset.Lookups > 0 {
- thr.Lookups = mem.Lookups + offset.Lookups
- }
- if offset.MCacheInuse > 0 {
- thr.MCacheInuse = mem.MCacheInuse + offset.MCacheInuse
- }
- if offset.MCacheSys > 0 {
- thr.MCacheSys = mem.MCacheSys + offset.MCacheSys
- }
- if offset.MSpanInuse > 0 {
- thr.MSpanInuse = mem.MSpanInuse + offset.MSpanInuse
- }
- if offset.MSpanSys > 0 {
- thr.MSpanSys = mem.MSpanSys + offset.MSpanSys
- }
- if offset.Mallocs > 0 {
- thr.Mallocs = mem.Mallocs + offset.Mallocs
- }
- if offset.NextGC > 0 {
- thr.NextGC = mem.NextGC + offset.NextGC
- }
- if offset.NumGC > 0 {
- thr.NumGC = mem.NumGC + offset.NumGC
- }
- if offset.OtherSys > 0 {
- thr.OtherSys = mem.OtherSys + offset.OtherSys
- }
- if offset.PauseTotalNs > 0 {
- thr.PauseTotalNs = mem.PauseTotalNs + offset.PauseTotalNs
- }
- if offset.StackInuse > 0 {
- thr.StackInuse = mem.StackInuse + offset.StackInuse
- }
- if offset.StackSys > 0 {
- thr.StackSys = mem.StackSys + offset.StackSys
- }
- if offset.Sys > 0 {
- thr.Sys = mem.Sys + offset.Sys
- }
- if offset.TotalAlloc > 0 {
- thr.TotalAlloc = mem.TotalAlloc + offset.TotalAlloc
- }
- return &thr
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/memstats_test.go b/vendor/gopkg.in/throttled/throttled.v1/memstats_test.go
deleted file mode 100644
index 2b8faa721..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/memstats_test.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package throttled
-
-import (
- "net/http"
- "runtime"
- "testing"
- "time"
-
- "github.com/PuerkitoBio/boom/commands"
-)
-
-func TestMemStats(t *testing.T) {
- if testing.Short() {
- t.Skip()
- }
- cases := []struct {
- n int
- c int
- gc uint32
- total uint64
- rate time.Duration
- }{
- 0: {1000, 10, 3, 0, 0},
- 1: {200, 10, 0, 600000, 0},
- 2: {500, 10, 2, 555555, 10 * time.Millisecond},
- }
- for i, c := range cases {
- // Setup the stats handler
- st := &stats{}
- // Create the throttler
- limit := MemThresholds(&runtime.MemStats{NumGC: c.gc, TotalAlloc: c.total})
- th := MemStats(limit, c.rate)
- th.DeniedHandler = http.HandlerFunc(st.DeniedHTTP)
- // Run the test
- b := commands.Boom{
- Req: &commands.ReqOpts{},
- N: c.n,
- C: c.c,
- Output: "quiet",
- }
- rpts := runTest(th.Throttle(st), b)
- // Assert results
- assertStats(t, i, st, rpts)
- assertMem(t, i, limit)
- }
-}
-
-func assertMem(t *testing.T, ix int, limit *runtime.MemStats) {
- var mem runtime.MemStats
- runtime.ReadMemStats(&mem)
- if mem.NumGC < limit.NumGC {
- t.Errorf("%d: expected gc to be at least %d, got %d", ix, limit.NumGC, mem.NumGC)
- }
- if mem.TotalAlloc < limit.TotalAlloc {
- t.Errorf("%d: expected total alloc to be at least %dKb, got %dKb", ix, limit.TotalAlloc/1024, mem.TotalAlloc/1024)
- }
-}
-
-func BenchmarkReadMemStats(b *testing.B) {
- var mem runtime.MemStats
- for i := 0; i < b.N; i++ {
- runtime.ReadMemStats(&mem)
- }
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/misc/pre-commit b/vendor/gopkg.in/throttled/throttled.v1/misc/pre-commit
deleted file mode 100755
index 88b61bfde..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/misc/pre-commit
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/bin/sh
-# Copyright 2012 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# git gofmt pre-commit hook
-#
-# To use, store as .git/hooks/pre-commit inside your repository and make sure
-# it has execute permissions.
-#
-# This script does not handle file names that contain spaces.
-
-# golint is purely informational, it doesn't fail with exit code != 0 if it finds something,
-# because it may find a lot of false positives. Just print out its result for information.
-echo "lint result (informational only):"
-echo
-golint .
-
-# go vet returns 1 if an error was found. Exit the hook with this exit code.
-go vet ./...
-vetres=$?
-
-# Check for gofmt problems and report if any.
-gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.go$')
-[ -z "$gofiles" ] && echo "EXIT $vetres" && exit $vetres
-
-unformatted=$(gofmt -l $gofiles)
-[ -z "$unformatted" ] && echo "EXIT $vetres" && exit $vetres
-
-# Some files are not gofmt'd. Print message and fail.
-
-echo >&2 "Go files must be formatted with gofmt. Please run:"
-for fn in $unformatted; do
- echo >&2 " gofmt -w $PWD/$fn"
-done
-
-echo "EXIT 1"
-exit 1
diff --git a/vendor/gopkg.in/throttled/throttled.v1/rate.go b/vendor/gopkg.in/throttled/throttled.v1/rate.go
deleted file mode 100644
index d7a7de6d7..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/rate.go
+++ /dev/null
@@ -1,116 +0,0 @@
-package throttled
-
-import (
- "math"
- "net/http"
- "strconv"
- "time"
-)
-
-// Static check to ensure that rateLimiter implements Limiter.
-var _ Limiter = (*rateLimiter)(nil)
-
-// RateLimit creates a throttler that limits the number of requests allowed
-// in a certain time window defined by the Quota q. The q parameter specifies
-// the requests per time window, and it is silently set to at least 1 request
-// and at least a 1 second window if it is less than that. The time window
-// starts when the first request is made outside an existing window. Fractions
-// of seconds are not supported, they are truncated.
-//
-// The vary parameter indicates what criteria should be used to group requests
-// for which the limit must be applied (ex.: rate limit based on the remote address).
-// See varyby.go for the various options.
-//
-// The specified store is used to keep track of the request count and the
-// time remaining in the window. The throttled package comes with some stores
-// in the throttled/store package. Custom stores can be created too, by implementing
-// the Store interface.
-//
-// Requests that bust the rate limit are denied access and go through the denied handler,
-// which may be specified on the Throttler and that defaults to the package-global
-// variable DefaultDeniedHandler.
-//
-// The rate limit throttler sets the following headers on the response:
-//
-// X-RateLimit-Limit : quota
-// X-RateLimit-Remaining : number of requests remaining in the current window
-// X-RateLimit-Reset : seconds before a new window
-//
-// Additionally, if the request was denied access, the following header is added:
-//
-// Retry-After : seconds before the caller should retry
-//
-func RateLimit(q Quota, vary *VaryBy, store Store) *Throttler {
- // Extract requests and window
- reqs, win := q.Quota()
-
- // Create and return the throttler
- return &Throttler{
- limiter: &rateLimiter{
- reqs: reqs,
- window: win,
- vary: vary,
- store: store,
- },
- }
-}
-
-// The rate limiter implements limiting the request to a certain quota
-// based on the vary-by criteria. State is saved in the store.
-type rateLimiter struct {
- reqs int
- window time.Duration
- vary *VaryBy
- store Store
-}
-
-// Start initializes the limiter for execution.
-func (r *rateLimiter) Start() {
- if r.reqs < 1 {
- r.reqs = 1
- }
- if r.window < time.Second {
- r.window = time.Second
- }
-}
-
-// Limit is called for each request to the throttled handler. It checks if
-// the request can go through and signals it via the returned channel.
-// It returns an error if the operation fails.
-func (r *rateLimiter) Limit(w http.ResponseWriter, req *http.Request) (<-chan bool, error) {
- // Create return channel and initialize
- ch := make(chan bool, 1)
- ok := true
- key := r.vary.Key(req)
-
- // Get the current count and remaining seconds
- cnt, secs, err := r.store.Incr(key, r.window)
- // Handle the possible situations: error, begin new window, or increment current window.
- switch {
- case err != nil && err != ErrNoSuchKey:
- // An unexpected error occurred
- return nil, err
- case err == ErrNoSuchKey || secs <= 0:
- // Reset counter
- if err := r.store.Reset(key, r.window); err != nil {
- return nil, err
- }
- cnt = 1
- secs = int(r.window.Seconds())
- default:
- // If the limit is reached, deny access
- if cnt > r.reqs {
- ok = false
- }
- }
- // Set rate-limit headers
- w.Header().Add("X-RateLimit-Limit", strconv.Itoa(r.reqs))
- w.Header().Add("X-RateLimit-Remaining", strconv.Itoa(int(math.Max(float64(r.reqs-cnt), 0))))
- w.Header().Add("X-RateLimit-Reset", strconv.Itoa(secs))
- if !ok {
- w.Header().Add("Retry-After", strconv.Itoa(secs))
- }
- // Send response via the return channel
- ch <- ok
- return ch, nil
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/rate_test.go b/vendor/gopkg.in/throttled/throttled.v1/rate_test.go
deleted file mode 100644
index 67dea74b1..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/rate_test.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package throttled
-
-import (
- "net/http"
- "net/http/httptest"
- "strconv"
- "testing"
- "time"
-)
-
-const deniedStatus = 429
-
-// Simple memory store for tests, unsafe for concurrent access
-type mapStore struct {
- cnt map[string]int
- ts map[string]time.Time
-}
-
-func newMapStore() *mapStore {
- return &mapStore{
- make(map[string]int),
- make(map[string]time.Time),
- }
-}
-func (ms *mapStore) Incr(key string, window time.Duration) (int, int, error) {
- if _, ok := ms.cnt[key]; !ok {
- return 0, 0, ErrNoSuchKey
- }
- ms.cnt[key]++
- ts := ms.ts[key]
- return ms.cnt[key], RemainingSeconds(ts, window), nil
-}
-func (ms *mapStore) Reset(key string, win time.Duration) error {
- ms.cnt[key] = 1
- ms.ts[key] = time.Now().UTC()
- return nil
-}
-
-func TestRateLimit(t *testing.T) {
- quota := Q{5, 5 * time.Second}
- cases := []struct {
- limit, remain, reset, status int
- }{
- 0: {5, 4, 5, 200},
- 1: {5, 3, 4, 200},
- 2: {5, 2, 4, 200},
- 3: {5, 1, 3, 200},
- 4: {5, 0, 3, 200},
- 5: {5, 0, 2, deniedStatus},
- }
- // Limit the requests to 2 per second
- th := Interval(PerSec(2), 0, nil, 0)
- // Rate limit
- rl := RateLimit(quota, nil, newMapStore())
- // Create the stats
- st := &stats{}
- // Create the handler
- h := th.Throttle(rl.Throttle(st))
-
- // Start the server
- srv := httptest.NewServer(h)
- defer srv.Close()
- for i, c := range cases {
- callRateLimited(t, i, c.limit, c.remain, c.reset, c.status, srv.URL)
- }
- // Wait 3 seconds and call again, should start a new window
- time.Sleep(3 * time.Second)
- callRateLimited(t, len(cases), 5, 4, 5, 200, srv.URL)
-}
-
-func callRateLimited(t *testing.T, i, limit, remain, reset, status int, url string) {
- res, err := http.Get(url)
- if err != nil {
- t.Fatal(err)
- }
- defer res.Body.Close()
- // Assert status code
- if status != res.StatusCode {
- t.Errorf("%d: expected status %d, got %d", i, status, res.StatusCode)
- }
- // Assert headers
- if v := res.Header.Get("X-RateLimit-Limit"); v != strconv.Itoa(limit) {
- t.Errorf("%d: expected limit header to be %d, got %s", i, limit, v)
- }
- if v := res.Header.Get("X-RateLimit-Remaining"); v != strconv.Itoa(remain) {
- t.Errorf("%d: expected remain header to be %d, got %s", i, remain, v)
- }
- // Allow 1 second wiggle room
- v := res.Header.Get("X-RateLimit-Reset")
- vi, _ := strconv.Atoi(v)
- if vi < reset-1 || vi > reset+1 {
- t.Errorf("%d: expected reset header to be close to %d, got %d", i, reset, vi)
- }
- if status == deniedStatus {
- v := res.Header.Get("Retry-After")
- vi, _ := strconv.Atoi(v)
- if vi < reset-1 || vi > reset+1 {
- t.Errorf("%d: expected retry after header to be close to %d, got %d", i, reset, vi)
- }
- }
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/store.go b/vendor/gopkg.in/throttled/throttled.v1/store.go
deleted file mode 100644
index 760fe2b69..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/store.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package throttled
-
-import (
- "errors"
- "time"
-)
-
-// The error returned if the key does not exist in the Store.
-var ErrNoSuchKey = errors.New("throttled: no such key")
-
-// Store is the interface to implement to store the RateLimit state (number
-// of requests per key, time-to-live or creation timestamp).
-type Store interface {
- // Incr increments the count for the specified key and returns the new value along
- // with the number of seconds remaining. It may return an error
- // if the operation fails.
- //
- // The method may return ErrNoSuchKey if the key to increment does not exist,
- // in which case Reset will be called to initialize the value.
- Incr(string, time.Duration) (int, int, error)
-
- // Reset resets the key to 1 with the specified window duration. It must create the
- // key if it doesn't exist. It returns an error if it fails.
- Reset(string, time.Duration) error
-}
-
-// RemainingSeconds is a helper function that returns the number of seconds
-// remaining from an absolute timestamp in UTC.
-func RemainingSeconds(ts time.Time, window time.Duration) int {
- return int((window - time.Now().UTC().Sub(ts)).Seconds())
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/store/doc.go b/vendor/gopkg.in/throttled/throttled.v1/store/doc.go
deleted file mode 100644
index 8e33f2c98..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/store/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package store offers a memory-based and a Redis-based throttled.Store implementation.
-package store // import "gopkg.in/throttled/throttled.v1/store"
diff --git a/vendor/gopkg.in/throttled/throttled.v1/store/mem.go b/vendor/gopkg.in/throttled/throttled.v1/store/mem.go
deleted file mode 100644
index 22d200e8d..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/store/mem.go
+++ /dev/null
@@ -1,90 +0,0 @@
-package store
-
-import (
- "sync"
- "time"
-
- "github.com/golang/groupcache/lru"
- "gopkg.in/throttled/throttled.v1"
-)
-
-// memStore implements an in-memory Store.
-type memStore struct {
- sync.Mutex
- keys *lru.Cache
- m map[string]*counter
-}
-
-// NewMemStore creates a new MemStore. If maxKeys > 0, the number of different keys
-// is restricted to the specified amount. In this case, it uses an LRU algorithm to
-// evict older keys to make room for newer ones. If a request is made for a key that
-// has been evicted, it will be processed as if its count was 0, possibly allowing requests
-// that should be denied.
-//
-// If maxKeys <= 0, there is no limit on the number of keys, which may use an unbounded amount of
-// memory depending on the server's load.
-//
-// The MemStore is only for single-process rate-limiting. To share the rate limit state
-// among multiple instances of the web server, use a database- or key-value-based
-// store.
-//
-func NewMemStore(maxKeys int) throttled.Store {
- var m *memStore
- if maxKeys > 0 {
- m = &memStore{
- keys: lru.New(maxKeys),
- }
- } else {
- m = &memStore{
- m: make(map[string]*counter),
- }
- }
- return m
-}
-
-// A counter represents a single entry in the MemStore.
-type counter struct {
- n int
- ts time.Time
-}
-
-// Incr increments the counter for the specified key. It returns the new
-// count value and the remaining number of seconds, or an error.
-func (ms *memStore) Incr(key string, window time.Duration) (int, int, error) {
- ms.Lock()
- defer ms.Unlock()
- var c *counter
- if ms.keys != nil {
- v, _ := ms.keys.Get(key)
- if v != nil {
- c = v.(*counter)
- }
- } else {
- c = ms.m[key]
- }
- if c == nil {
- c = &counter{0, time.Now().UTC()}
- }
- c.n++
- if ms.keys != nil {
- ms.keys.Add(key, c)
- } else {
- ms.m[key] = c
- }
- return c.n, throttled.RemainingSeconds(c.ts, window), nil
-}
-
-// Reset resets the counter for the specified key. It sets the count
-// to 1 and initializes the timestamp with the current time, in UTC.
-// It returns an error if the operation fails.
-func (ms *memStore) Reset(key string, win time.Duration) error {
- ms.Lock()
- defer ms.Unlock()
- c := &counter{1, time.Now().UTC()}
- if ms.keys != nil {
- ms.keys.Add(key, c)
- } else {
- ms.m[key] = c
- }
- return nil
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/store/mem_test.go b/vendor/gopkg.in/throttled/throttled.v1/store/mem_test.go
deleted file mode 100644
index e8ef8d0da..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/store/mem_test.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package store
-
-import (
- "testing"
- "time"
-)
-
-func TestMemStore(t *testing.T) {
- st := NewMemStore(0)
- win := time.Second
-
- // Reset stores a key with count of 1, current timestamp
- err := st.Reset("k", time.Second)
- if err != nil {
- t.Errorf("expected reset to return nil, got %s", err)
- }
- cnt, sec1, _ := st.Incr("k", win)
- if cnt != 2 {
- t.Errorf("expected reset+incr to set count to 2, got %d", cnt)
- }
-
- // Incr increments the key, keeps same timestamp
- cnt, sec2, err := st.Incr("k", win)
- if err != nil {
- t.Errorf("expected 2nd incr to return nil error, got %s", err)
- }
- if cnt != 3 {
- t.Errorf("expected 2nd incr to return 3, got %d", cnt)
- }
- if sec1 != sec2 {
- t.Errorf("expected 2nd incr to return %d secs, got %d", sec1, sec2)
- }
-
- // Reset on existing key brings it back to 1, new timestamp
- err = st.Reset("k", win)
- if err != nil {
- t.Errorf("expected reset on existing key to return nil, got %s", err)
- }
- cnt, _, _ = st.Incr("k", win)
- if cnt != 2 {
- t.Errorf("expected last reset+incr to return 2, got %d", cnt)
- }
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/store/redis.go b/vendor/gopkg.in/throttled/throttled.v1/store/redis.go
deleted file mode 100644
index b089f9f4e..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/store/redis.go
+++ /dev/null
@@ -1,85 +0,0 @@
-package store
-
-import (
- "time"
-
- "github.com/garyburd/redigo/redis"
- "gopkg.in/throttled/throttled.v1"
-)
-
-// redisStore implements a Redis-based store.
-type redisStore struct {
- pool *redis.Pool
- prefix string
- db int
-}
-
-// NewRedisStore creates a new Redis-based store, using the provided pool to get its
-// connections. The keys will have the specified keyPrefix, which may be an empty string,
-// and the database index specified by db will be selected to store the keys.
-//
-func NewRedisStore(pool *redis.Pool, keyPrefix string, db int) throttled.Store {
- return &redisStore{
- pool: pool,
- prefix: keyPrefix,
- db: db,
- }
-}
-
-// Incr increments the specified key. If the key did not exist, it sets it to 1
-// and sets it to expire after the number of seconds specified by window.
-//
-// It returns the new count value and the number of remaining seconds, or an error
-// if the operation fails.
-func (r *redisStore) Incr(key string, window time.Duration) (int, int, error) {
- conn := r.pool.Get()
- defer conn.Close()
- if err := selectDB(r.db, conn); err != nil {
- return 0, 0, err
- }
- // Atomically increment and read the TTL.
- conn.Send("MULTI")
- conn.Send("INCR", r.prefix+key)
- conn.Send("TTL", r.prefix+key)
- vals, err := redis.Values(conn.Do("EXEC"))
- if err != nil {
- conn.Do("DISCARD")
- return 0, 0, err
- }
- var cnt, ttl int
- if _, err = redis.Scan(vals, &cnt, &ttl); err != nil {
- return 0, 0, err
- }
- // If there was no TTL set, then this is a newly created key (INCR creates the key
- // if it didn't exist), so set it to expire.
- if ttl == -1 {
- ttl = int(window.Seconds())
- _, err = conn.Do("EXPIRE", r.prefix+key, ttl)
- if err != nil {
- return 0, 0, err
- }
- }
- return cnt, ttl, nil
-}
-
-// Reset sets the value of the key to 1, and resets its time window.
-func (r *redisStore) Reset(key string, window time.Duration) error {
- conn := r.pool.Get()
- defer conn.Close()
- if err := selectDB(r.db, conn); err != nil {
- return err
- }
- _, err := redis.String(conn.Do("SET", r.prefix+key, "1", "EX", int(window.Seconds()), "NX"))
- return err
-}
-
-// Select the specified database index.
-func selectDB(db int, conn redis.Conn) error {
- // Select the specified database
- if db > 0 {
- if _, err := redis.String(conn.Do("SELECT", db)); err != nil {
- return err
- }
- }
- return nil
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/store/redis_test.go b/vendor/gopkg.in/throttled/throttled.v1/store/redis_test.go
deleted file mode 100644
index a282d6d25..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/store/redis_test.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package store
-
-import (
- "testing"
- "time"
-
- "github.com/garyburd/redigo/redis"
-)
-
-func getPool() *redis.Pool {
- pool := &redis.Pool{
- MaxIdle: 3,
- IdleTimeout: 30 * time.Second,
- Dial: func() (redis.Conn, error) {
- return redis.Dial("tcp", ":6379")
- },
- TestOnBorrow: func(c redis.Conn, t time.Time) error {
- _, err := c.Do("PING")
- return err
- },
- }
- return pool
-}
-
-func TestRedisStore(t *testing.T) {
- pool := getPool()
- c := pool.Get()
- if _, err := redis.String(c.Do("PING")); err != nil {
- c.Close()
- t.Skip("redis server not available on localhost port 6379")
- }
- st := NewRedisStore(pool, "throttled:", 1)
- win := 2 * time.Second
-
- // Incr increments the key, even if it does not exist
- cnt, secs, err := st.Incr("k", win)
- if err != nil {
- t.Errorf("expected initial incr to return nil error, got %s", err)
- }
- if cnt != 1 {
- t.Errorf("expected initial incr to return 1, got %d", cnt)
- }
- if secs != int(win.Seconds()) {
- t.Errorf("expected initial incr to return %d secs, got %d", int(win.Seconds()), secs)
- }
-
- // Waiting a second diminishes the remaining seconds
- time.Sleep(time.Second)
- _, sec2, _ := st.Incr("k", win)
- if sec2 != secs-1 {
- t.Errorf("expected 2nd incr after a 1s sleep to return %d secs, got %d", secs-1, sec2)
- }
-
- // Waiting a second so the key expires, Incr should set back to 1, initial secs
- time.Sleep(1100 * time.Millisecond)
- cnt, sec3, err := st.Incr("k", win)
- if err != nil {
- t.Errorf("expected last incr to return nil error, got %s", err)
- }
- if cnt != 1 {
- t.Errorf("expected last incr to return 1, got %d", cnt)
- }
- if sec3 != int(win.Seconds()) {
- t.Errorf("expected last incr to return %d secs, got %d", int(win.Seconds()), sec3)
- }
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/throttler.go b/vendor/gopkg.in/throttled/throttled.v1/throttler.go
deleted file mode 100644
index 06da13051..000000000
--- a/vendor/gopkg.in/throttled/throttled.v1/throttler.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package throttled
-
-import (
- "net/http"
- "sync"
-)
-
-var (
- // DefaultDeniedHandler handles the requests that were denied access because
- // of a throttler. By default, returns a 429 status code with a
- // generic message.
- DefaultDeniedHandler = http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- http.Error(w, "limit exceeded", 429)
- }))
-
- // Error is the function to call when an error occurs on a throttled handler.
- // By default, returns a 500 status code with a generic message.
- Error = ErrorFunc(func(w http.ResponseWriter, r *http.Request, err error) {
- http.Error(w, "internal error", http.StatusInternalServerError)
- })
-)
-
-// ErrorFunc defines the function type for the Error variable.
-type ErrorFunc func(w http.ResponseWriter, r *http.Request, err error)
-
-// The Limiter interface defines the methods required to control access to a
-// throttled handler.
-type Limiter interface {
- Start()
- Limit(http.ResponseWriter, *http.Request) (<-chan bool, error)
-}
-
-// Custom creates a Throttler using the provided Limiter implementation.
-func Custom(l Limiter) *Throttler {
- return &Throttler{
- limiter: l,
- }
-}
-
-// A Throttler controls access to HTTP handlers using a Limiter.
-type Throttler struct {
- // DeniedHandler is called if the request is disallowed. If it is nil,
- // the DefaultDeniedHandler variable is used.
- DeniedHandler http.Handler
-
- limiter Limiter
- // The mutex protects the started flag
- mu sync.Mutex
- started bool
-}
-
-// Throttle wraps a HTTP handler so that its access is controlled by
-// the Throttler. It returns the Handler with the throttling logic.
-func (t *Throttler) Throttle(h http.Handler) http.Handler {
- dh := t.start()
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- ch, err := t.limiter.Limit(w, r)
- if err != nil {
- Error(w, r, err)
- return
- }
- ok := <-ch
- if ok {
- h.ServeHTTP(w, r)
- } else {
- dh.ServeHTTP(w, r)
- }
- })
-}
-
-// start starts the throttling and returns the effective denied handler to
-// use for requests that were denied access.
-func (t *Throttler) start() http.Handler {
- t.mu.Lock()
- defer t.mu.Unlock()
- // Get the effective denied handler
- dh := t.DeniedHandler
- if dh == nil {
- dh = DefaultDeniedHandler
- }
- if !t.started {
- t.limiter.Start()
- t.started = true
- }
- return dh
-}
diff --git a/vendor/gopkg.in/throttled/throttled.v2/.gitignore b/vendor/gopkg.in/throttled/throttled.v2/.gitignore
new file mode 100644
index 000000000..96c4e10b7
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/.gitignore
@@ -0,0 +1,5 @@
+.DS_Store
+*.swp
+*.swo
+*.test
+*.out
diff --git a/vendor/gopkg.in/throttled/throttled.v2/.travis.yml b/vendor/gopkg.in/throttled/throttled.v2/.travis.yml
new file mode 100644
index 000000000..29225d7af
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/.travis.yml
@@ -0,0 +1,24 @@
+sudo: false
+
+language: go
+
+go:
+ - 1.3
+ - tip
+
+notifications:
+ email: false
+
+services:
+ - redis-server
+
+install:
+ - make get-deps
+ # Move to the gopkg location that rather than the default clone location
+ # otherwise Go 1.4+ complains about incorrect import paths.
+ - export GOPKG_DIR=$GOPATH/src/gopkg.in/throttled
+ - mkdir -p $GOPKG_DIR
+ - mv $TRAVIS_BUILD_DIR $GOPKG_DIR/throttled.v2
+ - cd $GOPKG_DIR/throttled.v2
+
+script: make
diff --git a/vendor/gopkg.in/throttled/throttled.v1/LICENSE b/vendor/gopkg.in/throttled/throttled.v2/LICENSE
index f9616483e..f9616483e 100644
--- a/vendor/gopkg.in/throttled/throttled.v1/LICENSE
+++ b/vendor/gopkg.in/throttled/throttled.v2/LICENSE
diff --git a/vendor/gopkg.in/throttled/throttled.v2/Makefile b/vendor/gopkg.in/throttled/throttled.v2/Makefile
new file mode 100644
index 000000000..cfc235c31
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/Makefile
@@ -0,0 +1,31 @@
+
+
+.PHONY: test test-cover bench lint get-deps .go-test .go-test-cover
+
+test: .go-test bench lint
+
+test-cover: .go-test-cover bench lint
+
+bench:
+ go test -race -bench=. -cpu=1,2,4
+
+lint:
+ gofmt -l .
+ifneq ($(TRAVIS_GO_VERSION),1.3) # go vet doesn't play nicely on 1.3
+ go vet ./...
+endif
+ which golint # Fail if golint doesn't exist
+ -golint . # Don't fail on golint warnings themselves
+ -golint store # Don't fail on golint warnings themselves
+
+get-deps:
+ go get github.com/garyburd/redigo/redis
+ go get github.com/hashicorp/golang-lru
+ go get github.com/golang/lint/golint
+
+.go-test:
+ go test ./...
+
+.go-test-cover:
+ go test -coverprofile=throttled.coverage.out .
+ go test -coverprofile=store.coverage.out ./store
diff --git a/vendor/gopkg.in/throttled/throttled.v2/README.md b/vendor/gopkg.in/throttled/throttled.v2/README.md
new file mode 100644
index 000000000..b18dcbcc6
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/README.md
@@ -0,0 +1,85 @@
+# Throttled [![build status](https://secure.travis-ci.org/throttled/throttled.png)](https://travis-ci.org/throttled/throttled) [![GoDoc](https://godoc.org/gopkg.in/throttled/throttled.v2?status.png)](https://godoc.org/gopkg.in/throttled/throttled.v2)
+
+Package throttled implements rate limiting access to resources such as
+HTTP endpoints.
+
+The 2.0.0 release made some major changes to the throttled API. If
+this change broke your code in problematic ways or you wish a feature
+of the old API had been retained, please open an issue. We don't
+guarantee any particular changes but would like to hear more about
+what our users need. Thanks!
+
+## Installation
+
+throttled uses gopkg.in for semantic versioning:
+`go get gopkg.in/throttled/throttled.v2`
+
+As of July 27, 2015, the package is located under its own Github
+organization. Please adjust your imports to
+`gopkg.in/throttled/throttled.v2`.
+
+The 1.x release series is compatible with the original, unversioned
+library written by [Martin Angers][puerkitobio]. There is a
+[blog post explaining that version's usage on 0value.com][blog].
+
+## Documentation
+
+API documentation is available on [godoc.org][doc]. The following
+example demonstrates the usage of HTTPLimiter for rate-limiting access
+to an http.Handler to 20 requests per path per minute with bursts of
+up to 5 additional requests:
+
+ store, err := memstore.New(65536)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ quota := throttled.RateQuota{throttled.PerMin(20), 5}
+ rateLimiter, err := throttled.NewGCRARateLimiter(store, quota)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ httpRateLimiter := throttled.HTTPRateLimiter{
+ RateLimiter: rateLimiter,
+ VaryBy: &throttled.VaryBy{Path: true},
+ }
+
+ http.ListenAndServe(":8080", httpRateLimiter.RateLimit(myHandler))
+
+## Contributing
+
+Since throttled uses gopkg.in for versioning, running `go get` against
+a fork or cloning from Github to the default path will break
+imports. Instead, use the following process for setting up your
+environment and contributing:
+
+```sh
+# Retrieve the source and dependencies.
+go get gopkg.in/throttled/throttled.v2/...
+
+# Fork the project on Github. For all following directions replace
+# <username> with your Github username. Add your fork as a remote.
+cd $GOPATH/src/gopkg.in/throttled/throttled.v2
+git remote add fork git@github.com:<username>/throttled.git
+
+# Create a branch, make your changes, test them and commit.
+git checkout -b my-new-feature
+# <do some work>
+make test
+git commit -a
+git push -u fork my-new-feature
+```
+
+When your changes are ready, [open a pull request][pr] using "compare
+across forks".
+
+## License
+
+The [BSD 3-clause license][bsd]. Copyright (c) 2014 Martin Angers and Contributors.
+
+[blog]: http://0value.com/throttled--guardian-of-the-web-server
+[bsd]: https://opensource.org/licenses/BSD-3-Clause
+[doc]: https://godoc.org/gopkg.in/throttled/throttled.v2
+[puerkitobio]: https://github.com/puerkitobio/
+[pr]: https://github.com/throttled/throttled/compare
diff --git a/vendor/gopkg.in/throttled/throttled.v2/deprecated.go b/vendor/gopkg.in/throttled/throttled.v2/deprecated.go
new file mode 100644
index 000000000..f2c648a3e
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/deprecated.go
@@ -0,0 +1,73 @@
+package throttled
+
+import (
+ "net/http"
+ "time"
+)
+
+// DEPRECATED. Quota returns the number of requests allowed and the custom time window.
+func (q Rate) Quota() (int, time.Duration) {
+ return q.count, q.period * time.Duration(q.count)
+}
+
+// DEPRECATED. Q represents a custom quota.
+type Q struct {
+ Requests int
+ Window time.Duration
+}
+
+// DEPRECATED. Quota returns the number of requests allowed and the custom time window.
+func (q Q) Quota() (int, time.Duration) {
+ return q.Requests, q.Window
+}
+
+// DEPRECATED. The Quota interface defines the method to implement to describe
+// a time-window quota, as required by the RateLimit throttler.
+type Quota interface {
+ // Quota returns a number of requests allowed, and a duration.
+ Quota() (int, time.Duration)
+}
+
+// DEPRECATED. Throttler is a backwards-compatible alias for HTTPLimiter.
+type Throttler struct {
+ HTTPRateLimiter
+}
+
+// DEPRECATED. Throttle is an alias for HTTPLimiter#Limit
+func (t *Throttler) Throttle(h http.Handler) http.Handler {
+ return t.RateLimit(h)
+}
+
+// DEPRECATED. RateLimit creates a Throttler that conforms to the given
+// rate limits
+func RateLimit(q Quota, vary *VaryBy, store GCRAStore) *Throttler {
+ count, period := q.Quota()
+
+ if count < 1 {
+ count = 1
+ }
+ if period <= 0 {
+ period = time.Second
+ }
+
+ rate := Rate{period: period / time.Duration(count)}
+ limiter, err := NewGCRARateLimiter(store, RateQuota{rate, count - 1})
+
+ // This panic in unavoidable because the original interface does
+ // not support returning an error.
+ if err != nil {
+ panic(err)
+ }
+
+ return &Throttler{
+ HTTPRateLimiter{
+ RateLimiter: limiter,
+ VaryBy: vary,
+ },
+ }
+}
+
+// DEPRECATED. Store is an alias for GCRAStore
+type Store interface {
+ GCRAStore
+}
diff --git a/vendor/gopkg.in/throttled/throttled.v2/deprecated_test.go b/vendor/gopkg.in/throttled/throttled.v2/deprecated_test.go
new file mode 100644
index 000000000..93406648f
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/deprecated_test.go
@@ -0,0 +1,59 @@
+package throttled_test
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "gopkg.in/throttled/throttled.v2"
+ "gopkg.in/throttled/throttled.v2/store"
+)
+
+// Ensure that the current implementation remains compatible with the
+// supported but deprecated usage until the next major version.
+func TestDeprecatedUsage(t *testing.T) {
+ // Declare interfaces to statically check that names haven't changed
+ var st throttled.Store
+ var thr *throttled.Throttler
+ var q throttled.Quota
+
+ st = store.NewMemStore(100)
+ vary := &throttled.VaryBy{Path: true}
+ q = throttled.PerMin(2)
+ thr = throttled.RateLimit(q, vary, st)
+ handler := thr.Throttle(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(200)
+ }))
+
+ cases := []struct {
+ path string
+ code int
+ headers map[string]string
+ }{
+ {"/foo", 200, map[string]string{"X-Ratelimit-Limit": "2", "X-Ratelimit-Remaining": "1", "X-Ratelimit-Reset": "30"}},
+ {"/foo", 200, map[string]string{"X-Ratelimit-Limit": "2", "X-Ratelimit-Remaining": "0", "X-Ratelimit-Reset": "60"}},
+ {"/foo", 429, map[string]string{"X-Ratelimit-Limit": "2", "X-Ratelimit-Remaining": "0", "X-Ratelimit-Reset": "60", "Retry-After": "30"}},
+ {"/bar", 200, map[string]string{"X-Ratelimit-Limit": "2", "X-Ratelimit-Remaining": "1", "X-Ratelimit-Reset": "30"}},
+ }
+
+ for i, c := range cases {
+ req, err := http.NewRequest("GET", c.path, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ rr := httptest.NewRecorder()
+ handler.ServeHTTP(rr, req)
+ if have, want := rr.Code, c.code; have != want {
+ t.Errorf("Expected request %d at %s to return %d but got %d",
+ i, c.path, want, have)
+ }
+
+ for name, want := range c.headers {
+ if have := rr.HeaderMap.Get(name); have != want {
+ t.Errorf("Expected request %d at %s to have header '%s: %s' but got '%s'",
+ i, c.path, name, want, have)
+ }
+ }
+ }
+}
diff --git a/vendor/gopkg.in/throttled/throttled.v2/doc.go b/vendor/gopkg.in/throttled/throttled.v2/doc.go
new file mode 100644
index 000000000..302c2bed7
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/doc.go
@@ -0,0 +1,3 @@
+// Package throttled implements rate limiting access to resources such
+// as HTTP endpoints.
+package throttled // import "gopkg.in/throttled/throttled.v2"
diff --git a/vendor/gopkg.in/throttled/throttled.v2/example_test.go b/vendor/gopkg.in/throttled/throttled.v2/example_test.go
new file mode 100644
index 000000000..66e6374be
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/example_test.go
@@ -0,0 +1,103 @@
+package throttled_test
+
+import (
+ "fmt"
+ "log"
+ "net/http"
+
+ "gopkg.in/throttled/throttled.v2"
+ "gopkg.in/throttled/throttled.v2/store/memstore"
+)
+
+var myHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte("hi there!"))
+})
+
+// ExampleHTTPRateLimiter demonstrates the usage of HTTPRateLimiter
+// for rate-limiting access to an http.Handler to 20 requests per path
+// per minute with a maximum burst of 5 requests.
+func ExampleHTTPRateLimiter() {
+ store, err := memstore.New(65536)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Maximum burst of 5 which refills at 20 tokens per minute.
+ quota := throttled.RateQuota{throttled.PerMin(20), 5}
+
+ rateLimiter, err := throttled.NewGCRARateLimiter(store, quota)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ httpRateLimiter := throttled.HTTPRateLimiter{
+ RateLimiter: rateLimiter,
+ VaryBy: &throttled.VaryBy{Path: true},
+ }
+
+ http.ListenAndServe(":8080", httpRateLimiter.RateLimit(myHandler))
+}
+
+// Demonstrates direct use of GCRARateLimiter's RateLimit function (and the
+// more general RateLimiter interface). This should be used anywhere where
+// granular control over rate limiting is required.
+func ExampleGCRARateLimiter() {
+ store, err := memstore.New(65536)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Maximum burst of 5 which refills at 1 token per hour.
+ quota := throttled.RateQuota{throttled.PerHour(1), 5}
+
+ rateLimiter, err := throttled.NewGCRARateLimiter(store, quota)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Bucket according to the number i / 10 (so 1 falls into the bucket 0
+ // while 11 falls into the bucket 1). This has the effect of allowing a
+ // burst of 5 plus 1 (a single emission interval) on every ten iterations
+ // of the loop. See the output for better clarity here.
+ //
+ // We also refill the bucket at 1 token per hour, but that has no effect
+ // for the purposes of this example.
+ for i := 0; i < 20; i++ {
+ bucket := fmt.Sprintf("by-order:%v", i/10)
+
+ limited, result, err := rateLimiter.RateLimit(bucket, 1)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ if limited {
+ fmt.Printf("Iteration %2v; bucket %v: FAILED. Rate limit exceeded.\n",
+ i, bucket)
+ } else {
+ fmt.Printf("Iteration %2v; bucket %v: Operation successful (remaining=%v).\n",
+ i, bucket, result.Remaining)
+ }
+ }
+
+ // Output:
+ // Iteration 0; bucket by-order:0: Operation successful (remaining=5).
+ // Iteration 1; bucket by-order:0: Operation successful (remaining=4).
+ // Iteration 2; bucket by-order:0: Operation successful (remaining=3).
+ // Iteration 3; bucket by-order:0: Operation successful (remaining=2).
+ // Iteration 4; bucket by-order:0: Operation successful (remaining=1).
+ // Iteration 5; bucket by-order:0: Operation successful (remaining=0).
+ // Iteration 6; bucket by-order:0: FAILED. Rate limit exceeded.
+ // Iteration 7; bucket by-order:0: FAILED. Rate limit exceeded.
+ // Iteration 8; bucket by-order:0: FAILED. Rate limit exceeded.
+ // Iteration 9; bucket by-order:0: FAILED. Rate limit exceeded.
+ // Iteration 10; bucket by-order:1: Operation successful (remaining=5).
+ // Iteration 11; bucket by-order:1: Operation successful (remaining=4).
+ // Iteration 12; bucket by-order:1: Operation successful (remaining=3).
+ // Iteration 13; bucket by-order:1: Operation successful (remaining=2).
+ // Iteration 14; bucket by-order:1: Operation successful (remaining=1).
+ // Iteration 15; bucket by-order:1: Operation successful (remaining=0).
+ // Iteration 16; bucket by-order:1: FAILED. Rate limit exceeded.
+ // Iteration 17; bucket by-order:1: FAILED. Rate limit exceeded.
+ // Iteration 18; bucket by-order:1: FAILED. Rate limit exceeded.
+ // Iteration 19; bucket by-order:1: FAILED. Rate limit exceeded.
+}
diff --git a/vendor/gopkg.in/throttled/throttled.v2/http.go b/vendor/gopkg.in/throttled/throttled.v2/http.go
new file mode 100644
index 000000000..4c513a81d
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/http.go
@@ -0,0 +1,110 @@
+package throttled
+
+import (
+ "errors"
+ "math"
+ "net/http"
+ "strconv"
+)
+
+var (
+ // DefaultDeniedHandler is the default DeniedHandler for an
+ // HTTPRateLimiter. It returns a 429 status code with a generic
+ // message.
+ DefaultDeniedHandler = http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ http.Error(w, "limit exceeded", 429)
+ }))
+
+ // DefaultError is the default Error function for an HTTPRateLimiter.
+ // It returns a 500 status code with a generic message.
+ DefaultError = func(w http.ResponseWriter, r *http.Request, err error) {
+ http.Error(w, "internal error", http.StatusInternalServerError)
+ }
+)
+
+// HTTPRateLimiter faciliates using a Limiter to limit HTTP requests.
+type HTTPRateLimiter struct {
+ // DeniedHandler is called if the request is disallowed. If it is
+ // nil, the DefaultDeniedHandler variable is used.
+ DeniedHandler http.Handler
+
+ // Error is called if the RateLimiter returns an error. If it is
+ // nil, the DefaultErrorFunc is used.
+ Error func(w http.ResponseWriter, r *http.Request, err error)
+
+ // Limiter is call for each request to determine whether the
+ // request is permitted and update internal state. It must be set.
+ RateLimiter RateLimiter
+
+ // VaryBy is called for each request to generate a key for the
+ // limiter. If it is nil, all requests use an empty string key.
+ VaryBy interface {
+ Key(*http.Request) string
+ }
+}
+
+// RateLimit wraps an http.Handler to limit incoming requests.
+// Requests that are not limited will be passed to the handler
+// unchanged. Limited requests will be passed to the DeniedHandler.
+// X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and
+// Retry-After headers will be written to the response based on the
+// values in the RateLimitResult.
+func (t *HTTPRateLimiter) RateLimit(h http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if t.RateLimiter == nil {
+ t.error(w, r, errors.New("You must set a RateLimiter on HTTPRateLimiter"))
+ }
+
+ var k string
+ if t.VaryBy != nil {
+ k = t.VaryBy.Key(r)
+ }
+
+ limited, context, err := t.RateLimiter.RateLimit(k, 1)
+
+ if err != nil {
+ t.error(w, r, err)
+ return
+ }
+
+ setRateLimitHeaders(w, context)
+
+ if !limited {
+ h.ServeHTTP(w, r)
+ } else {
+ dh := t.DeniedHandler
+ if dh == nil {
+ dh = DefaultDeniedHandler
+ }
+ dh.ServeHTTP(w, r)
+ }
+ })
+}
+
+func (t *HTTPRateLimiter) error(w http.ResponseWriter, r *http.Request, err error) {
+ e := t.Error
+ if e == nil {
+ e = DefaultError
+ }
+ e(w, r, err)
+}
+
+func setRateLimitHeaders(w http.ResponseWriter, context RateLimitResult) {
+ if v := context.Limit; v >= 0 {
+ w.Header().Add("X-RateLimit-Limit", strconv.Itoa(v))
+ }
+
+ if v := context.Remaining; v >= 0 {
+ w.Header().Add("X-RateLimit-Remaining", strconv.Itoa(v))
+ }
+
+ if v := context.ResetAfter; v >= 0 {
+ vi := int(math.Ceil(v.Seconds()))
+ w.Header().Add("X-RateLimit-Reset", strconv.Itoa(vi))
+ }
+
+ if v := context.RetryAfter; v >= 0 {
+ vi := int(math.Ceil(v.Seconds()))
+ w.Header().Add("Retry-After", strconv.Itoa(vi))
+ }
+}
diff --git a/vendor/gopkg.in/throttled/throttled.v2/http_test.go b/vendor/gopkg.in/throttled/throttled.v2/http_test.go
new file mode 100644
index 000000000..42761da09
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/http_test.go
@@ -0,0 +1,99 @@
+package throttled_test
+
+import (
+ "errors"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+ "time"
+
+ "gopkg.in/throttled/throttled.v2"
+)
+
+type stubLimiter struct {
+}
+
+func (sl *stubLimiter) RateLimit(key string, quantity int) (bool, throttled.RateLimitResult, error) {
+ switch key {
+ case "limit":
+ return true, throttled.RateLimitResult{-1, -1, -1, time.Minute}, nil
+ case "error":
+ return false, throttled.RateLimitResult{}, errors.New("stubLimiter error")
+ default:
+ return false, throttled.RateLimitResult{1, 2, time.Minute, -1}, nil
+ }
+}
+
+type pathGetter struct{}
+
+func (*pathGetter) Key(r *http.Request) string {
+ return r.URL.Path
+}
+
+type httpTestCase struct {
+ path string
+ code int
+ headers map[string]string
+}
+
+func TestHTTPRateLimiter(t *testing.T) {
+ limiter := throttled.HTTPRateLimiter{
+ RateLimiter: &stubLimiter{},
+ VaryBy: &pathGetter{},
+ }
+
+ handler := limiter.RateLimit(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(200)
+ }))
+
+ runHTTPTestCases(t, handler, []httpTestCase{
+ {"ok", 200, map[string]string{"X-Ratelimit-Limit": "1", "X-Ratelimit-Remaining": "2", "X-Ratelimit-Reset": "60"}},
+ {"error", 500, map[string]string{}},
+ {"limit", 429, map[string]string{"Retry-After": "60"}},
+ })
+}
+
+func TestCustomHTTPRateLimiterHandlers(t *testing.T) {
+ limiter := throttled.HTTPRateLimiter{
+ RateLimiter: &stubLimiter{},
+ VaryBy: &pathGetter{},
+ DeniedHandler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ http.Error(w, "custom limit exceeded", 400)
+ }),
+ Error: func(w http.ResponseWriter, r *http.Request, err error) {
+ http.Error(w, "custom internal error", 501)
+ },
+ }
+
+ handler := limiter.RateLimit(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(200)
+ }))
+
+ runHTTPTestCases(t, handler, []httpTestCase{
+ {"limit", 400, map[string]string{}},
+ {"error", 501, map[string]string{}},
+ })
+}
+
+func runHTTPTestCases(t *testing.T, h http.Handler, cs []httpTestCase) {
+ for i, c := range cs {
+ req, err := http.NewRequest("GET", c.path, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ rr := httptest.NewRecorder()
+ h.ServeHTTP(rr, req)
+ if have, want := rr.Code, c.code; have != want {
+ t.Errorf("Expected request %d at %s to return %d but got %d",
+ i, c.path, want, have)
+ }
+
+ for name, want := range c.headers {
+ if have := rr.HeaderMap.Get(name); have != want {
+ t.Errorf("Expected request %d at %s to have header '%s: %s' but got '%s'",
+ i, c.path, name, want, have)
+ }
+ }
+ }
+}
diff --git a/vendor/gopkg.in/throttled/throttled.v2/rate.go b/vendor/gopkg.in/throttled/throttled.v2/rate.go
new file mode 100644
index 000000000..8c11cdb47
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/rate.go
@@ -0,0 +1,239 @@
+package throttled
+
+import (
+ "fmt"
+ "time"
+)
+
+const (
+ // Maximum number of times to retry SetIfNotExists/CompareAndSwap operations
+ // before returning an error.
+ maxCASAttempts = 10
+)
+
+// A RateLimiter manages limiting the rate of actions by key.
+type RateLimiter interface {
+ // RateLimit checks whether a particular key has exceeded a rate
+ // limit. It also returns a RateLimitResult to provide additional
+ // information about the state of the RateLimiter.
+ //
+ // If the rate limit has not been exceeded, the underlying storage
+ // is updated by the supplied quantity. For example, a quantity of
+ // 1 might be used to rate limit a single request while a greater
+ // quantity could rate limit based on the size of a file upload in
+ // megabytes. If quantity is 0, no update is performed allowing
+ // you to "peek" at the state of the RateLimiter for a given key.
+ RateLimit(key string, quantity int) (bool, RateLimitResult, error)
+}
+
+// RateLimitResult represents the state of the RateLimiter for a
+// given key at the time of the query. This state can be used, for
+// example, to communicate information to the client via HTTP
+// headers. Negative values indicate that the attribute is not
+// relevant to the implementation or state.
+type RateLimitResult struct {
+ // Limit is the maximum number of requests that could be permitted
+ // instantaneously for this key starting from an empty state. For
+ // example, if a rate limiter allows 10 requests per second per
+ // key, Limit would always be 10.
+ Limit int
+
+ // Remaining is the maximum number of requests that could be
+ // permitted instantaneously for this key given the current
+ // state. For example, if a rate limiter allows 10 requests per
+ // second and has already received 6 requests for this key this
+ // second, Remaining would be 4.
+ Remaining int
+
+ // ResetAfter is the time until the RateLimiter returns to its
+ // initial state for a given key. For example, if a rate limiter
+ // manages requests per second and received one request 200ms ago,
+ // Reset would return 800ms. You can also think of this as the time
+ // until Limit and Remaining will be equal.
+ ResetAfter time.Duration
+
+ // RetryAfter is the time until the next request will be permitted.
+ // It should be -1 unless the rate limit has been exceeded.
+ RetryAfter time.Duration
+}
+
+type limitResult struct {
+ limited bool
+}
+
+func (r *limitResult) Limited() bool { return r.limited }
+
+type rateLimitResult struct {
+ limitResult
+
+ limit, remaining int
+ reset, retryAfter time.Duration
+}
+
+func (r *rateLimitResult) Limit() int { return r.limit }
+func (r *rateLimitResult) Remaining() int { return r.remaining }
+func (r *rateLimitResult) Reset() time.Duration { return r.reset }
+func (r *rateLimitResult) RetryAfter() time.Duration { return r.retryAfter }
+
+// Rate describes a frequency of an activity such as the number of requests
+// allowed per minute.
+type Rate struct {
+ period time.Duration // Time between equally spaced requests at the rate
+ count int // Used internally for deprecated `RateLimit` interface only
+}
+
+// RateQuota describes the number of requests allowed per time period.
+// MaxRate specified the maximum sustained rate of requests and must
+// be greater than zero. MaxBurst defines the number of requests that
+// will be allowed to exceed the rate in a single burst and must be
+// greater than or equal to zero.
+//
+// Rate{PerSec(1), 0} would mean that after each request, no more
+// requests will be permitted for that client for one second. In
+// practice, you probably want to set MaxBurst >0 to provide some
+// flexibility to clients that only need to make a handful of
+// requests. In fact a MaxBurst of zero will *never* permit a request
+// with a quantity greater than one because it will immediately exceed
+// the limit.
+type RateQuota struct {
+ MaxRate Rate
+ MaxBurst int
+}
+
+// PerSec represents a number of requests per second.
+func PerSec(n int) Rate { return Rate{time.Second / time.Duration(n), n} }
+
+// PerMin represents a number of requests per minute.
+func PerMin(n int) Rate { return Rate{time.Minute / time.Duration(n), n} }
+
+// PerHour represents a number of requests per hour.
+func PerHour(n int) Rate { return Rate{time.Hour / time.Duration(n), n} }
+
+// PerDay represents a number of requests per day.
+func PerDay(n int) Rate { return Rate{24 * time.Hour / time.Duration(n), n} }
+
+// GCRARateLimiter is a RateLimiter that users the generic cell-rate
+// algorithm. The algorithm has been slightly modified from its usual
+// form to support limiting with an additional quantity parameter, such
+// as for limiting the number of bytes uploaded.
+type GCRARateLimiter struct {
+ limit int
+ // Think of the DVT as our flexibility:
+ // How far can you deviate from the nominal equally spaced schedule?
+ // If you like leaky buckets, think about it as the size of your bucket.
+ delayVariationTolerance time.Duration
+ // Think of the emission interval as the time between events
+ // in the nominal equally spaced schedule. If you like leaky buckets,
+ // think of it as how frequently the bucket leaks one unit.
+ emissionInterval time.Duration
+
+ store GCRAStore
+}
+
+// NewGCRARateLimiter creates a GCRARateLimiter. quota.Count defines
+// the maximum number of requests permitted in an instantaneous burst
+// and quota.Count / quota.Period defines the maximum sustained
+// rate. For example, PerMin(60) permits 60 requests instantly per key
+// followed by one request per second indefinitely whereas PerSec(1)
+// only permits one request per second with no tolerance for bursts.
+func NewGCRARateLimiter(st GCRAStore, quota RateQuota) (*GCRARateLimiter, error) {
+ if quota.MaxBurst < 0 {
+ return nil, fmt.Errorf("Invalid RateQuota %#v. MaxBurst must be greater than zero.", quota)
+ }
+ if quota.MaxRate.period <= 0 {
+ return nil, fmt.Errorf("Invalid RateQuota %#v. MaxRate must be greater than zero.", quota)
+ }
+
+ return &GCRARateLimiter{
+ delayVariationTolerance: quota.MaxRate.period * (time.Duration(quota.MaxBurst) + 1),
+ emissionInterval: quota.MaxRate.period,
+ limit: quota.MaxBurst + 1,
+ store: st,
+ }, nil
+}
+
+// RateLimit checks whether a particular key has exceeded a rate
+// limit. It also returns a RateLimitResult to provide additional
+// information about the state of the RateLimiter.
+//
+// If the rate limit has not been exceeded, the underlying storage is
+// updated by the supplied quantity. For example, a quantity of 1
+// might be used to rate limit a single request while a greater
+// quantity could rate limit based on the size of a file upload in
+// megabytes. If quantity is 0, no update is performed allowing you
+// to "peek" at the state of the RateLimiter for a given key.
+func (g *GCRARateLimiter) RateLimit(key string, quantity int) (bool, RateLimitResult, error) {
+ var tat, newTat, now time.Time
+ var ttl time.Duration
+ rlc := RateLimitResult{Limit: g.limit, RetryAfter: -1}
+ limited := false
+
+ i := 0
+ for {
+ var err error
+ var tatVal int64
+ var updated bool
+
+ // tat refers to the theoretical arrival time that would be expected
+ // from equally spaced requests at exactly the rate limit.
+ tatVal, now, err = g.store.GetWithTime(key)
+ if err != nil {
+ return false, rlc, err
+ }
+
+ if tatVal == -1 {
+ tat = now
+ } else {
+ tat = time.Unix(0, tatVal)
+ }
+
+ increment := time.Duration(quantity) * g.emissionInterval
+ if now.After(tat) {
+ newTat = now.Add(increment)
+ } else {
+ newTat = tat.Add(increment)
+ }
+
+ // Block the request if the next permitted time is in the future
+ allowAt := newTat.Add(-(g.delayVariationTolerance))
+ if diff := now.Sub(allowAt); diff < 0 {
+ if increment <= g.delayVariationTolerance {
+ rlc.RetryAfter = -diff
+ }
+ ttl = tat.Sub(now)
+ limited = true
+ break
+ }
+
+ ttl = newTat.Sub(now)
+
+ if tatVal == -1 {
+ updated, err = g.store.SetIfNotExistsWithTTL(key, newTat.UnixNano(), ttl)
+ } else {
+ updated, err = g.store.CompareAndSwapWithTTL(key, tatVal, newTat.UnixNano(), ttl)
+ }
+
+ if err != nil {
+ return false, rlc, err
+ }
+ if updated {
+ break
+ }
+
+ i++
+ if i > maxCASAttempts {
+ return false, rlc, fmt.Errorf(
+ "Failed to store updated rate limit data for key %s after %d attempts",
+ key, i,
+ )
+ }
+ }
+
+ next := g.delayVariationTolerance - ttl
+ if next > -g.emissionInterval {
+ rlc.Remaining = int(next / g.emissionInterval)
+ }
+ rlc.ResetAfter = ttl
+
+ return limited, rlc, nil
+}
diff --git a/vendor/gopkg.in/throttled/throttled.v2/rate_test.go b/vendor/gopkg.in/throttled/throttled.v2/rate_test.go
new file mode 100644
index 000000000..292a050bc
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/rate_test.go
@@ -0,0 +1,128 @@
+package throttled_test
+
+import (
+ "testing"
+ "time"
+
+ "gopkg.in/throttled/throttled.v2"
+ "gopkg.in/throttled/throttled.v2/store/memstore"
+)
+
+const deniedStatus = 429
+
+type testStore struct {
+ store throttled.GCRAStore
+
+ clock time.Time
+ failUpdates bool
+}
+
+func (ts *testStore) GetWithTime(key string) (int64, time.Time, error) {
+ v, _, e := ts.store.GetWithTime(key)
+ return v, ts.clock, e
+}
+
+func (ts *testStore) SetIfNotExistsWithTTL(key string, value int64, ttl time.Duration) (bool, error) {
+ if ts.failUpdates {
+ return false, nil
+ }
+ return ts.store.SetIfNotExistsWithTTL(key, value, ttl)
+}
+
+func (ts *testStore) CompareAndSwapWithTTL(key string, old, new int64, ttl time.Duration) (bool, error) {
+ if ts.failUpdates {
+ return false, nil
+ }
+ return ts.store.CompareAndSwapWithTTL(key, old, new, ttl)
+}
+
+func TestRateLimit(t *testing.T) {
+ limit := 5
+ rq := throttled.RateQuota{throttled.PerSec(1), limit - 1}
+ start := time.Unix(0, 0)
+ cases := []struct {
+ now time.Time
+ volume, remaining int
+ reset, retry time.Duration
+ limited bool
+ }{
+ // You can never make a request larger than the maximum
+ 0: {start, 6, 5, 0, -1, true},
+ // Rate limit normal requests appropriately
+ 1: {start, 1, 4, time.Second, -1, false},
+ 2: {start, 1, 3, 2 * time.Second, -1, false},
+ 3: {start, 1, 2, 3 * time.Second, -1, false},
+ 4: {start, 1, 1, 4 * time.Second, -1, false},
+ 5: {start, 1, 0, 5 * time.Second, -1, false},
+ 6: {start, 1, 0, 5 * time.Second, time.Second, true},
+ 7: {start.Add(3000 * time.Millisecond), 1, 2, 3000 * time.Millisecond, -1, false},
+ 8: {start.Add(3100 * time.Millisecond), 1, 1, 3900 * time.Millisecond, -1, false},
+ 9: {start.Add(4000 * time.Millisecond), 1, 1, 4000 * time.Millisecond, -1, false},
+ 10: {start.Add(8000 * time.Millisecond), 1, 4, 1000 * time.Millisecond, -1, false},
+ 11: {start.Add(9500 * time.Millisecond), 1, 4, 1000 * time.Millisecond, -1, false},
+ // Zero-volume request just peeks at the state
+ 12: {start.Add(9500 * time.Millisecond), 0, 4, time.Second, -1, false},
+ // High-volume request uses up more of the limit
+ 13: {start.Add(9500 * time.Millisecond), 2, 2, 3 * time.Second, -1, false},
+ // Large requests cannot exceed limits
+ 14: {start.Add(9500 * time.Millisecond), 5, 2, 3 * time.Second, 3 * time.Second, true},
+ }
+
+ mst, err := memstore.New(0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ st := testStore{store: mst}
+
+ rl, err := throttled.NewGCRARateLimiter(&st, rq)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Start the server
+ for i, c := range cases {
+ st.clock = c.now
+
+ limited, context, err := rl.RateLimit("foo", c.volume)
+ if err != nil {
+ t.Fatalf("%d: %#v", i, err)
+ }
+
+ if limited != c.limited {
+ t.Errorf("%d: expected Limited to be %t but got %t", i, c.limited, limited)
+ }
+
+ if have, want := context.Limit, limit; have != want {
+ t.Errorf("%d: expected Limit to be %d but got %d", i, want, have)
+ }
+
+ if have, want := context.Remaining, c.remaining; have != want {
+ t.Errorf("%d: expected Remaining to be %d but got %d", i, want, have)
+ }
+
+ if have, want := context.ResetAfter, c.reset; have != want {
+ t.Errorf("%d: expected ResetAfter to be %s but got %s", i, want, have)
+ }
+
+ if have, want := context.RetryAfter, c.retry; have != want {
+ t.Errorf("%d: expected RetryAfter to be %d but got %d", i, want, have)
+ }
+ }
+}
+
+func TestRateLimitUpdateFailures(t *testing.T) {
+ rq := throttled.RateQuota{throttled.PerSec(1), 1}
+ mst, err := memstore.New(0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ st := testStore{store: mst, failUpdates: true}
+ rl, err := throttled.NewGCRARateLimiter(&st, rq)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if _, _, err := rl.RateLimit("foo", 1); err == nil {
+ t.Error("Expected limiting to fail when store updates fail")
+ }
+}
diff --git a/vendor/gopkg.in/throttled/throttled.v2/store.go b/vendor/gopkg.in/throttled/throttled.v2/store.go
new file mode 100644
index 000000000..a26bbc2c5
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/store.go
@@ -0,0 +1,34 @@
+package throttled
+
+import (
+ "time"
+)
+
+// GCRAStore is the interface to implement to store state for a GCRA
+// rate limiter
+type GCRAStore interface {
+ // GetWithTime returns the value of the key if it is in the store
+ // or -1 if it does not exist. It also returns the current time at
+ // the Store. The time must be representable as a positive int64
+ // of nanoseconds since the epoch.
+ //
+ // GCRA assumes that all instances sharing the same Store also
+ // share the same clock. Using separate clocks will work if the
+ // skew is small but not recommended in practice unless you're
+ // lucky enough to be hooked up to GPS or atomic clocks.
+ GetWithTime(key string) (int64, time.Time, error)
+
+ // SetIfNotExistsWithTTL sets the value of key only if it is not
+ // already set in the store it returns whether a new value was
+ // set. If the store supports expiring keys and a new value was
+ // set, the key will expire after the provided ttl.
+ SetIfNotExistsWithTTL(key string, value int64, ttl time.Duration) (bool, error)
+
+ // CompareAndSwapWithTTL atomically compares the value at key to
+ // the old value. If it matches, it sets it to the new value and
+ // returns true. Otherwise, it returns false. If the key does not
+ // exist in the store, it returns false with no error. If the
+ // store supports expiring keys and the swap succeeded, the key
+ // will expire after the provided ttl.
+ CompareAndSwapWithTTL(key string, old, new int64, ttl time.Duration) (bool, error)
+}
diff --git a/vendor/gopkg.in/throttled/throttled.v2/store/deprecated.go b/vendor/gopkg.in/throttled/throttled.v2/store/deprecated.go
new file mode 100644
index 000000000..5476e87ac
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/store/deprecated.go
@@ -0,0 +1,32 @@
+// Package store contains deprecated aliases for subpackages
+package store // import "gopkg.in/throttled/throttled.v2/store"
+
+import (
+ "github.com/garyburd/redigo/redis"
+
+ "gopkg.in/throttled/throttled.v2/store/memstore"
+ "gopkg.in/throttled/throttled.v2/store/redigostore"
+)
+
+// DEPRECATED. NewMemStore is a compatible alias for mem.New
+func NewMemStore(maxKeys int) *memstore.MemStore {
+ st, err := memstore.New(maxKeys)
+ if err != nil {
+ // As of this writing, `lru.New` can only return an error if you pass
+ // maxKeys <= 0 so this should never occur.
+ panic(err)
+ }
+ return st
+}
+
+// DEPRECATED. NewMemStore is a compatible alias for redis.New
+func NewRedisStore(pool *redis.Pool, keyPrefix string, db int) *redigostore.RedigoStore {
+ st, err := redigostore.New(pool, keyPrefix, db)
+ if err != nil {
+ // As of this writing, creating a Redis store never returns an error
+ // so this should be safe while providing some ability to return errors
+ // in the future.
+ panic(err)
+ }
+ return st
+}
diff --git a/vendor/gopkg.in/throttled/throttled.v2/store/memstore/memstore.go b/vendor/gopkg.in/throttled/throttled.v2/store/memstore/memstore.go
new file mode 100644
index 000000000..5d8fee8b5
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/store/memstore/memstore.go
@@ -0,0 +1,127 @@
+// Package memstore offers an in-memory store implementation for throttled.
+package memstore // import "gopkg.in/throttled/throttled.v2/store/memstore"
+
+import (
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/hashicorp/golang-lru"
+)
+
+// MemStore is an in-memory store implementation for throttled. It
+// supports evicting the least recently used keys to control memory
+// usage. It is stored in memory in the current process and thus
+// doesn't share state with other rate limiters.
+type MemStore struct {
+ sync.RWMutex
+ keys *lru.Cache
+ m map[string]*int64
+}
+
+// New initializes a Store. If maxKeys > 0, the number of different
+// keys is restricted to the specified amount. In this case, it uses
+// an LRU algorithm to evict older keys to make room for newer
+// ones. If maxKeys <= 0, there is no limit on the number of keys,
+// which may use an unbounded amount of memory.
+func New(maxKeys int) (*MemStore, error) {
+ var m *MemStore
+
+ if maxKeys > 0 {
+ keys, err := lru.New(maxKeys)
+ if err != nil {
+ return nil, err
+ }
+
+ m = &MemStore{
+ keys: keys,
+ }
+ } else {
+ m = &MemStore{
+ m: make(map[string]*int64),
+ }
+ }
+ return m, nil
+}
+
+// GetWithTime returns the value of the key if it is in the store or
+// -1 if it does not exist. It also returns the current local time on
+// the machine.
+func (ms *MemStore) GetWithTime(key string) (int64, time.Time, error) {
+ now := time.Now()
+ valP, ok := ms.get(key, false)
+
+ if !ok {
+ return -1, now, nil
+ }
+
+ return atomic.LoadInt64(valP), now, nil
+}
+
+// SetIfNotExistsWithTTL sets the value of key only if it is not
+// already set in the store it returns whether a new value was set. It
+// ignores the ttl.
+func (ms *MemStore) SetIfNotExistsWithTTL(key string, value int64, _ time.Duration) (bool, error) {
+ _, ok := ms.get(key, false)
+
+ if ok {
+ return false, nil
+ }
+
+ ms.Lock()
+ defer ms.Unlock()
+
+ _, ok = ms.get(key, true)
+
+ if ok {
+ return false, nil
+ }
+
+ // Store a pointer to a new instance so that the caller
+ // can't mutate the value after setting
+ v := value
+
+ if ms.keys != nil {
+ ms.keys.Add(key, &v)
+ } else {
+ ms.m[key] = &v
+ }
+
+ return true, nil
+}
+
+// CompareAndSwapWithTTL atomically compares the value at key to the
+// old value. If it matches, it sets it to the new value and returns
+// true. Otherwise, it returns false. If the key does not exist in the
+// store, it returns false with no error. It ignores the ttl.
+func (ms *MemStore) CompareAndSwapWithTTL(key string, old, new int64, _ time.Duration) (bool, error) {
+ valP, ok := ms.get(key, false)
+
+ if !ok {
+ return false, nil
+ }
+
+ return atomic.CompareAndSwapInt64(valP, old, new), nil
+}
+
+func (ms *MemStore) get(key string, locked bool) (*int64, bool) {
+ var valP *int64
+ var ok bool
+
+ if ms.keys != nil {
+ var valI interface{}
+
+ valI, ok = ms.keys.Get(key)
+ if ok {
+ valP = valI.(*int64)
+ }
+ } else {
+ if !locked {
+ ms.RLock()
+ defer ms.RUnlock()
+ }
+ valP, ok = ms.m[key]
+ }
+
+ return valP, ok
+}
diff --git a/vendor/gopkg.in/throttled/throttled.v2/store/memstore/memstore_test.go b/vendor/gopkg.in/throttled/throttled.v2/store/memstore/memstore_test.go
new file mode 100644
index 000000000..ef003d3de
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/store/memstore/memstore_test.go
@@ -0,0 +1,40 @@
+package memstore_test
+
+import (
+ "testing"
+
+ "gopkg.in/throttled/throttled.v2/store/memstore"
+ "gopkg.in/throttled/throttled.v2/store/storetest"
+)
+
+func TestMemStoreLRU(t *testing.T) {
+ st, err := memstore.New(10)
+ if err != nil {
+ t.Fatal(err)
+ }
+ storetest.TestGCRAStore(t, st)
+}
+
+func TestMemStoreUnlimited(t *testing.T) {
+ st, err := memstore.New(10)
+ if err != nil {
+ t.Fatal(err)
+ }
+ storetest.TestGCRAStore(t, st)
+}
+
+func BenchmarkMemStoreLRU(b *testing.B) {
+ st, err := memstore.New(10)
+ if err != nil {
+ b.Fatal(err)
+ }
+ storetest.BenchmarkGCRAStore(b, st)
+}
+
+func BenchmarkMemStoreUnlimited(b *testing.B) {
+ st, err := memstore.New(0)
+ if err != nil {
+ b.Fatal(err)
+ }
+ storetest.BenchmarkGCRAStore(b, st)
+}
diff --git a/vendor/gopkg.in/throttled/throttled.v2/store/redigostore/redigostore.go b/vendor/gopkg.in/throttled/throttled.v2/store/redigostore/redigostore.go
new file mode 100644
index 000000000..54208fa6d
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/store/redigostore/redigostore.go
@@ -0,0 +1,156 @@
+// Package redigostore offers Redis-based store implementation for throttled using redigo.
+package redigostore // import "gopkg.in/throttled/throttled.v2/store/redigostore"
+
+import (
+ "strings"
+ "time"
+
+ "github.com/garyburd/redigo/redis"
+)
+
+const (
+ redisCASMissingKey = "key does not exist"
+ redisCASScript = `
+local v = redis.call('get', KEYS[1])
+if v == false then
+ return redis.error_reply("key does not exist")
+end
+if v ~= ARGV[1] then
+ return 0
+end
+if ARGV[3] ~= "0" then
+ redis.call('setex', KEYS[1], ARGV[3], ARGV[2])
+else
+ redis.call('set', KEYS[1], ARGV[2])
+end
+return 1
+`
+)
+
+// RedigoStore implements a Redis-based store using redigo.
+type RedigoStore struct {
+ pool *redis.Pool
+ prefix string
+ db int
+}
+
+// New creates a new Redis-based store, using the provided pool to get
+// its connections. The keys will have the specified keyPrefix, which
+// may be an empty string, and the database index specified by db will
+// be selected to store the keys. Any updating operations will reset
+// the key TTL to the provided value rounded down to the nearest
+// second. Depends on Redis 2.6+ for EVAL support.
+func New(pool *redis.Pool, keyPrefix string, db int) (*RedigoStore, error) {
+ return &RedigoStore{
+ pool: pool,
+ prefix: keyPrefix,
+ db: db,
+ }, nil
+}
+
+// GetWithTime returns the value of the key if it is in the store
+// or -1 if it does not exist. It also returns the current time at
+// the redis server to microsecond precision.
+func (r *RedigoStore) GetWithTime(key string) (int64, time.Time, error) {
+ var now time.Time
+
+ key = r.prefix + key
+
+ conn, err := r.getConn()
+ if err != nil {
+ return 0, now, err
+ }
+ defer conn.Close()
+
+ conn.Send("TIME")
+ conn.Send("GET", key)
+ conn.Flush()
+ timeReply, err := redis.Values(conn.Receive())
+ if err != nil {
+ return 0, now, err
+ }
+
+ var s, us int64
+ if _, err := redis.Scan(timeReply, &s, &us); err != nil {
+ return 0, now, err
+ }
+ now = time.Unix(s, us*int64(time.Microsecond))
+
+ v, err := redis.Int64(conn.Receive())
+ if err == redis.ErrNil {
+ return -1, now, nil
+ } else if err != nil {
+ return 0, now, err
+ }
+
+ return v, now, nil
+}
+
+// SetIfNotExistsWithTTL sets the value of key only if it is not
+// already set in the store it returns whether a new value was set.
+// If a new value was set, the ttl in the key is also set, though this
+// operation is not performed atomically.
+func (r *RedigoStore) SetIfNotExistsWithTTL(key string, value int64, ttl time.Duration) (bool, error) {
+ key = r.prefix + key
+
+ conn, err := r.getConn()
+ if err != nil {
+ return false, err
+ }
+ defer conn.Close()
+
+ v, err := redis.Int64(conn.Do("SETNX", key, value))
+ if err != nil {
+ return false, err
+ }
+
+ updated := v == 1
+
+ if ttl >= time.Second {
+ if _, err := conn.Do("EXPIRE", key, int(ttl.Seconds())); err != nil {
+ return updated, err
+ }
+ }
+
+ return updated, nil
+}
+
+// CompareAndSwapWithTTL atomically compares the value at key to the
+// old value. If it matches, it sets it to the new value and returns
+// true. Otherwise, it returns false. If the key does not exist in the
+// store, it returns false with no error. If the swap succeeds, the
+// ttl for the key is updated atomically.
+func (r *RedigoStore) CompareAndSwapWithTTL(key string, old, new int64, ttl time.Duration) (bool, error) {
+ key = r.prefix + key
+ conn, err := r.getConn()
+ if err != nil {
+ return false, err
+ }
+ defer conn.Close()
+
+ swapped, err := redis.Bool(conn.Do("EVAL", redisCASScript, 1, key, old, new, int(ttl.Seconds())))
+ if err != nil {
+ if strings.Contains(err.Error(), redisCASMissingKey) {
+ return false, nil
+ }
+
+ return false, err
+ }
+
+ return swapped, nil
+}
+
+// Select the specified database index.
+func (r *RedigoStore) getConn() (redis.Conn, error) {
+ conn := r.pool.Get()
+
+ // Select the specified database
+ if r.db > 0 {
+ if _, err := redis.String(conn.Do("SELECT", r.db)); err != nil {
+ conn.Close()
+ return nil, err
+ }
+ }
+
+ return conn, nil
+}
diff --git a/vendor/gopkg.in/throttled/throttled.v2/store/redigostore/redisstore_test.go b/vendor/gopkg.in/throttled/throttled.v2/store/redigostore/redisstore_test.go
new file mode 100644
index 000000000..d47b635d2
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/store/redigostore/redisstore_test.go
@@ -0,0 +1,85 @@
+package redigostore_test
+
+import (
+ "testing"
+ "time"
+
+ "github.com/garyburd/redigo/redis"
+
+ "gopkg.in/throttled/throttled.v2/store/redigostore"
+ "gopkg.in/throttled/throttled.v2/store/storetest"
+)
+
+const (
+ redisTestDB = 1
+ redisTestPrefix = "throttled:"
+)
+
+func getPool() *redis.Pool {
+ pool := &redis.Pool{
+ MaxIdle: 3,
+ IdleTimeout: 30 * time.Second,
+ Dial: func() (redis.Conn, error) {
+ return redis.Dial("tcp", ":6379")
+ },
+ TestOnBorrow: func(c redis.Conn, t time.Time) error {
+ _, err := c.Do("PING")
+ return err
+ },
+ }
+ return pool
+}
+
+func TestRedisStore(t *testing.T) {
+ c, st := setupRedis(t, 0)
+ defer c.Close()
+ defer clearRedis(c)
+
+ clearRedis(c)
+ storetest.TestGCRAStore(t, st)
+ storetest.TestGCRAStoreTTL(t, st)
+}
+
+func BenchmarkRedisStore(b *testing.B) {
+ c, st := setupRedis(b, 0)
+ defer c.Close()
+ defer clearRedis(c)
+
+ storetest.BenchmarkGCRAStore(b, st)
+}
+
+func clearRedis(c redis.Conn) error {
+ keys, err := redis.Values(c.Do("KEYS", redisTestPrefix+"*"))
+ if err != nil {
+ return err
+ }
+
+ if _, err := redis.Int(c.Do("DEL", keys...)); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func setupRedis(tb testing.TB, ttl time.Duration) (redis.Conn, *redigostore.RedigoStore) {
+ pool := getPool()
+ c := pool.Get()
+
+ if _, err := redis.String(c.Do("PING")); err != nil {
+ c.Close()
+ tb.Skip("redis server not available on localhost port 6379")
+ }
+
+ if _, err := redis.String(c.Do("SELECT", redisTestDB)); err != nil {
+ c.Close()
+ tb.Fatal(err)
+ }
+
+ st, err := redigostore.New(pool, redisTestPrefix, redisTestDB)
+ if err != nil {
+ c.Close()
+ tb.Fatal(err)
+ }
+
+ return c, st
+}
diff --git a/vendor/gopkg.in/throttled/throttled.v2/store/storetest/doc.go b/vendor/gopkg.in/throttled/throttled.v2/store/storetest/doc.go
new file mode 100644
index 000000000..ecfee2638
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/store/storetest/doc.go
@@ -0,0 +1,2 @@
+// Package storetest provides a helper for testing throttled stores.
+package storetest // import "gopkg.in/throttled/throttled.v2/store/storetest"
diff --git a/vendor/gopkg.in/throttled/throttled.v2/store/storetest/storetest.go b/vendor/gopkg.in/throttled/throttled.v2/store/storetest/storetest.go
new file mode 100644
index 000000000..191b40a4f
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v2/store/storetest/storetest.go
@@ -0,0 +1,176 @@
+// Package storetest provides a helper for testing throttled stores.
+package storetest // import "gopkg.in/throttled/throttled.v2/store/storetest"
+
+import (
+ "math/rand"
+ "strconv"
+ "sync/atomic"
+ "testing"
+ "time"
+
+ "gopkg.in/throttled/throttled.v2"
+)
+
+// TestGCRAStore tests the behavior of a GCRAStore implementation for
+// compliance with the throttled API. It does not require support
+// for TTLs.
+func TestGCRAStore(t *testing.T, st throttled.GCRAStore) {
+ // GetWithTime a missing key
+ if have, _, err := st.GetWithTime("foo"); err != nil {
+ t.Fatal(err)
+ } else if have != -1 {
+ t.Errorf("expected GetWithTime to return -1 for a missing key but got %d", have)
+ }
+
+ // SetIfNotExists on a new key
+ want := int64(1)
+
+ if set, err := st.SetIfNotExistsWithTTL("foo", want, 0); err != nil {
+ t.Fatal(err)
+ } else if !set {
+ t.Errorf("expected SetIfNotExists on an empty key to succeed")
+ }
+
+ before := time.Now()
+
+ if have, now, err := st.GetWithTime("foo"); err != nil {
+ t.Fatal(err)
+ } else if have != want {
+ t.Errorf("expected GetWithTime to return %d but got %d", want, have)
+ } else if now.UnixNano() <= 0 {
+ t.Errorf("expected GetWithTime to return a time representable representable as a positive int64 of nanoseconds since the epoch")
+ } else if now.Before(before) || now.After(time.Now()) {
+ // Note that we make the assumption here that the store is running on
+ // the same machine as this test and thus shares a clock. This can be a
+ // little tricky in the case of Redis, which could be running
+ // elsewhere. The test assumes that it's running either locally on on
+ // Travis (where currently the Redis is available on localhost). If new
+ // test environments are procured, this may need to be revisited.
+ t.Errorf("expected GetWithTime to return a time between the time before the call and the time after the call")
+ }
+
+ // SetIfNotExists on an existing key
+ if set, err := st.SetIfNotExistsWithTTL("foo", 123, 0); err != nil {
+ t.Fatal(err)
+ } else if set {
+ t.Errorf("expected SetIfNotExists on an existing key to fail")
+ }
+
+ if have, _, err := st.GetWithTime("foo"); err != nil {
+ t.Fatal(err)
+ } else if have != want {
+ t.Errorf("expected GetWithTime to return %d but got %d", want, have)
+ }
+
+ // SetIfNotExists on a different key
+ if set, err := st.SetIfNotExistsWithTTL("bar", 456, 0); err != nil {
+ t.Fatal(err)
+ } else if !set {
+ t.Errorf("expected SetIfNotExists on an empty key to succeed")
+ }
+
+ // Returns the false on a missing key
+ if swapped, err := st.CompareAndSwapWithTTL("baz", 1, 2, 0); err != nil {
+ t.Fatal(err)
+ } else if swapped {
+ t.Errorf("expected CompareAndSwap to fail on a missing key")
+ }
+
+ // Test a successful CAS
+ want = int64(2)
+
+ if swapped, err := st.CompareAndSwapWithTTL("foo", 1, want, 0); err != nil {
+ t.Fatal(err)
+ } else if !swapped {
+ t.Errorf("expected CompareAndSwap to succeed")
+ }
+
+ if have, _, err := st.GetWithTime("foo"); err != nil {
+ t.Fatal(err)
+ } else if have != want {
+ t.Errorf("expected GetWithTime to return %d but got %d", want, have)
+ }
+
+ // Test an unsuccessful CAS
+ if swapped, err := st.CompareAndSwapWithTTL("foo", 1, 2, 0); err != nil {
+ t.Fatal(err)
+ } else if swapped {
+ t.Errorf("expected CompareAndSwap to fail")
+ }
+
+ if have, _, err := st.GetWithTime("foo"); err != nil {
+ t.Fatal(err)
+ } else if have != want {
+ t.Errorf("expected GetWithTime to return %d but got %d", want, have)
+ }
+}
+
+// TestGCRAStoreTTL tests the behavior of TTLs in a GCRAStore implementation.
+func TestGCRAStoreTTL(t *testing.T, st throttled.GCRAStore) {
+ ttl := time.Second
+ want := int64(1)
+ key := "ttl"
+
+ if _, err := st.SetIfNotExistsWithTTL(key, want, ttl); err != nil {
+ t.Fatal(err)
+ }
+
+ if have, _, err := st.GetWithTime(key); err != nil {
+ t.Fatal(err)
+ } else if have != want {
+ t.Errorf("expected GetWithTime to return %d, got %d", want, have)
+ }
+
+ // I can't think of a generic way to test expiration without a sleep
+ time.Sleep(ttl + time.Millisecond)
+
+ if have, _, err := st.GetWithTime(key); err != nil {
+ t.Fatal(err)
+ } else if have != -1 {
+ t.Errorf("expected GetWithTime to fail on an expired key but got %d", have)
+ }
+}
+
+// BenchmarkGCRAStore runs parallel benchmarks against a GCRAStore implementation.
+// Aside from being useful for performance testing, this is useful for finding
+// race conditions with the Go race detector.
+func BenchmarkGCRAStore(b *testing.B, st throttled.GCRAStore) {
+ seed := int64(42)
+ var attempts, updates int64
+
+ b.RunParallel(func(pb *testing.PB) {
+ // We need atomic behavior around the RNG or go detects a race in the test
+ delta := int64(1)
+ seedValue := atomic.AddInt64(&seed, delta) - delta
+ gen := rand.New(rand.NewSource(seedValue))
+
+ for pb.Next() {
+ key := strconv.FormatInt(gen.Int63n(50), 10)
+
+ var v int64
+ var updated bool
+
+ v, _, err := st.GetWithTime(key)
+ if v == -1 {
+ updated, err = st.SetIfNotExistsWithTTL(key, gen.Int63(), 0)
+ if err != nil {
+ b.Error(err)
+ }
+ } else if err != nil {
+ b.Error(err)
+ } else {
+ updated, err = st.CompareAndSwapWithTTL(key, v, gen.Int63(), 0)
+ if err != nil {
+ b.Error(err)
+ }
+ }
+
+ atomic.AddInt64(&attempts, 1)
+ if updated {
+ atomic.AddInt64(&updates, 1)
+ }
+ }
+ })
+
+ b.Logf("%d/%d update operations succeeed", updates, attempts)
+}
diff --git a/vendor/gopkg.in/throttled/throttled.v1/varyby.go b/vendor/gopkg.in/throttled/throttled.v2/varyby.go
index 3b2cdb011..dc6a01718 100644
--- a/vendor/gopkg.in/throttled/throttled.v1/varyby.go
+++ b/vendor/gopkg.in/throttled/throttled.v2/varyby.go
@@ -31,7 +31,7 @@ type VaryBy struct {
// Defaults to a newline character if empty (\n).
Separator string
- // Custom specifies the custom-generated key to use for this request.
+ // DEPRECATED. Custom specifies the custom-generated key to use for this request.
// If not nil, the value returned by this function is used instead of any
// VaryBy criteria.
Custom func(r *http.Request) string
diff --git a/vendor/gopkg.in/throttled/throttled.v1/varyby_test.go b/vendor/gopkg.in/throttled/throttled.v2/varyby_test.go
index 91b7ae0ae..66a5f4e98 100644
--- a/vendor/gopkg.in/throttled/throttled.v1/varyby_test.go
+++ b/vendor/gopkg.in/throttled/throttled.v2/varyby_test.go
@@ -1,9 +1,11 @@
-package throttled
+package throttled_test
import (
"net/http"
"net/url"
"testing"
+
+ "gopkg.in/throttled/throttled.v2"
)
func TestVaryBy(t *testing.T) {
@@ -13,34 +15,34 @@ func TestVaryBy(t *testing.T) {
}
ck := &http.Cookie{Name: "ssn", Value: "test"}
cases := []struct {
- vb *VaryBy
+ vb *throttled.VaryBy
r *http.Request
k string
}{
0: {nil, &http.Request{}, ""},
- 1: {&VaryBy{RemoteAddr: true}, &http.Request{RemoteAddr: "::"}, "::\n"},
+ 1: {&throttled.VaryBy{RemoteAddr: true}, &http.Request{RemoteAddr: "::"}, "::\n"},
2: {
- &VaryBy{Method: true, Path: true},
+ &throttled.VaryBy{Method: true, Path: true},
&http.Request{Method: "POST", URL: u},
"post\n/test/path\n",
},
3: {
- &VaryBy{Headers: []string{"Content-length"}},
+ &throttled.VaryBy{Headers: []string{"Content-length"}},
&http.Request{Header: http.Header{"Content-Type": []string{"text/plain"}, "Content-Length": []string{"123"}}},
"123\n",
},
4: {
- &VaryBy{Separator: ",", Method: true, Headers: []string{"Content-length"}, Params: []string{"q", "user"}},
+ &throttled.VaryBy{Separator: ",", Method: true, Headers: []string{"Content-length"}, Params: []string{"q", "user"}},
&http.Request{Method: "GET", Header: http.Header{"Content-Type": []string{"text/plain"}, "Content-Length": []string{"123"}}, Form: url.Values{"q": []string{"s"}, "pwd": []string{"secret"}, "user": []string{"test"}}},
"get,123,s,test,",
},
5: {
- &VaryBy{Cookies: []string{"ssn"}},
+ &throttled.VaryBy{Cookies: []string{"ssn"}},
&http.Request{Header: http.Header{"Cookie": []string{ck.String()}}},
"test\n",
},
6: {
- &VaryBy{Cookies: []string{"ssn"}, RemoteAddr: true, Custom: func(r *http.Request) string {
+ &throttled.VaryBy{Cookies: []string{"ssn"}, RemoteAddr: true, Custom: func(r *http.Request) string {
return "blah"
}},
&http.Request{Header: http.Header{"Cookie": []string{ck.String()}}},
diff --git a/vendor/gopkg.in/yaml.v2/LICENSE b/vendor/gopkg.in/yaml.v2/LICENSE
index a68e67f01..866d74a7a 100644
--- a/vendor/gopkg.in/yaml.v2/LICENSE
+++ b/vendor/gopkg.in/yaml.v2/LICENSE
@@ -1,188 +1,13 @@
+Copyright 2011-2016 Canonical Ltd.
-Copyright (c) 2011-2014 - Canonical Inc.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
-This software is licensed under the LGPLv3, included below.
+ http://www.apache.org/licenses/LICENSE-2.0
-As a special exception to the GNU Lesser General Public License version 3
-("LGPL3"), the copyright holders of this Library give you permission to
-convey to a third party a Combined Work that links statically or dynamically
-to this Library without providing any Minimal Corresponding Source or
-Minimal Application Code as set out in 4d or providing the installation
-information set out in section 4e, provided that you comply with the other
-provisions of LGPL3 and provided that you meet, for the Application the
-terms and conditions of the license(s) which apply to the Application.
-
-Except as stated in this special exception, the provisions of LGPL3 will
-continue to comply in full to this Library. If you modify this Library, you
-may apply this exception to your version of this Library, but you are not
-obliged to do so. If you do not wish to do so, delete this exception
-statement from your version. This exception does not (and cannot) modify any
-license terms which apply to the Application, with which you must still
-comply.
-
-
- GNU LESSER GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-
- This version of the GNU Lesser General Public License incorporates
-the terms and conditions of version 3 of the GNU General Public
-License, supplemented by the additional permissions listed below.
-
- 0. Additional Definitions.
-
- As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the GNU
-General Public License.
-
- "The Library" refers to a covered work governed by this License,
-other than an Application or a Combined Work as defined below.
-
- An "Application" is any work that makes use of an interface provided
-by the Library, but which is not otherwise based on the Library.
-Defining a subclass of a class defined by the Library is deemed a mode
-of using an interface provided by the Library.
-
- A "Combined Work" is a work produced by combining or linking an
-Application with the Library. The particular version of the Library
-with which the Combined Work was made is also called the "Linked
-Version".
-
- The "Minimal Corresponding Source" for a Combined Work means the
-Corresponding Source for the Combined Work, excluding any source code
-for portions of the Combined Work that, considered in isolation, are
-based on the Application, and not on the Linked Version.
-
- The "Corresponding Application Code" for a Combined Work means the
-object code and/or source code for the Application, including any data
-and utility programs needed for reproducing the Combined Work from the
-Application, but excluding the System Libraries of the Combined Work.
-
- 1. Exception to Section 3 of the GNU GPL.
-
- You may convey a covered work under sections 3 and 4 of this License
-without being bound by section 3 of the GNU GPL.
-
- 2. Conveying Modified Versions.
-
- If you modify a copy of the Library, and, in your modifications, a
-facility refers to a function or data to be supplied by an Application
-that uses the facility (other than as an argument passed when the
-facility is invoked), then you may convey a copy of the modified
-version:
-
- a) under this License, provided that you make a good faith effort to
- ensure that, in the event an Application does not supply the
- function or data, the facility still operates, and performs
- whatever part of its purpose remains meaningful, or
-
- b) under the GNU GPL, with none of the additional permissions of
- this License applicable to that copy.
-
- 3. Object Code Incorporating Material from Library Header Files.
-
- The object code form of an Application may incorporate material from
-a header file that is part of the Library. You may convey such object
-code under terms of your choice, provided that, if the incorporated
-material is not limited to numerical parameters, data structure
-layouts and accessors, or small macros, inline functions and templates
-(ten or fewer lines in length), you do both of the following:
-
- a) Give prominent notice with each copy of the object code that the
- Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the object code with a copy of the GNU GPL and this license
- document.
-
- 4. Combined Works.
-
- You may convey a Combined Work under terms of your choice that,
-taken together, effectively do not restrict modification of the
-portions of the Library contained in the Combined Work and reverse
-engineering for debugging such modifications, if you also do each of
-the following:
-
- a) Give prominent notice with each copy of the Combined Work that
- the Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the Combined Work with a copy of the GNU GPL and this license
- document.
-
- c) For a Combined Work that displays copyright notices during
- execution, include the copyright notice for the Library among
- these notices, as well as a reference directing the user to the
- copies of the GNU GPL and this license document.
-
- d) Do one of the following:
-
- 0) Convey the Minimal Corresponding Source under the terms of this
- License, and the Corresponding Application Code in a form
- suitable for, and under terms that permit, the user to
- recombine or relink the Application with a modified version of
- the Linked Version to produce a modified Combined Work, in the
- manner specified by section 6 of the GNU GPL for conveying
- Corresponding Source.
-
- 1) Use a suitable shared library mechanism for linking with the
- Library. A suitable mechanism is one that (a) uses at run time
- a copy of the Library already present on the user's computer
- system, and (b) will operate properly with a modified version
- of the Library that is interface-compatible with the Linked
- Version.
-
- e) Provide Installation Information, but only if you would otherwise
- be required to provide such information under section 6 of the
- GNU GPL, and only to the extent that such information is
- necessary to install and execute a modified version of the
- Combined Work produced by recombining or relinking the
- Application with a modified version of the Linked Version. (If
- you use option 4d0, the Installation Information must accompany
- the Minimal Corresponding Source and Corresponding Application
- Code. If you use option 4d1, you must provide the Installation
- Information in the manner specified by section 6 of the GNU GPL
- for conveying Corresponding Source.)
-
- 5. Combined Libraries.
-
- You may place library facilities that are a work based on the
-Library side by side in a single library together with other library
-facilities that are not Applications and are not covered by this
-License, and convey such a combined library under terms of your
-choice, if you do both of the following:
-
- a) Accompany the combined library with a copy of the same work based
- on the Library, uncombined with any other library facilities,
- conveyed under the terms of this License.
-
- b) Give prominent notice with the combined library that part of it
- is a work based on the Library, and explaining where to find the
- accompanying uncombined form of the same work.
-
- 6. Revised Versions of the GNU Lesser General Public License.
-
- The Free Software Foundation may publish revised and/or new versions
-of the GNU Lesser General Public License from time to time. Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Library as you received it specifies that a certain numbered version
-of the GNU Lesser General Public License "or any later version"
-applies to it, you have the option of following the terms and
-conditions either of that published version or of any later version
-published by the Free Software Foundation. If the Library as you
-received it does not specify a version number of the GNU Lesser
-General Public License, you may choose any version of the GNU Lesser
-General Public License ever published by the Free Software Foundation.
-
- If the Library as you received it specifies that a proxy can decide
-whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is
-permanent authorization for you to choose that version for the
-Library.
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/vendor/gopkg.in/yaml.v2/README.md b/vendor/gopkg.in/yaml.v2/README.md
index 7b8bd8670..1884de6a7 100644
--- a/vendor/gopkg.in/yaml.v2/README.md
+++ b/vendor/gopkg.in/yaml.v2/README.md
@@ -42,7 +42,7 @@ The package API for yaml v2 will remain stable as described in [gopkg.in](https:
License
-------
-The yaml package is licensed under the LGPL with an exception that allows it to be linked statically. Please see the LICENSE file for details.
+The yaml package is licensed under the Apache License 2.0. Please see the LICENSE file for details.
Example