diff --git a/proxy/proxy.go b/proxy/proxy.go index 25973bd530d792439eda0087a19108ab4d656de8..d379ed6d2aebe46d958e4e21e18d2c7956c31440 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -2,7 +2,6 @@ package proxy import ( "crypto/tls" - "crypto/x509" "errors" "fmt" "io/ioutil" @@ -10,6 +9,7 @@ import ( "net/http/httputil" "net/url" + "git.autistici.org/ai3/go-common/clientutil" "github.com/gorilla/mux" "git.autistici.org/id/go-sso/httpsso" @@ -18,10 +18,9 @@ import ( // Backend defines a single-host HTTP proxy to a set of upstream // backends. type Backend struct { - Host string `yaml:"host"` - Upstream []string `yaml:"upstream"` - ClientTLSConfig *TLSConfig `yaml:"client_tls"` - //ServerTLSConfig *TLSConfig `yaml:"server_tls"` + Host string `yaml:"host"` + Upstream []string `yaml:"upstream"` + ClientTLSConfig *clientutil.TLSClientConfig `yaml:"client_tls"` AllowedGroups []string `yaml:"allowed_groups"` } @@ -42,51 +41,17 @@ func (b *Backend) newHandler(ssow *httpsso.SSOWrapper) (http.Handler, error) { var tlsConfig *tls.Config if b.ClientTLSConfig != nil { var err error - tlsConfig, err = b.ClientTLSConfig.toClientConfig() + tlsConfig, err = b.ClientTLSConfig.TLSConfig() if err != nil { return nil, err } } - proxy.Transport = newTransport(b.Upstream, tlsConfig) + proxy.Transport = clientutil.NewTransport(b.Upstream, tlsConfig, nil) h := ssow.Wrap(proxy, b.Host+"/", b.AllowedGroups) return h, nil } -// TLSConfig defines the TLS parameters for a client connection. -type TLSConfig struct { - Cert string `yaml:"cert"` - Key string `yaml:"key"` - CA string `yaml:"ca"` -} - -func (c *TLSConfig) toClientConfig() (*tls.Config, error) { - cert, err := tls.LoadX509KeyPair(c.Cert, c.Key) - if err != nil { - return nil, err - } - - cas, err := loadCA(c.CA) - if err != nil { - return nil, err - } - - return &tls.Config{ - Certificates: []tls.Certificate{cert}, - RootCAs: cas, - }, nil -} - -func loadCA(path string) (*x509.CertPool, error) { - data, err := ioutil.ReadFile(path) - if err != nil { - return nil, err - } - cas := x509.NewCertPool() - cas.AppendCertsFromPEM(data) - return cas, nil -} - // func buildServerTLSConfig(config *Configuration) (*tls.Config, error) { // var certs []tls.Certificate // for _, b := range config.Backends { diff --git a/proxy/transport.go b/proxy/transport.go deleted file mode 100644 index 38da38300762a10caddbe27ce27702954454dcc5..0000000000000000000000000000000000000000 --- a/proxy/transport.go +++ /dev/null @@ -1,128 +0,0 @@ -package proxy - -import ( - "crypto/tls" - "errors" - "log" - "math/rand" - "net" - "net/http" - "sort" - "sync" -) - -func resolveIPs(hosts []string) []string { - var resolved []string - for _, hostport := range hosts { - host, port, err := net.SplitHostPort(hostport) - if err != nil { - log.Printf("error parsing %s: %v", hostport, err) - continue - } - hostIPs, err := net.LookupIP(host) - if err != nil { - log.Printf("error resolving %s: %v", host, err) - continue - } - for _, ip := range hostIPs { - resolved = append(resolved, net.JoinHostPort(ip.String(), port)) - } - } - return resolved -} - -type balancer struct { - mx sync.Mutex - ips []string - errs []uint64 -} - -func (b *balancer) incrError(index int) { - b.mx.Lock() - b.errs[index]++ - b.mx.Unlock() -} - -func (b *balancer) dial(network, addr string) (net.Conn, error) { - ips, err := b.pickIPs() - if err != nil { - return nil, err - } - for _, s := range ips { - conn, err := net.Dial(network, s.ip) - if err == nil { - return conn, nil - } - log.Printf("error connecting to %s: %v", s.ip, err) - b.incrError(s.index) - } - return nil, errors.New("all upstream connections failed") -} - -type ipScore struct { - ip string - score int - index int -} - -type ipScoreList []ipScore - -func (l ipScoreList) Len() int { return len(l) } -func (l ipScoreList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } -func (l ipScoreList) Less(i, j int) bool { return l[i].score < l[j].score } - -func shuffleScores(scores []ipScore) { - for i, j := range rand.Perm(len(scores)) { - scores[i], scores[j] = scores[j], scores[i] - } -} - -const minErrs = 3 - -func (b *balancer) pickIPs() ([]ipScore, error) { - b.mx.Lock() - scores := make([]ipScore, len(b.ips)) - for i, ip := range b.ips { - score := 1 - if b.errs[i] > minErrs { - score *= 10 - } - scores[i] = ipScore{ip: ip, score: score, index: i} - } - b.mx.Unlock() - - sort.Sort(ipScoreList(scores)) - - // Iterate through the sorted list, shuffling groups of - // elements that have identical scores. - curScore := scores[0].score - head := 0 - for i := 1; i < len(scores); i++ { - if scores[i].score != curScore { - group := scores[head : i+1] - if len(group) > 1 { - shuffleScores(group) - } - head = i + 1 - } - } - group := scores[head:] - if len(group) > 1 { - shuffleScores(group) - } - - return scores, nil -} - -func newTransport(backends []string, tlsConf *tls.Config) http.RoundTripper { - ips := resolveIPs(backends) - b := &balancer{ - ips: ips, - errs: make([]uint64, len(ips)), - } - - return &http.Transport{ - Dial: b.dial, - TLSClientConfig: tlsConf, - } -} diff --git a/vendor/git.autistici.org/ai3/go-common/clientutil/retry.go b/vendor/git.autistici.org/ai3/go-common/clientutil/retry.go new file mode 100644 index 0000000000000000000000000000000000000000..ae5159f1cc0b053d4ae54d6c7c8d2b68834b7bda --- /dev/null +++ b/vendor/git.autistici.org/ai3/go-common/clientutil/retry.go @@ -0,0 +1,61 @@ +package clientutil + +import ( + "errors" + "net" + "net/http" + "time" + + "github.com/cenkalti/backoff" +) + +// NewExponentialBackOff creates a backoff.ExponentialBackOff object +// with our own default values. +func NewExponentialBackOff() *backoff.ExponentialBackOff { + b := backoff.NewExponentialBackOff() + b.InitialInterval = 100 * time.Millisecond + //b.Multiplier = 1.4142 + return b +} + +// Retry operation op until it succeeds according to the backoff policy b. +func Retry(op backoff.Operation, b backoff.BackOff) error { + innerOp := func() error { + err := op() + if err == nil { + return err + } + if netErr, ok := err.(net.Error); ok && netErr.Temporary() { + return err + } + return backoff.Permanent(err) + } + return backoff.Retry(innerOp, b) +} + +var errHTTPBackOff = errors.New("http status 503") + +// RetryHTTPDo retries an HTTP request until it succeeds, according to +// the backoff policy b. It will retry on temporary network errors and +// upon receiving specific throttling HTTP errors (currently just +// status code 503). +func RetryHTTPDo(client *http.Client, req *http.Request, b backoff.BackOff) (*http.Response, error) { + var resp *http.Response + op := func() error { + // Clear up previous response if set. + if resp != nil { + resp.Body.Close() + } + + var err error + resp, err = client.Do(req) + if err == nil && resp.StatusCode == 503 { + resp.Body.Close() + return errHTTPBackOff + } + return err + } + + err := Retry(op, b) + return resp, err +} diff --git a/vendor/git.autistici.org/ai3/go-common/clientutil/tls.go b/vendor/git.autistici.org/ai3/go-common/clientutil/tls.go new file mode 100644 index 0000000000000000000000000000000000000000..6eb0749eb78cd75cedb4168626ff67536387e9de --- /dev/null +++ b/vendor/git.autistici.org/ai3/go-common/clientutil/tls.go @@ -0,0 +1,37 @@ +package clientutil + +import ( + "crypto/tls" + + common "git.autistici.org/ai3/go-common" +) + +// TLSClientConfig defines the TLS parameters for a client connection +// that should use a client X509 certificate for authentication. +type TLSClientConfig struct { + Cert string `yaml:"cert"` + Key string `yaml:"key"` + CA string `yaml:"ca"` +} + +// TLSConfig returns a tls.Config object with the current configuration. +func (c *TLSClientConfig) TLSConfig() (*tls.Config, error) { + cert, err := tls.LoadX509KeyPair(c.Cert, c.Key) + if err != nil { + return nil, err + } + tlsConf := &tls.Config{ + Certificates: []tls.Certificate{cert}, + } + + if c.CA != "" { + cas, err := common.LoadCA(c.CA) + if err != nil { + return nil, err + } + tlsConf.RootCAs = cas + } + tlsConf.BuildNameToCertificate() + + return tlsConf, nil +} diff --git a/vendor/git.autistici.org/ai3/go-common/clientutil/transport.go b/vendor/git.autistici.org/ai3/go-common/clientutil/transport.go new file mode 100644 index 0000000000000000000000000000000000000000..3894ca121444af667d67c07d9e4c8292c9daee74 --- /dev/null +++ b/vendor/git.autistici.org/ai3/go-common/clientutil/transport.go @@ -0,0 +1,169 @@ +package clientutil + +import ( + "context" + "crypto/tls" + "errors" + "log" + "net" + "net/http" + "sync" + "time" +) + +var errAllBackendsFailed = errors.New("all backends failed") + +type dnsResolver struct{} + +func (r *dnsResolver) ResolveIPs(hosts []string) []string { + var resolved []string + for _, hostport := range hosts { + host, port, err := net.SplitHostPort(hostport) + if err != nil { + log.Printf("error parsing %s: %v", hostport, err) + continue + } + hostIPs, err := net.LookupIP(host) + if err != nil { + log.Printf("error resolving %s: %v", host, err) + continue + } + for _, ip := range hostIPs { + resolved = append(resolved, net.JoinHostPort(ip.String(), port)) + } + } + return resolved +} + +var defaultResolver = &dnsResolver{} + +type resolver interface { + ResolveIPs([]string) []string +} + +// Balancer for HTTP connections. It will round-robin across available +// backends, trying to avoid ones that are erroring out, until one +// succeeds or they all fail. +// +// This object should not be used for load balancing of individual +// HTTP requests: once a new connection is established, requests will +// be sent over it until it errors out. It's meant to provide a +// *reliable* connection to a set of equivalent backends for HA +// purposes. +type balancer struct { + hosts []string + resolver resolver + stop chan bool + + // List of currently valid (or untested) backends, and ones + // that errored out at least once. + mx sync.Mutex + addrs []string + ok map[string]bool +} + +var backendUpdateInterval = 60 * time.Second + +// Periodically update the list of available backends. +func (b *balancer) updateProc() { + tick := time.NewTicker(backendUpdateInterval) + for { + select { + case <-b.stop: + return + case <-tick.C: + resolved := b.resolver.ResolveIPs(b.hosts) + if len(resolved) > 0 { + b.mx.Lock() + b.addrs = resolved + b.mx.Unlock() + } + } + } +} + +// Returns a list of all available backends, split into "good ones" +// (no errors seen since last successful connection) and "bad ones". +func (b *balancer) getBackends() ([]string, []string) { + b.mx.Lock() + defer b.mx.Unlock() + + var good, bad []string + for _, addr := range b.addrs { + if ok := b.ok[addr]; ok { + good = append(good, addr) + } else { + bad = append(bad, addr) + } + } + return good, bad +} + +func (b *balancer) notify(addr string, ok bool) { + b.mx.Lock() + b.ok[addr] = ok + b.mx.Unlock() +} + +func netDialContext(ctx context.Context, network, addr string) (net.Conn, error) { + timeout := 30 * time.Second + // Go < 1.9 does not have net.DialContext, reimplement it in + // terms of net.DialTimeout. + if deadline, ok := ctx.Deadline(); ok { + timeout = time.Until(deadline) + } + return net.DialTimeout(network, addr, timeout) +} + +func (b *balancer) dial(ctx context.Context, network, addr string) (net.Conn, error) { + // Start by attempting a connection on 'good' targets. + good, bad := b.getBackends() + + for _, addr := range good { + // Go < 1.9 does not have DialContext, deal with it + conn, err := netDialContext(ctx, network, addr) + if err == nil { + return conn, nil + } else if err == context.Canceled { + return nil, err + } + b.notify(addr, false) + } + + for _, addr := range bad { + conn, err := netDialContext(ctx, network, addr) + if err == nil { + b.notify(addr, true) + return conn, nil + } else if err == context.Canceled { + return nil, err + } + } + + return nil, errAllBackendsFailed +} + +// NewTransport returns a suitably configured http.RoundTripper that +// talks to a specific backend service. It performs discovery of +// available backends via DNS (using A or AAAA record lookups), tries +// to route traffic away from faulty backends. +// +// It will periodically attempt to rediscover new backends. +func NewTransport(backends []string, tlsConf *tls.Config, resolver resolver) http.RoundTripper { + if resolver == nil { + resolver = defaultResolver + } + addrs := resolver.ResolveIPs(backends) + b := &balancer{ + hosts: backends, + resolver: resolver, + addrs: addrs, + ok: make(map[string]bool), + } + go b.updateProc() + + return &http.Transport{ + DialContext: b.dial, + TLSClientConfig: tlsConf, + } +} diff --git a/vendor/git.autistici.org/ai3/go-common/misc.go b/vendor/git.autistici.org/ai3/go-common/misc.go new file mode 100644 index 0000000000000000000000000000000000000000..582af3f7e148988ff382d50e9a2e0e1e9e0894d2 --- /dev/null +++ b/vendor/git.autistici.org/ai3/go-common/misc.go @@ -0,0 +1,17 @@ +package common + +import ( + "crypto/x509" + "io/ioutil" +) + +// LoadCA loads a file containing CA certificates into a x509.CertPool. +func LoadCA(path string) (*x509.CertPool, error) { + data, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + cas := x509.NewCertPool() + cas.AppendCertsFromPEM(data) + return cas, nil +} diff --git a/vendor/github.com/cenkalti/backoff/LICENSE b/vendor/github.com/cenkalti/backoff/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..89b8179965581339743b5edd9e18c1fcc778b56d --- /dev/null +++ b/vendor/github.com/cenkalti/backoff/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014 Cenk Altı + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/cenkalti/backoff/README.md b/vendor/github.com/cenkalti/backoff/README.md new file mode 100644 index 0000000000000000000000000000000000000000..13b347fb95179f97b99d9f9224211e0159ed1c8c --- /dev/null +++ b/vendor/github.com/cenkalti/backoff/README.md @@ -0,0 +1,30 @@ +# Exponential Backoff [![GoDoc][godoc image]][godoc] [![Build Status][travis image]][travis] [![Coverage Status][coveralls image]][coveralls] + +This is a Go port of the exponential backoff algorithm from [Google's HTTP Client Library for Java][google-http-java-client]. + +[Exponential backoff][exponential backoff wiki] +is an algorithm that uses feedback to multiplicatively decrease the rate of some process, +in order to gradually find an acceptable rate. +The retries exponentially increase and stop increasing when a certain threshold is met. + +## Usage + +See https://godoc.org/github.com/cenkalti/backoff#pkg-examples + +## Contributing + +* I would like to keep this library as small as possible. +* Please don't send a PR without opening an issue and discussing it first. +* If proposed change is not a common use case, I will probably not accept it. + +[godoc]: https://godoc.org/github.com/cenkalti/backoff +[godoc image]: https://godoc.org/github.com/cenkalti/backoff?status.png +[travis]: https://travis-ci.org/cenkalti/backoff +[travis image]: https://travis-ci.org/cenkalti/backoff.png?branch=master +[coveralls]: https://coveralls.io/github/cenkalti/backoff?branch=master +[coveralls image]: https://coveralls.io/repos/github/cenkalti/backoff/badge.svg?branch=master + +[google-http-java-client]: https://github.com/google/google-http-java-client +[exponential backoff wiki]: http://en.wikipedia.org/wiki/Exponential_backoff + +[advanced example]: https://godoc.org/github.com/cenkalti/backoff#example_ diff --git a/vendor/github.com/cenkalti/backoff/backoff.go b/vendor/github.com/cenkalti/backoff/backoff.go new file mode 100644 index 0000000000000000000000000000000000000000..3676ee405d87b3dc7b675f3c93f719a2abca12d2 --- /dev/null +++ b/vendor/github.com/cenkalti/backoff/backoff.go @@ -0,0 +1,66 @@ +// Package backoff implements backoff algorithms for retrying operations. +// +// Use Retry function for retrying operations that may fail. +// If Retry does not meet your needs, +// copy/paste the function into your project and modify as you wish. +// +// There is also Ticker type similar to time.Ticker. +// You can use it if you need to work with channels. +// +// See Examples section below for usage examples. +package backoff + +import "time" + +// BackOff is a backoff policy for retrying an operation. +type BackOff interface { + // NextBackOff returns the duration to wait before retrying the operation, + // or backoff. Stop to indicate that no more retries should be made. + // + // Example usage: + // + // duration := backoff.NextBackOff(); + // if (duration == backoff.Stop) { + // // Do not retry operation. + // } else { + // // Sleep for duration and retry operation. + // } + // + NextBackOff() time.Duration + + // Reset to initial state. + Reset() +} + +// Stop indicates that no more retries should be made for use in NextBackOff(). +const Stop time.Duration = -1 + +// ZeroBackOff is a fixed backoff policy whose backoff time is always zero, +// meaning that the operation is retried immediately without waiting, indefinitely. +type ZeroBackOff struct{} + +func (b *ZeroBackOff) Reset() {} + +func (b *ZeroBackOff) NextBackOff() time.Duration { return 0 } + +// StopBackOff is a fixed backoff policy that always returns backoff.Stop for +// NextBackOff(), meaning that the operation should never be retried. +type StopBackOff struct{} + +func (b *StopBackOff) Reset() {} + +func (b *StopBackOff) NextBackOff() time.Duration { return Stop } + +// ConstantBackOff is a backoff policy that always returns the same backoff delay. +// This is in contrast to an exponential backoff policy, +// which returns a delay that grows longer as you call NextBackOff() over and over again. +type ConstantBackOff struct { + Interval time.Duration +} + +func (b *ConstantBackOff) Reset() {} +func (b *ConstantBackOff) NextBackOff() time.Duration { return b.Interval } + +func NewConstantBackOff(d time.Duration) *ConstantBackOff { + return &ConstantBackOff{Interval: d} +} diff --git a/vendor/github.com/cenkalti/backoff/context.go b/vendor/github.com/cenkalti/backoff/context.go new file mode 100644 index 0000000000000000000000000000000000000000..5d157092544fdbc8225327191883c46876faab94 --- /dev/null +++ b/vendor/github.com/cenkalti/backoff/context.go @@ -0,0 +1,60 @@ +package backoff + +import ( + "time" + + "golang.org/x/net/context" +) + +// BackOffContext is a backoff policy that stops retrying after the context +// is canceled. +type BackOffContext interface { + BackOff + Context() context.Context +} + +type backOffContext struct { + BackOff + ctx context.Context +} + +// WithContext returns a BackOffContext with context ctx +// +// ctx must not be nil +func WithContext(b BackOff, ctx context.Context) BackOffContext { + if ctx == nil { + panic("nil context") + } + + if b, ok := b.(*backOffContext); ok { + return &backOffContext{ + BackOff: b.BackOff, + ctx: ctx, + } + } + + return &backOffContext{ + BackOff: b, + ctx: ctx, + } +} + +func ensureContext(b BackOff) BackOffContext { + if cb, ok := b.(BackOffContext); ok { + return cb + } + return WithContext(b, context.Background()) +} + +func (b *backOffContext) Context() context.Context { + return b.ctx +} + +func (b *backOffContext) NextBackOff() time.Duration { + select { + case <-b.Context().Done(): + return Stop + default: + return b.BackOff.NextBackOff() + } +} diff --git a/vendor/github.com/cenkalti/backoff/exponential.go b/vendor/github.com/cenkalti/backoff/exponential.go new file mode 100644 index 0000000000000000000000000000000000000000..d9de15a177bf47b61964e0b149e741eec2c14d19 --- /dev/null +++ b/vendor/github.com/cenkalti/backoff/exponential.go @@ -0,0 +1,158 @@ +package backoff + +import ( + "math/rand" + "time" +) + +/* +ExponentialBackOff is a backoff implementation that increases the backoff +period for each retry attempt using a randomization function that grows exponentially. + +NextBackOff() is calculated using the following formula: + + randomized interval = + RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor]) + +In other words NextBackOff() will range between the randomization factor +percentage below and above the retry interval. + +For example, given the following parameters: + + RetryInterval = 2 + RandomizationFactor = 0.5 + Multiplier = 2 + +the actual backoff period used in the next retry attempt will range between 1 and 3 seconds, +multiplied by the exponential, that is, between 2 and 6 seconds. + +Note: MaxInterval caps the RetryInterval and not the randomized interval. + +If the time elapsed since an ExponentialBackOff instance is created goes past the +MaxElapsedTime, then the method NextBackOff() starts returning backoff.Stop. + +The elapsed time can be reset by calling Reset(). + +Example: Given the following default arguments, for 10 tries the sequence will be, +and assuming we go over the MaxElapsedTime on the 10th try: + + Request # RetryInterval (seconds) Randomized Interval (seconds) + + 1 0.5 [0.25, 0.75] + 2 0.75 [0.375, 1.125] + 3 1.125 [0.562, 1.687] + 4 1.687 [0.8435, 2.53] + 5 2.53 [1.265, 3.795] + 6 3.795 [1.897, 5.692] + 7 5.692 [2.846, 8.538] + 8 8.538 [4.269, 12.807] + 9 12.807 [6.403, 19.210] + 10 19.210 backoff.Stop + +Note: Implementation is not thread-safe. +*/ +type ExponentialBackOff struct { + InitialInterval time.Duration + RandomizationFactor float64 + Multiplier float64 + MaxInterval time.Duration + // After MaxElapsedTime the ExponentialBackOff stops. + // It never stops if MaxElapsedTime == 0. + MaxElapsedTime time.Duration + Clock Clock + + currentInterval time.Duration + startTime time.Time + random *rand.Rand +} + +// Clock is an interface that returns current time for BackOff. +type Clock interface { + Now() time.Time +} + +// Default values for ExponentialBackOff. +const ( + DefaultInitialInterval = 500 * time.Millisecond + DefaultRandomizationFactor = 0.5 + DefaultMultiplier = 1.5 + DefaultMaxInterval = 60 * time.Second + DefaultMaxElapsedTime = 15 * time.Minute +) + +// NewExponentialBackOff creates an instance of ExponentialBackOff using default values. +func NewExponentialBackOff() *ExponentialBackOff { + b := &ExponentialBackOff{ + InitialInterval: DefaultInitialInterval, + RandomizationFactor: DefaultRandomizationFactor, + Multiplier: DefaultMultiplier, + MaxInterval: DefaultMaxInterval, + MaxElapsedTime: DefaultMaxElapsedTime, + Clock: SystemClock, + random: rand.New(rand.NewSource(time.Now().UnixNano())), + } + b.Reset() + return b +} + +type systemClock struct{} + +func (t systemClock) Now() time.Time { + return time.Now() +} + +// SystemClock implements Clock interface that uses time.Now(). +var SystemClock = systemClock{} + +// Reset the interval back to the initial retry interval and restarts the timer. +func (b *ExponentialBackOff) Reset() { + b.currentInterval = b.InitialInterval + b.startTime = b.Clock.Now() +} + +// NextBackOff calculates the next backoff interval using the formula: +// Randomized interval = RetryInterval +/- (RandomizationFactor * RetryInterval) +func (b *ExponentialBackOff) NextBackOff() time.Duration { + // Make sure we have not gone over the maximum elapsed time. + if b.MaxElapsedTime != 0 && b.GetElapsedTime() > b.MaxElapsedTime { + return Stop + } + defer b.incrementCurrentInterval() + if b.random == nil { + b.random = rand.New(rand.NewSource(time.Now().UnixNano())) + } + return getRandomValueFromInterval(b.RandomizationFactor, b.random.Float64(), b.currentInterval) +} + +// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance +// is created and is reset when Reset() is called. +// +// The elapsed time is computed using time.Now().UnixNano(). It is +// safe to call even while the backoff policy is used by a running +// ticker. +func (b *ExponentialBackOff) GetElapsedTime() time.Duration { + return b.Clock.Now().Sub(b.startTime) +} + +// Increments the current interval by multiplying it with the multiplier. +func (b *ExponentialBackOff) incrementCurrentInterval() { + // Check for overflow, if overflow is detected set the current interval to the max interval. + if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier { + b.currentInterval = b.MaxInterval + } else { + b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier) + } +} + +// Returns a random value from the following interval: +// [randomizationFactor * currentInterval, randomizationFactor * currentInterval]. +func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration { + var delta = randomizationFactor * float64(currentInterval) + var minInterval = float64(currentInterval) - delta + var maxInterval = float64(currentInterval) + delta + + // Get a random value from the range [minInterval, maxInterval]. + // The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then + // we want a 33% chance for selecting either 1, 2 or 3. + return time.Duration(minInterval + (random * (maxInterval - minInterval + 1))) +} diff --git a/vendor/github.com/cenkalti/backoff/retry.go b/vendor/github.com/cenkalti/backoff/retry.go new file mode 100644 index 0000000000000000000000000000000000000000..5dbd825b5c8b5761dcb613d198ea9662b761b1a7 --- /dev/null +++ b/vendor/github.com/cenkalti/backoff/retry.go @@ -0,0 +1,78 @@ +package backoff + +import "time" + +// An Operation is executing by Retry() or RetryNotify(). +// The operation will be retried using a backoff policy if it returns an error. +type Operation func() error + +// Notify is a notify-on-error function. It receives an operation error and +// backoff delay if the operation failed (with an error). +// +// NOTE that if the backoff policy stated to stop retrying, +// the notify function isn't called. +type Notify func(error, time.Duration) + +// Retry the operation o until it does not return error or BackOff stops. +// o is guaranteed to be run at least once. +// It is the caller's responsibility to reset b after Retry returns. +// +// If o returns a *PermanentError, the operation is not retried, and the +// wrapped error is returned. +// +// Retry sleeps the goroutine for the duration returned by BackOff after a +// failed operation returns. +func Retry(o Operation, b BackOff) error { return RetryNotify(o, b, nil) } + +// RetryNotify calls notify function with the error and wait duration +// for each failed attempt before sleep. +func RetryNotify(operation Operation, b BackOff, notify Notify) error { + var err error + var next time.Duration + + cb := ensureContext(b) + + b.Reset() + for { + if err = operation(); err == nil { + return nil + } + + if permanent, ok := err.(*PermanentError); ok { + return permanent.Err + } + + if next = b.NextBackOff(); next == Stop { + return err + } + + if notify != nil { + notify(err, next) + } + + t := time.NewTimer(next) + + select { + case <-cb.Context().Done(): + t.Stop() + return err + case <-t.C: + } + } +} + +// PermanentError signals that the operation should not be retried. +type PermanentError struct { + Err error +} + +func (e *PermanentError) Error() string { + return e.Err.Error() +} + +// Permanent wraps the given err in a *PermanentError. +func Permanent(err error) *PermanentError { + return &PermanentError{ + Err: err, + } +} diff --git a/vendor/github.com/cenkalti/backoff/ticker.go b/vendor/github.com/cenkalti/backoff/ticker.go new file mode 100644 index 0000000000000000000000000000000000000000..e742512fd3a83f04e560dd8438e1048366fc9821 --- /dev/null +++ b/vendor/github.com/cenkalti/backoff/ticker.go @@ -0,0 +1,84 @@ +package backoff + +import ( + "runtime" + "sync" + "time" +) + +// Ticker holds a channel that delivers `ticks' of a clock at times reported by a BackOff. +// +// Ticks will continue to arrive when the previous operation is still running, +// so operations that take a while to fail could run in quick succession. +type Ticker struct { + C <-chan time.Time + c chan time.Time + b BackOffContext + stop chan struct{} + stopOnce sync.Once +} + +// NewTicker returns a new Ticker containing a channel that will send +// the time at times specified by the BackOff argument. Ticker is +// guaranteed to tick at least once. The channel is closed when Stop +// method is called or BackOff stops. It is not safe to manipulate the +// provided backoff policy (notably calling NextBackOff or Reset) +// while the ticker is running. +func NewTicker(b BackOff) *Ticker { + c := make(chan time.Time) + t := &Ticker{ + C: c, + c: c, + b: ensureContext(b), + stop: make(chan struct{}), + } + t.b.Reset() + go t.run() + runtime.SetFinalizer(t, (*Ticker).Stop) + return t +} + +// Stop turns off a ticker. After Stop, no more ticks will be sent. +func (t *Ticker) Stop() { + t.stopOnce.Do(func() { close(t.stop) }) +} + +func (t *Ticker) run() { + c := t.c + defer close(c) + + // Ticker is guaranteed to tick at least once. + afterC := t.send(time.Now()) + + for { + if afterC == nil { + return + } + + select { + case tick := <-afterC: + afterC = t.send(tick) + case <-t.stop: + t.c = nil // Prevent future ticks from being sent to the channel. + return + case <-t.b.Context().Done(): + return + } + } +} + +func (t *Ticker) send(tick time.Time) <-chan time.Time { + select { + case t.c <- tick: + case <-t.stop: + return nil + } + + next := t.b.NextBackOff() + if next == Stop { + t.Stop() + return nil + } + + return time.After(next) +} diff --git a/vendor/github.com/cenkalti/backoff/tries.go b/vendor/github.com/cenkalti/backoff/tries.go new file mode 100644 index 0000000000000000000000000000000000000000..d2da7308b6aa99813b1a895f0c6f49b1af6764a2 --- /dev/null +++ b/vendor/github.com/cenkalti/backoff/tries.go @@ -0,0 +1,35 @@ +package backoff + +import "time" + +/* +WithMaxTries creates a wrapper around another BackOff, which will +return Stop if NextBackOff() has been called too many times since +the last time Reset() was called + +Note: Implementation is not thread-safe. +*/ +func WithMaxTries(b BackOff, max uint64) BackOff { + return &backOffTries{delegate: b, maxTries: max} +} + +type backOffTries struct { + delegate BackOff + maxTries uint64 + numTries uint64 +} + +func (b *backOffTries) NextBackOff() time.Duration { + if b.maxTries > 0 { + if b.maxTries <= b.numTries { + return Stop + } + b.numTries++ + } + return b.delegate.NextBackOff() +} + +func (b *backOffTries) Reset() { + b.numTries = 0 + b.delegate.Reset() +} diff --git a/vendor/golang.org/x/net/LICENSE b/vendor/golang.org/x/net/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..6a66aea5eafe0ca6a688840c47219556c552488e --- /dev/null +++ b/vendor/golang.org/x/net/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/net/PATENTS b/vendor/golang.org/x/net/PATENTS new file mode 100644 index 0000000000000000000000000000000000000000..733099041f84fa1e58611ab2e11af51c1f26d1d2 --- /dev/null +++ b/vendor/golang.org/x/net/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/net/context/context.go b/vendor/golang.org/x/net/context/context.go new file mode 100644 index 0000000000000000000000000000000000000000..d3681ab428c861b3da7600573612d62aa22a5098 --- /dev/null +++ b/vendor/golang.org/x/net/context/context.go @@ -0,0 +1,54 @@ +// Copyright 2014 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. + +// Package context defines the Context type, which carries deadlines, +// cancelation signals, and other request-scoped values across API boundaries +// and between processes. +// +// Incoming requests to a server should create a Context, and outgoing calls to +// servers should accept a Context. The chain of function calls between must +// propagate the Context, optionally replacing it with a modified copy created +// using WithDeadline, WithTimeout, WithCancel, or WithValue. +// +// Programs that use Contexts should follow these rules to keep interfaces +// consistent across packages and enable static analysis tools to check context +// propagation: +// +// Do not store Contexts inside a struct type; instead, pass a Context +// explicitly to each function that needs it. The Context should be the first +// parameter, typically named ctx: +// +// func DoSomething(ctx context.Context, arg Arg) error { +// // ... use ctx ... +// } +// +// Do not pass a nil Context, even if a function permits it. Pass context.TODO +// if you are unsure about which Context to use. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +// +// The same Context may be passed to functions running in different goroutines; +// Contexts are safe for simultaneous use by multiple goroutines. +// +// See http://blog.golang.org/context for example code for a server that uses +// Contexts. +package context // import "golang.org/x/net/context" + +// Background returns a non-nil, empty Context. It is never canceled, has no +// values, and has no deadline. It is typically used by the main function, +// initialization, and tests, and as the top-level Context for incoming +// requests. +func Background() Context { + return background +} + +// TODO returns a non-nil, empty Context. Code should use context.TODO when +// it's unclear which Context to use or it is not yet available (because the +// surrounding function has not yet been extended to accept a Context +// parameter). TODO is recognized by static analysis tools that determine +// whether Contexts are propagated correctly in a program. +func TODO() Context { + return todo +} diff --git a/vendor/golang.org/x/net/context/go17.go b/vendor/golang.org/x/net/context/go17.go new file mode 100644 index 0000000000000000000000000000000000000000..d20f52b7de93f81675cb405bd4309af9ee61d2d1 --- /dev/null +++ b/vendor/golang.org/x/net/context/go17.go @@ -0,0 +1,72 @@ +// Copyright 2016 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. + +// +build go1.7 + +package context + +import ( + "context" // standard library's context, as of Go 1.7 + "time" +) + +var ( + todo = context.TODO() + background = context.Background() +) + +// Canceled is the error returned by Context.Err when the context is canceled. +var Canceled = context.Canceled + +// DeadlineExceeded is the error returned by Context.Err when the context's +// deadline passes. +var DeadlineExceeded = context.DeadlineExceeded + +// WithCancel returns a copy of parent with a new Done channel. The returned +// context's Done channel is closed when the returned cancel function is called +// or when the parent context's Done channel is closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { + ctx, f := context.WithCancel(parent) + return ctx, CancelFunc(f) +} + +// WithDeadline returns a copy of the parent context with the deadline adjusted +// to be no later than d. If the parent's deadline is already earlier than d, +// WithDeadline(parent, d) is semantically equivalent to parent. The returned +// context's Done channel is closed when the deadline expires, when the returned +// cancel function is called, or when the parent context's Done channel is +// closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { + ctx, f := context.WithDeadline(parent, deadline) + return ctx, CancelFunc(f) +} + +// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete: +// +// func slowOperationWithTimeout(ctx context.Context) (Result, error) { +// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) +// defer cancel() // releases resources if slowOperation completes before timeout elapses +// return slowOperation(ctx) +// } +func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { + return WithDeadline(parent, time.Now().Add(timeout)) +} + +// WithValue returns a copy of parent in which the value associated with key is +// val. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +func WithValue(parent Context, key interface{}, val interface{}) Context { + return context.WithValue(parent, key, val) +} diff --git a/vendor/golang.org/x/net/context/go19.go b/vendor/golang.org/x/net/context/go19.go new file mode 100644 index 0000000000000000000000000000000000000000..d88bd1db127dd8154ca418b459efa269197f1508 --- /dev/null +++ b/vendor/golang.org/x/net/context/go19.go @@ -0,0 +1,20 @@ +// Copyright 2017 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. + +// +build go1.9 + +package context + +import "context" // standard library's context, as of Go 1.7 + +// A Context carries a deadline, a cancelation signal, and other values across +// API boundaries. +// +// Context's methods may be called by multiple goroutines simultaneously. +type Context = context.Context + +// A CancelFunc tells an operation to abandon its work. +// A CancelFunc does not wait for the work to stop. +// After the first call, subsequent calls to a CancelFunc do nothing. +type CancelFunc = context.CancelFunc diff --git a/vendor/golang.org/x/net/context/pre_go17.go b/vendor/golang.org/x/net/context/pre_go17.go new file mode 100644 index 0000000000000000000000000000000000000000..0f35592df51885abc1eb01b2e58318590df65874 --- /dev/null +++ b/vendor/golang.org/x/net/context/pre_go17.go @@ -0,0 +1,300 @@ +// Copyright 2014 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. + +// +build !go1.7 + +package context + +import ( + "errors" + "fmt" + "sync" + "time" +) + +// An emptyCtx is never canceled, has no values, and has no deadline. It is not +// struct{}, since vars of this type must have distinct addresses. +type emptyCtx int + +func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { + return +} + +func (*emptyCtx) Done() <-chan struct{} { + return nil +} + +func (*emptyCtx) Err() error { + return nil +} + +func (*emptyCtx) Value(key interface{}) interface{} { + return nil +} + +func (e *emptyCtx) String() string { + switch e { + case background: + return "context.Background" + case todo: + return "context.TODO" + } + return "unknown empty Context" +} + +var ( + background = new(emptyCtx) + todo = new(emptyCtx) +) + +// Canceled is the error returned by Context.Err when the context is canceled. +var Canceled = errors.New("context canceled") + +// DeadlineExceeded is the error returned by Context.Err when the context's +// deadline passes. +var DeadlineExceeded = errors.New("context deadline exceeded") + +// WithCancel returns a copy of parent with a new Done channel. The returned +// context's Done channel is closed when the returned cancel function is called +// or when the parent context's Done channel is closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { + c := newCancelCtx(parent) + propagateCancel(parent, c) + return c, func() { c.cancel(true, Canceled) } +} + +// newCancelCtx returns an initialized cancelCtx. +func newCancelCtx(parent Context) *cancelCtx { + return &cancelCtx{ + Context: parent, + done: make(chan struct{}), + } +} + +// propagateCancel arranges for child to be canceled when parent is. +func propagateCancel(parent Context, child canceler) { + if parent.Done() == nil { + return // parent is never canceled + } + if p, ok := parentCancelCtx(parent); ok { + p.mu.Lock() + if p.err != nil { + // parent has already been canceled + child.cancel(false, p.err) + } else { + if p.children == nil { + p.children = make(map[canceler]bool) + } + p.children[child] = true + } + p.mu.Unlock() + } else { + go func() { + select { + case <-parent.Done(): + child.cancel(false, parent.Err()) + case <-child.Done(): + } + }() + } +} + +// parentCancelCtx follows a chain of parent references until it finds a +// *cancelCtx. This function understands how each of the concrete types in this +// package represents its parent. +func parentCancelCtx(parent Context) (*cancelCtx, bool) { + for { + switch c := parent.(type) { + case *cancelCtx: + return c, true + case *timerCtx: + return c.cancelCtx, true + case *valueCtx: + parent = c.Context + default: + return nil, false + } + } +} + +// removeChild removes a context from its parent. +func removeChild(parent Context, child canceler) { + p, ok := parentCancelCtx(parent) + if !ok { + return + } + p.mu.Lock() + if p.children != nil { + delete(p.children, child) + } + p.mu.Unlock() +} + +// A canceler is a context type that can be canceled directly. The +// implementations are *cancelCtx and *timerCtx. +type canceler interface { + cancel(removeFromParent bool, err error) + Done() <-chan struct{} +} + +// A cancelCtx can be canceled. When canceled, it also cancels any children +// that implement canceler. +type cancelCtx struct { + Context + + done chan struct{} // closed by the first cancel call. + + mu sync.Mutex + children map[canceler]bool // set to nil by the first cancel call + err error // set to non-nil by the first cancel call +} + +func (c *cancelCtx) Done() <-chan struct{} { + return c.done +} + +func (c *cancelCtx) Err() error { + c.mu.Lock() + defer c.mu.Unlock() + return c.err +} + +func (c *cancelCtx) String() string { + return fmt.Sprintf("%v.WithCancel", c.Context) +} + +// cancel closes c.done, cancels each of c's children, and, if +// removeFromParent is true, removes c from its parent's children. +func (c *cancelCtx) cancel(removeFromParent bool, err error) { + if err == nil { + panic("context: internal error: missing cancel error") + } + c.mu.Lock() + if c.err != nil { + c.mu.Unlock() + return // already canceled + } + c.err = err + close(c.done) + for child := range c.children { + // NOTE: acquiring the child's lock while holding parent's lock. + child.cancel(false, err) + } + c.children = nil + c.mu.Unlock() + + if removeFromParent { + removeChild(c.Context, c) + } +} + +// WithDeadline returns a copy of the parent context with the deadline adjusted +// to be no later than d. If the parent's deadline is already earlier than d, +// WithDeadline(parent, d) is semantically equivalent to parent. The returned +// context's Done channel is closed when the deadline expires, when the returned +// cancel function is called, or when the parent context's Done channel is +// closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { + if cur, ok := parent.Deadline(); ok && cur.Before(deadline) { + // The current deadline is already sooner than the new one. + return WithCancel(parent) + } + c := &timerCtx{ + cancelCtx: newCancelCtx(parent), + deadline: deadline, + } + propagateCancel(parent, c) + d := deadline.Sub(time.Now()) + if d <= 0 { + c.cancel(true, DeadlineExceeded) // deadline has already passed + return c, func() { c.cancel(true, Canceled) } + } + c.mu.Lock() + defer c.mu.Unlock() + if c.err == nil { + c.timer = time.AfterFunc(d, func() { + c.cancel(true, DeadlineExceeded) + }) + } + return c, func() { c.cancel(true, Canceled) } +} + +// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to +// implement Done and Err. It implements cancel by stopping its timer then +// delegating to cancelCtx.cancel. +type timerCtx struct { + *cancelCtx + timer *time.Timer // Under cancelCtx.mu. + + deadline time.Time +} + +func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { + return c.deadline, true +} + +func (c *timerCtx) String() string { + return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now())) +} + +func (c *timerCtx) cancel(removeFromParent bool, err error) { + c.cancelCtx.cancel(false, err) + if removeFromParent { + // Remove this timerCtx from its parent cancelCtx's children. + removeChild(c.cancelCtx.Context, c) + } + c.mu.Lock() + if c.timer != nil { + c.timer.Stop() + c.timer = nil + } + c.mu.Unlock() +} + +// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete: +// +// func slowOperationWithTimeout(ctx context.Context) (Result, error) { +// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) +// defer cancel() // releases resources if slowOperation completes before timeout elapses +// return slowOperation(ctx) +// } +func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { + return WithDeadline(parent, time.Now().Add(timeout)) +} + +// WithValue returns a copy of parent in which the value associated with key is +// val. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +func WithValue(parent Context, key interface{}, val interface{}) Context { + return &valueCtx{parent, key, val} +} + +// A valueCtx carries a key-value pair. It implements Value for that key and +// delegates all other calls to the embedded Context. +type valueCtx struct { + Context + key, val interface{} +} + +func (c *valueCtx) String() string { + return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val) +} + +func (c *valueCtx) Value(key interface{}) interface{} { + if c.key == key { + return c.val + } + return c.Context.Value(key) +} diff --git a/vendor/golang.org/x/net/context/pre_go19.go b/vendor/golang.org/x/net/context/pre_go19.go new file mode 100644 index 0000000000000000000000000000000000000000..b105f80be4fe2bbcb225ae4343b551fc6d851b15 --- /dev/null +++ b/vendor/golang.org/x/net/context/pre_go19.go @@ -0,0 +1,109 @@ +// Copyright 2014 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. + +// +build !go1.9 + +package context + +import "time" + +// A Context carries a deadline, a cancelation signal, and other values across +// API boundaries. +// +// Context's methods may be called by multiple goroutines simultaneously. +type Context interface { + // Deadline returns the time when work done on behalf of this context + // should be canceled. Deadline returns ok==false when no deadline is + // set. Successive calls to Deadline return the same results. + Deadline() (deadline time.Time, ok bool) + + // Done returns a channel that's closed when work done on behalf of this + // context should be canceled. Done may return nil if this context can + // never be canceled. Successive calls to Done return the same value. + // + // WithCancel arranges for Done to be closed when cancel is called; + // WithDeadline arranges for Done to be closed when the deadline + // expires; WithTimeout arranges for Done to be closed when the timeout + // elapses. + // + // Done is provided for use in select statements: + // + // // Stream generates values with DoSomething and sends them to out + // // until DoSomething returns an error or ctx.Done is closed. + // func Stream(ctx context.Context, out chan<- Value) error { + // for { + // v, err := DoSomething(ctx) + // if err != nil { + // return err + // } + // select { + // case <-ctx.Done(): + // return ctx.Err() + // case out <- v: + // } + // } + // } + // + // See http://blog.golang.org/pipelines for more examples of how to use + // a Done channel for cancelation. + Done() <-chan struct{} + + // Err returns a non-nil error value after Done is closed. Err returns + // Canceled if the context was canceled or DeadlineExceeded if the + // context's deadline passed. No other values for Err are defined. + // After Done is closed, successive calls to Err return the same value. + Err() error + + // Value returns the value associated with this context for key, or nil + // if no value is associated with key. Successive calls to Value with + // the same key returns the same result. + // + // Use context values only for request-scoped data that transits + // processes and API boundaries, not for passing optional parameters to + // functions. + // + // A key identifies a specific value in a Context. Functions that wish + // to store values in Context typically allocate a key in a global + // variable then use that key as the argument to context.WithValue and + // Context.Value. A key can be any type that supports equality; + // packages should define keys as an unexported type to avoid + // collisions. + // + // Packages that define a Context key should provide type-safe accessors + // for the values stores using that key: + // + // // Package user defines a User type that's stored in Contexts. + // package user + // + // import "golang.org/x/net/context" + // + // // User is the type of value stored in the Contexts. + // type User struct {...} + // + // // key is an unexported type for keys defined in this package. + // // This prevents collisions with keys defined in other packages. + // type key int + // + // // userKey is the key for user.User values in Contexts. It is + // // unexported; clients use user.NewContext and user.FromContext + // // instead of using this key directly. + // var userKey key = 0 + // + // // NewContext returns a new Context that carries value u. + // func NewContext(ctx context.Context, u *User) context.Context { + // return context.WithValue(ctx, userKey, u) + // } + // + // // FromContext returns the User value stored in ctx, if any. + // func FromContext(ctx context.Context) (*User, bool) { + // u, ok := ctx.Value(userKey).(*User) + // return u, ok + // } + Value(key interface{}) interface{} +} + +// A CancelFunc tells an operation to abandon its work. +// A CancelFunc does not wait for the work to stop. +// After the first call, subsequent calls to a CancelFunc do nothing. +type CancelFunc func() diff --git a/vendor/vendor.json b/vendor/vendor.json index 303a94cd4ea2202037e94548ae97d02be07b3541..be247eafb0c1f311d181daa8f1d47e6477cc98e7 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -2,6 +2,18 @@ "comment": "", "ignore": "test", "package": [ + { + "checksumSHA1": "raJx5BjBbVQG0ylGSjPpi+JvqjU=", + "path": "git.autistici.org/ai3/go-common", + "revision": "245211ebe9f881b575461958274bada3b8e20b7b", + "revisionTime": "2017-11-23T18:34:19Z" + }, + { + "checksumSHA1": "7xURJYvjMNDtt88/yg9/YzI59kQ=", + "path": "git.autistici.org/ai3/go-common/clientutil", + "revision": "245211ebe9f881b575461958274bada3b8e20b7b", + "revisionTime": "2017-11-23T18:34:19Z" + }, { "checksumSHA1": "hJvRJwSx9aZUKF26o/gOmgUJSsE=", "path": "git.autistici.org/id/auth", @@ -26,6 +38,12 @@ "revision": "4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9", "revisionTime": "2016-08-04T10:47:26Z" }, + { + "checksumSHA1": "hTThB1Cw2ue02RD5Oig4eu1Dkzk=", + "path": "github.com/cenkalti/backoff", + "revision": "309aa717adbf351e92864cbedf9cca0b769a4b5a", + "revisionTime": "2017-10-07T11:45:50Z" + }, { "checksumSHA1": "8zBDp6vRYSBYxja1o+Vr3RpAq2U=", "path": "github.com/crewjam/saml", @@ -207,6 +225,12 @@ "revision": "9419663f5a44be8b34ca85f08abc5fe1be11f8a3", "revisionTime": "2017-09-30T17:45:11Z" }, + { + "checksumSHA1": "dr5+PfIRzXeN+l1VG+s0lea9qz8=", + "path": "golang.org/x/net/context", + "revision": "66aacef3dd8a676686c7ae3716979581e8b03c47", + "revisionTime": "2016-09-14T00:11:54Z" + }, { "checksumSHA1": "dp+OSc8jJIOd6h1mXoAmD3GGAAs=", "path": "golang.org/x/sys/unix",