]> git.lizzy.rs Git - go-anidb.git/blob - udp.go
anidb: Add missing loop 'break's to EpisodeByEID
[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 noauthAPIReply struct {
67         udpapi.APIReply
68 }
69
70 func (r *noauthAPIReply) Code() int {
71         return 501
72 }
73
74 func (r *noauthAPIReply) Text() string {
75         return "LOGIN FIRST"
76 }
77
78 func (r *noauthAPIReply) Error() error {
79         return &udpapi.APIError{Code: r.Code(), Desc: r.Text()}
80 }
81
82 type bannedAPIReply struct {
83         udpapi.APIReply
84 }
85
86 func (r *bannedAPIReply) Code() int {
87         return 555
88 }
89 func (r *bannedAPIReply) Text() string {
90         return "BANNED"
91 }
92 func (r *bannedAPIReply) Error() error {
93         return &udpapi.APIError{Code: r.Code(), Desc: r.Text()}
94 }
95
96 var bannedReply udpapi.APIReply = &bannedAPIReply{}
97
98 func (udp *udpWrap) sendQueue() {
99         initialWait := 6 * time.Second
100         wait := initialWait
101         for set := range udp.sendQueueCh {
102         Retry:
103                 reply := <-udp.AniDBUDP.SendRecv(set.cmd, udpapi.ParamMap(set.params))
104
105                 if reply.Error() == udpapi.TimeoutError {
106                         // retry
107                         wait = (wait * 15) / 10
108                         if wait > time.Minute {
109                                 wait = time.Minute
110                         }
111                         time.Sleep(wait)
112                         goto Retry
113                 }
114                 wait = initialWait
115
116                 switch reply.Code() {
117                 case 403, 501, 506: // not logged in, or session expired
118                         if err := udp.ReAuth(); err == nil {
119                                 // retry
120                                 goto Retry
121                         }
122                 case 503, 504: // client library rejected
123                         panic(reply.Error())
124                 case 555: // IP (and user, possibly client) temporarily banned
125                         setBanned()
126                 }
127                 set.ch <- reply
128                 close(set.ch)
129         }
130 }
131
132 func (udp *udpWrap) SendRecv(cmd string, params paramMap) <-chan udpapi.APIReply {
133         ch := make(chan udpapi.APIReply, 1)
134         if udp.credentials == nil {
135                 ch <- &noauthAPIReply{}
136                 close(ch)
137                 return ch
138         }
139
140         if Banned() {
141                 ch <- bannedReply
142                 close(ch)
143                 return ch
144         }
145
146         udp.sendQueueCh <- paramSet{
147                 cmd:    cmd,
148                 params: params,
149                 ch:     ch,
150         }
151
152         return ch
153 }