turbotunnel.go 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. package lib
  2. import (
  3. "sync"
  4. "git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel"
  5. )
  6. // clientIDMap is a fixed-capacity mapping from ClientIDs to address strings.
  7. // Adding a new entry using the Set method causes the oldest existing entry to
  8. // be forgotten.
  9. //
  10. // This data type is meant to be used to remember the IP address associated with
  11. // a ClientID, during the short period of time between when a WebSocket
  12. // connection with that ClientID began, and when a KCP session is established.
  13. //
  14. // The design requirements of this type are that it needs to remember a mapping
  15. // for only a short time, and old entries should expire so as not to consume
  16. // unbounded memory. It is not a critical error if an entry is forgotten before
  17. // it is needed; better to forget entries than to use too much memory.
  18. type clientIDMap struct {
  19. lock sync.Mutex
  20. // entries is a circular buffer of (ClientID, addr) pairs.
  21. entries []struct {
  22. clientID turbotunnel.ClientID
  23. addr string
  24. }
  25. // oldest is the index of the oldest member of the entries buffer, the
  26. // one that will be overwritten at the next call to Set.
  27. oldest int
  28. // current points to the index of the most recent entry corresponding to
  29. // each ClientID.
  30. current map[turbotunnel.ClientID]int
  31. }
  32. // newClientIDMap makes a new clientIDMap with the given capacity.
  33. func newClientIDMap(capacity int) *clientIDMap {
  34. return &clientIDMap{
  35. entries: make([]struct {
  36. clientID turbotunnel.ClientID
  37. addr string
  38. }, capacity),
  39. oldest: 0,
  40. current: make(map[turbotunnel.ClientID]int),
  41. }
  42. }
  43. // Set adds a mapping from clientID to addr, replacing any previous mapping for
  44. // clientID. It may also cause the clientIDMap to forget at most one other
  45. // mapping, the oldest one.
  46. func (m *clientIDMap) Set(clientID turbotunnel.ClientID, addr string) {
  47. m.lock.Lock()
  48. defer m.lock.Unlock()
  49. if len(m.entries) == 0 {
  50. // The invariant m.oldest < len(m.entries) does not hold in this
  51. // special case.
  52. return
  53. }
  54. // m.oldest is the index of the entry we're about to overwrite. If it's
  55. // the current entry for any ClientID, we need to delete that clientID
  56. // from the current map (that ClientID is now forgotten).
  57. if i, ok := m.current[m.entries[m.oldest].clientID]; ok && i == m.oldest {
  58. delete(m.current, m.entries[m.oldest].clientID)
  59. }
  60. // Overwrite the oldest entry.
  61. m.entries[m.oldest].clientID = clientID
  62. m.entries[m.oldest].addr = addr
  63. // Add the overwritten entry to the quick-lookup map.
  64. m.current[clientID] = m.oldest
  65. // What was the oldest entry is now the newest.
  66. m.oldest = (m.oldest + 1) % len(m.entries)
  67. }
  68. // Get returns a previously stored mapping. The second return value indicates
  69. // whether clientID was actually present in the map. If it is false, then the
  70. // returned address string will be "".
  71. func (m *clientIDMap) Get(clientID turbotunnel.ClientID) (string, bool) {
  72. m.lock.Lock()
  73. defer m.lock.Unlock()
  74. if i, ok := m.current[clientID]; ok {
  75. return m.entries[i].addr, true
  76. } else {
  77. return "", false
  78. }
  79. }