]> git.lizzy.rs Git - go-anidb.git/blob - udp.go
anidb: New caching mechanism
[go-anidb.git] / udp.go
1 package anidb
2
3 import (
4         "encoding/gob"
5         "github.com/Kovensky/go-anidb/udp"
6         "time"
7 )
8
9 func init() {
10         gob.RegisterName("*github.com/Kovensky/go-anidb.banCache", &banCache{})
11 }
12
13 const banDuration = 30*time.Minute + 1*time.Second
14
15 type banCache struct{ time.Time }
16
17 func (c *banCache) Touch() {
18         c.Time = time.Now()
19 }
20 func (c *banCache) IsStale() bool {
21         return time.Now().Sub(c.Time) > banDuration
22 }
23
24 // Returns whether the last UDP API access returned a 555 BANNED message.
25 func Banned() bool {
26         var banTime banCache
27         cache.Get(&banTime, "banned")
28
29         stale := banTime.IsStale()
30         if stale {
31                 cache.Delete("banned")
32         }
33         return !stale
34 }
35
36 func setBanned() {
37         cache.Set(&banCache{}, "banned")
38 }
39
40 type paramSet struct {
41         cmd    string
42         params paramMap
43         ch     chan udpapi.APIReply
44 }
45
46 type udpWrap struct {
47         *udpapi.AniDBUDP
48
49         sendQueueCh chan paramSet
50
51         credentials *credentials
52         connected   bool
53 }
54
55 func newUDPWrap() *udpWrap {
56         u := &udpWrap{
57                 AniDBUDP:    udpapi.NewAniDBUDP(),
58                 sendQueueCh: make(chan paramSet, 10),
59         }
60         go u.sendQueue()
61         return u
62 }
63
64 type paramMap udpapi.ParamMap // shortcut
65
66 type bannedAPIReply struct {
67         udpapi.APIReply
68 }
69
70 func (r *bannedAPIReply) Code() int {
71         return 555
72 }
73 func (r *bannedAPIReply) Text() string {
74         return "BANNED"
75 }
76 func (r *bannedAPIReply) Error() error {
77         return &udpapi.APIError{Code: 555, Desc: "BANNED"}
78 }
79
80 var bannedReply udpapi.APIReply = &bannedAPIReply{}
81
82 func (udp *udpWrap) sendQueue() {
83         for set := range udp.sendQueueCh {
84                 reply := <-udp.AniDBUDP.SendRecv(set.cmd, udpapi.ParamMap(set.params))
85
86                 if reply.Error() == udpapi.TimeoutError {
87                         // retry
88                         go func(set paramSet) { udp.sendQueueCh <- set }(set)
89                         continue
90                 }
91
92                 switch reply.Code() {
93                 case 403, 501, 506: // not logged in, or session expired
94                         if err := udp.ReAuth(); err == nil {
95                                 // retry
96                                 go func(set paramSet) { udp.sendQueueCh <- set }(set)
97                                 continue
98                         }
99                 case 503, 504: // client library rejected
100                         panic(reply.Error())
101                 case 555: // IP (and user, possibly client) temporarily banned
102                         setBanned()
103                 }
104                 set.ch <- reply
105                 close(set.ch)
106         }
107 }
108
109 func (udp *udpWrap) SendRecv(cmd string, params paramMap) <-chan udpapi.APIReply {
110         ch := make(chan udpapi.APIReply, 1)
111
112         if Banned() {
113                 ch <- bannedReply
114                 close(ch)
115                 return ch
116         }
117
118         udp.sendQueueCh <- paramSet{
119                 cmd:    cmd,
120                 params: params,
121                 ch:     ch,
122         }
123
124         return ch
125 }