浏览代码

Accept Retry-After header on discovery lookup failures

Jakob Borg 10 年之前
父节点
当前提交
9f2dc4554d
共有 3 个文件被更改,包括 39 次插入11 次删除
  1. 16 4
      lib/discover/cache.go
  2. 5 4
      lib/discover/discover.go
  3. 18 3
      lib/discover/global.go

+ 16 - 4
lib/discover/cache.go

@@ -44,6 +44,13 @@ type prioritizedAddress struct {
 	addr     string
 }
 
+// An error may implement cachedError, in which case it will be interrogated
+// to see how long we should cache the error. This overrides the default
+// negative cache time.
+type cachedError interface {
+	CacheFor() time.Duration
+}
+
 func NewCachingMux() *CachingMux {
 	return &CachingMux{
 		Supervisor: suture.NewSimple("discover.cachingMux"),
@@ -84,10 +91,11 @@ func (m *CachingMux) Lookup(deviceID protocol.DeviceID) (direct []string, relays
 				continue
 			}
 
-			if !cacheEntry.found && time.Since(cacheEntry.when) < finder.negCacheTime {
+			valid := time.Now().Before(cacheEntry.validUntil) || time.Since(cacheEntry.when) < finder.negCacheTime
+			if !cacheEntry.found && valid {
 				// It's a negative, valid entry. We should not make another
 				// attempt right now.
-				l.Debugln("negative cache entry for", deviceID, "at", finder)
+				l.Debugln("negative cache entry for", deviceID, "at", finder, "valid until", cacheEntry.when.Add(finder.negCacheTime), "or", cacheEntry.validUntil)
 				continue
 			}
 
@@ -111,10 +119,14 @@ func (m *CachingMux) Lookup(deviceID protocol.DeviceID) (direct []string, relays
 			})
 		} else {
 			// Lookup returned error, add a negative cache entry.
-			m.caches[i].Set(deviceID, CacheEntry{
+			entry := CacheEntry{
 				when:  time.Now(),
 				found: false,
-			})
+			}
+			if err, ok := err.(cachedError); ok {
+				entry.validUntil = time.Now().Add(err.CacheFor())
+			}
+			m.caches[i].Set(deviceID, entry)
 		}
 	}
 	m.mut.Unlock()

+ 5 - 4
lib/discover/discover.go

@@ -22,10 +22,11 @@ type Finder interface {
 }
 
 type CacheEntry struct {
-	Direct []string  `json:"direct"`
-	Relays []Relay   `json:"relays"`
-	when   time.Time // When did we get the result
-	found  bool      // Is it a success (cacheTime applies) or a failure (negCacheTime applies)?
+	Direct     []string  `json:"direct"`
+	Relays     []Relay   `json:"relays"`
+	when       time.Time // When did we get the result
+	found      bool      // Is it a success (cacheTime applies) or a failure (negCacheTime applies)?
+	validUntil time.Time // Validity time, overrides normal calculation
 }
 
 // A FinderService is a Finder that has background activity and must be run as

+ 18 - 3
lib/discover/global.go

@@ -56,6 +56,16 @@ type serverOptions struct {
 	id         string // expected server device ID
 }
 
+// A lookupError is any other error but with a cache validity time attached.
+type lookupError struct {
+	error
+	cacheFor time.Duration
+}
+
+func (e lookupError) CacheFor() time.Duration {
+	return e.cacheFor
+}
+
 func NewGlobal(server string, cert tls.Certificate, addrList AddressLister, relayStat RelayStatusProvider) (FinderService, error) {
 	server, opts, err := parseOptions(server)
 	if err != nil {
@@ -138,11 +148,16 @@ func (c *globalClient) Lookup(device protocol.DeviceID) (direct []string, relays
 	if resp.StatusCode != 200 {
 		resp.Body.Close()
 		l.Debugln("globalClient.Lookup", qURL, resp.Status)
-		return nil, nil, errors.New(resp.Status)
+		err := errors.New(resp.Status)
+		if secs, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil && secs > 0 {
+			err = lookupError{
+				error:    err,
+				cacheFor: time.Duration(secs) * time.Second,
+			}
+		}
+		return nil, nil, err
 	}
 
-	// TODO: Handle 429 and Retry-After?
-
 	var ann announcement
 	err = json.NewDecoder(resp.Body).Decode(&ann)
 	resp.Body.Close()