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 {
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()
type paramMap udpapi.ParamMap // shortcut
+type noauthAPIReply struct {
+ udpapi.APIReply
+}
+
+func (r *noauthAPIReply) Code() int {
+ return 501
+}
+
+func (r *noauthAPIReply) Text() string {
+ return "LOGIN FIRST"
+}
+
+func (r *noauthAPIReply) Error() error {
+ return &udpapi.APIError{Code: r.Code(), Desc: r.Text()}
+}
+
type bannedAPIReply struct {
udpapi.APIReply
}
return "BANNED"
}
func (r *bannedAPIReply) Error() error {
- return &udpapi.APIError{Code: 555, Desc: "BANNED"}
+ return &udpapi.APIError{Code: r.Code(), Desc: r.Text()}
}
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
func (udp *udpWrap) SendRecv(cmd string, params paramMap) <-chan udpapi.APIReply {
ch := make(chan udpapi.APIReply, 1)
+ udp.sendLock.Lock()
+ defer udp.sendLock.Unlock()
+
if Banned() {
ch <- bannedReply
close(ch)
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,