| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485 |
- package toc
- import (
- "sync"
- "time"
- "github.com/mk6i/open-oscar-server/wire"
- )
- // snacCacheTTL is how long a sent SNAC waits for a reply before it is dropped
- // from the cache. Requests the server drops, such as rate-limited ones, never
- // get a reply.
- const snacCacheTTL = 60 * time.Second
- // snacCache tracks the SNACs a TOC connection sends to the OSCAR server by
- // request ID, so that a reply can be matched to the request that produced it.
- type snacCache struct {
- mu sync.Mutex
- lastID uint32
- entries map[uint32]sentSNAC
- timeNow func() time.Time
- }
- // sentSNAC is a SNAC request awaiting a reply.
- type sentSNAC struct {
- // frame is the request's SNAC frame.
- frame wire.SNACFrame
- // data holds whatever the reply handler needs to know about the request.
- data any
- sentAt time.Time
- }
- func newSNACCache() *snacCache {
- return &snacCache{
- entries: make(map[uint32]sentSNAC),
- timeNow: time.Now,
- }
- }
- // add assigns the next request ID to frame and records the request along with
- // data. Entries older than snacCacheTTL are swept out.
- func (c *snacCache) add(frame *wire.SNACFrame, data any) {
- c.mu.Lock()
- defer c.mu.Unlock()
- now := c.timeNow()
- for id, entry := range c.entries {
- if now.Sub(entry.sentAt) > snacCacheTTL {
- delete(c.entries, id)
- }
- }
- c.lastID++
- if c.lastID >= wire.ReqIDFromServer {
- c.lastID = 1
- }
- frame.RequestID = c.lastID
- c.entries[frame.RequestID] = sentSNAC{
- frame: *frame,
- data: data,
- sentAt: now,
- }
- }
- // take removes and returns the request that reply answers. A reply only
- // matches a request in the same food group, so that a relayed SNAC carrying
- // another user's request ID is not mistaken for a reply.
- func (c *snacCache) take(reply wire.SNACFrame) (sentSNAC, bool) {
- c.mu.Lock()
- defer c.mu.Unlock()
- entry, ok := c.entries[reply.RequestID]
- if !ok || entry.frame.FoodGroup != reply.FoodGroup {
- return sentSNAC{}, false
- }
- delete(c.entries, reply.RequestID)
- return entry, true
- }
- // remove drops the request with the given ID.
- func (c *snacCache) remove(requestID uint32) {
- c.mu.Lock()
- defer c.mu.Unlock()
- delete(c.entries, requestID)
- }
|