]> git.lizzy.rs Git - go-anidb.git/blobdiff - udp.go
Modernize
[go-anidb.git] / udp.go
diff --git a/udp.go b/udp.go
index df320041a6dd208bfd1a6df110ca7531adf61579..92eba66a47268594182fcd59ce298e160c56c930 100644 (file)
--- a/udp.go
+++ b/udp.go
@@ -1,40 +1,32 @@
 package anidb
 
 import (
-       "encoding/gob"
-       "github.com/Kovensky/go-anidb/udp"
+       "github.com/EliasFleckenstein03/go-anidb/udp"
+       "sync"
        "time"
 )
 
-func init() {
-       gob.RegisterName("*github.com/Kovensky/go-anidb.banCache", &banCache{})
-}
-
 const banDuration = 30*time.Minute + 1*time.Second
 
-type banCache struct{ time.Time }
-
-func (c *banCache) Touch() {
-       c.Time = time.Now()
-}
-func (c *banCache) IsStale() bool {
-       return time.Now().Sub(c.Time) > banDuration
-}
-
 // Returns whether the last UDP API access returned a 555 BANNED message.
 func Banned() bool {
-       var banTime banCache
-       cache.Get(&banTime, "banned")
+       stat, err := Cache.Stat("banned")
+       if err != nil {
+               return false
+       }
 
-       stale := banTime.IsStale()
-       if stale {
-               cache.Delete("banned")
+       switch ts := stat.ModTime(); {
+       case ts.IsZero():
+               return false
+       case time.Now().Sub(ts) > banDuration:
+               return false
+       default:
+               return true
        }
-       return !stale
 }
 
 func setBanned() {
-       cache.Set(&banCache{}, "banned")
+       Cache.Touch("banned")
 }
 
 type paramSet struct {
@@ -46,15 +38,22 @@ type paramSet struct {
 type udpWrap struct {
        *udpapi.AniDBUDP
 
+       adb *AniDB
+
+       sendLock    sync.Mutex
        sendQueueCh chan paramSet
 
+       credLock    sync.Mutex
        credentials *credentials
        connected   bool
+
+       user *User
 }
 
-func newUDPWrap() *udpWrap {
+func newUDPWrap(adb *AniDB) *udpWrap {
        u := &udpWrap{
                AniDBUDP:    udpapi.NewAniDBUDP(),
+               adb:         adb,
                sendQueueCh: make(chan paramSet, 10),
        }
        go u.sendQueue()
@@ -95,26 +94,66 @@ func (r *bannedAPIReply) Error() error {
 
 var bannedReply udpapi.APIReply = &bannedAPIReply{}
 
+func (udp *udpWrap) logRequest(set paramSet) {
+       switch set.cmd {
+       case "AUTH":
+               udp.adb.Logger.Printf("UDP>>> AUTH user=%s\n", set.params["user"])
+       default:
+               udp.adb.Logger.Printf("UDP>>> %s %s\n", set.cmd, udpapi.ParamMap(set.params).String())
+       }
+}
+
+func (udp *udpWrap) logReply(reply udpapi.APIReply) {
+       udp.adb.Logger.Printf("UDP<<< %d %s\n", reply.Code(), reply.Text())
+}
+
 func (udp *udpWrap) sendQueue() {
+       initialWait := 5 * time.Second
+       wait := initialWait
        for set := range udp.sendQueueCh {
+       Retry:
+               if Banned() {
+                       set.ch <- bannedReply
+                       close(set.ch)
+                       continue
+               }
+
+               udp.logRequest(set)
                reply := <-udp.AniDBUDP.SendRecv(set.cmd, udpapi.ParamMap(set.params))
 
                if reply.Error() == udpapi.TimeoutError {
                        // retry
-                       go func(set paramSet) { udp.sendQueueCh <- set }(set)
-                       continue
+                       wait = wait * 2
+                       if wait > time.Minute {
+                               wait = time.Minute
+                       }
+                       udp.adb.Logger.Printf("UDP--- Timeout; waiting %s before retry", wait)
+
+                       delete(set.params, "s")
+                       delete(set.params, "tag")
+
+                       time.Sleep(wait)
+                       goto Retry
                }
+               udp.logReply(reply)
+
+               wait = initialWait
 
                switch reply.Code() {
                case 403, 501, 506: // not logged in, or session expired
-                       if err := udp.ReAuth(); err == nil {
+                       if r := udp.ReAuth(); r.Error() == nil {
                                // retry
-                               go func(set paramSet) { udp.sendQueueCh <- set }(set)
-                               continue
+
+                               delete(set.params, "s")
+                               delete(set.params, "tag")
+
+                               goto Retry
                        }
                case 503, 504: // client library rejected
                        panic(reply.Error())
-               case 555: // IP (and user, possibly client) temporarily banned
+               // 555: IP (and user, possibly client) temporarily banned
+               // 601: Server down (treat the same as a ban)
+               case 555, 601:
                        setBanned()
                }
                set.ch <- reply
@@ -124,11 +163,9 @@ func (udp *udpWrap) sendQueue() {
 
 func (udp *udpWrap) SendRecv(cmd string, params paramMap) <-chan udpapi.APIReply {
        ch := make(chan udpapi.APIReply, 1)
-       if udp.credentials == nil {
-               ch <- &noauthAPIReply{}
-               close(ch)
-               return ch
-       }
+
+       udp.sendLock.Lock()
+       defer udp.sendLock.Unlock()
 
        if Banned() {
                ch <- bannedReply
@@ -136,6 +173,18 @@ func (udp *udpWrap) SendRecv(cmd string, params paramMap) <-chan udpapi.APIReply
                return ch
        }
 
+       if !udp.connected {
+               if r := udp.ReAuth(); r.Error() != nil {
+                       ch <- r
+                       close(ch)
+                       return ch
+               }
+       }
+
+       if params == nil {
+               params = paramMap{}
+       }
+
        udp.sendQueueCh <- paramSet{
                cmd:    cmd,
                params: params,