summaryrefslogtreecommitdiffstats
path: root/vendor/gopkg.in/throttled/throttled.v1
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2016-05-12 15:08:58 -0400
committerChristopher Speller <crspeller@gmail.com>2016-05-12 16:37:29 -0400
commit84d2482ddbff9564c9ad75b2d30af66e3ddfd44d (patch)
tree8bfa567d2b6381f4a996ada2deff8a16aa85a3ac /vendor/gopkg.in/throttled/throttled.v1
parentd1efb66ad7b017f0fbfe6f0c20843b30f396e504 (diff)
downloadchat-84d2482ddbff9564c9ad75b2d30af66e3ddfd44d.tar.gz
chat-84d2482ddbff9564c9ad75b2d30af66e3ddfd44d.tar.bz2
chat-84d2482ddbff9564c9ad75b2d30af66e3ddfd44d.zip
Updating go depencancies. Switching to go1.6 vendoring (#2949)
Diffstat (limited to 'vendor/gopkg.in/throttled/throttled.v1')
-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/LICENSE12
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/README.md82
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/delayer.go109
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/doc.go77
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/interval.go164
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/memstats.go214
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/rate.go116
-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/redis.go85
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/throttler.go86
-rw-r--r--vendor/gopkg.in/throttled/throttled.v1/varyby.go78
15 files changed, 1166 insertions, 0 deletions
diff --git a/vendor/gopkg.in/throttled/throttled.v1/.gitignore b/vendor/gopkg.in/throttled/throttled.v1/.gitignore
new file mode 100644
index 000000000..c2a6499b4
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v1/.gitignore
@@ -0,0 +1,10 @@
+.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
new file mode 100644
index 000000000..1b2427202
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v1/.travis.yml
@@ -0,0 +1,10 @@
+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/LICENSE b/vendor/gopkg.in/throttled/throttled.v1/LICENSE
new file mode 100644
index 000000000..f9616483e
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v1/LICENSE
@@ -0,0 +1,12 @@
+Copyright (c) 2014, Martin Angers and Contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/gopkg.in/throttled/throttled.v1/README.md b/vendor/gopkg.in/throttled/throttled.v1/README.md
new file mode 100644
index 000000000..fbb4a01fc
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v1/README.md
@@ -0,0 +1,82 @@
+# 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/delayer.go b/vendor/gopkg.in/throttled/throttled.v1/delayer.go
new file mode 100644
index 000000000..e62ec9e86
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v1/delayer.go
@@ -0,0 +1,109 @@
+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/doc.go b/vendor/gopkg.in/throttled/throttled.v1/doc.go
new file mode 100644
index 000000000..acf5213b0
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v1/doc.go
@@ -0,0 +1,77 @@
+// 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
diff --git a/vendor/gopkg.in/throttled/throttled.v1/interval.go b/vendor/gopkg.in/throttled/throttled.v1/interval.go
new file mode 100644
index 000000000..628a5593e
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v1/interval.go
@@ -0,0 +1,164 @@
+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/memstats.go b/vendor/gopkg.in/throttled/throttled.v1/memstats.go
new file mode 100644
index 000000000..bd2765630
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v1/memstats.go
@@ -0,0 +1,214 @@
+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/rate.go b/vendor/gopkg.in/throttled/throttled.v1/rate.go
new file mode 100644
index 000000000..d7a7de6d7
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v1/rate.go
@@ -0,0 +1,116 @@
+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/store.go b/vendor/gopkg.in/throttled/throttled.v1/store.go
new file mode 100644
index 000000000..760fe2b69
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v1/store.go
@@ -0,0 +1,31 @@
+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
new file mode 100644
index 000000000..adb4618d3
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v1/store/doc.go
@@ -0,0 +1,2 @@
+// Package store offers a memory-based and a Redis-based throttled.Store implementation.
+package store
diff --git a/vendor/gopkg.in/throttled/throttled.v1/store/mem.go b/vendor/gopkg.in/throttled/throttled.v1/store/mem.go
new file mode 100644
index 000000000..22d200e8d
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v1/store/mem.go
@@ -0,0 +1,90 @@
+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/redis.go b/vendor/gopkg.in/throttled/throttled.v1/store/redis.go
new file mode 100644
index 000000000..b089f9f4e
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v1/store/redis.go
@@ -0,0 +1,85 @@
+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/throttler.go b/vendor/gopkg.in/throttled/throttled.v1/throttler.go
new file mode 100644
index 000000000..06da13051
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v1/throttler.go
@@ -0,0 +1,86 @@
+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.v1/varyby.go b/vendor/gopkg.in/throttled/throttled.v1/varyby.go
new file mode 100644
index 000000000..3b2cdb011
--- /dev/null
+++ b/vendor/gopkg.in/throttled/throttled.v1/varyby.go
@@ -0,0 +1,78 @@
+package throttled
+
+import (
+ "bytes"
+ "net/http"
+ "strings"
+)
+
+// VaryBy defines the criteria to use to group requests.
+type VaryBy struct {
+ // Vary by the RemoteAddr as specified by the net/http.Request field.
+ RemoteAddr bool
+
+ // Vary by the HTTP Method as specified by the net/http.Request field.
+ Method bool
+
+ // Vary by the URL's Path as specified by the Path field of the net/http.Request
+ // URL field.
+ Path bool
+
+ // Vary by this list of header names, read from the net/http.Request Header field.
+ Headers []string
+
+ // Vary by this list of parameters, read from the net/http.Request FormValue method.
+ Params []string
+
+ // Vary by this list of cookie names, read from the net/http.Request Cookie method.
+ Cookies []string
+
+ // Use this separator string to concatenate the various criteria of the VaryBy struct.
+ // Defaults to a newline character if empty (\n).
+ Separator string
+
+ // 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
+}
+
+// Key returns the key for this request based on the criteria defined by the VaryBy struct.
+func (vb *VaryBy) Key(r *http.Request) string {
+ var buf bytes.Buffer
+
+ if vb == nil {
+ return "" // Special case for no vary-by option
+ }
+ if vb.Custom != nil {
+ // A custom key generator is specified
+ return vb.Custom(r)
+ }
+ sep := vb.Separator
+ if sep == "" {
+ sep = "\n" // Separator defaults to newline
+ }
+ if vb.RemoteAddr {
+ buf.WriteString(strings.ToLower(r.RemoteAddr) + sep)
+ }
+ if vb.Method {
+ buf.WriteString(strings.ToLower(r.Method) + sep)
+ }
+ for _, h := range vb.Headers {
+ buf.WriteString(strings.ToLower(r.Header.Get(h)) + sep)
+ }
+ if vb.Path {
+ buf.WriteString(r.URL.Path + sep)
+ }
+ for _, p := range vb.Params {
+ buf.WriteString(r.FormValue(p) + sep)
+ }
+ for _, c := range vb.Cookies {
+ ck, err := r.Cookie(c)
+ if err == nil {
+ buf.WriteString(ck.Value)
+ }
+ buf.WriteString(sep) // Write the separator anyway, whether or not the cookie exists
+ }
+ return buf.String()
+}