channel.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. package sharedspace
  2. /*
  3. SharedSpace realtime channel
  4. A Channel is the realtime fan-out hub of a space: WebSocket handlers
  5. (and any other transport) Join it to receive a numbered Subscriber
  6. whose Send buffer they drain into their connection. Frames pushed
  7. through Broadcast / SendTo are opaque bytes - the channel never
  8. interprets them - so the same hub carries chat delivery, document
  9. patches, presence and ephemeral WebRTC signaling (MeetRoom runs its
  10. meeting signaling over room channels).
  11. Locking rules:
  12. - Channel.mu is a leaf lock: no Space method is called and no hook
  13. or listener is invoked while it is held. Join / Leave snapshot
  14. under the lock and fire the presence hooks after unlocking.
  15. - Space.mu -> Channel.mu nesting never occurs: Space.Channel()
  16. only constructs the hub, and channel methods never re-enter the
  17. space.
  18. - Full send buffers drop the frame rather than blocking the hub
  19. (same policy as the MeetRoom relay and Arozcast).
  20. */
  21. import (
  22. "sync"
  23. "time"
  24. )
  25. // SubscriberSendBuffer is the per-subscriber outgoing frame buffer size.
  26. const SubscriberSendBuffer = 256
  27. // Subscriber is one connected member of a channel. The transport layer
  28. // drains Send and writes each frame to its connection. IDs are 1-based;
  29. // ID 0 is reserved for server-side senders in transport protocols.
  30. type Subscriber struct {
  31. ID int
  32. Username string
  33. Send chan []byte
  34. joinedAt time.Time
  35. once sync.Once
  36. }
  37. // CloseSend closes the subscriber's send channel exactly once.
  38. func (s *Subscriber) CloseSend() {
  39. s.once.Do(func() { close(s.Send) })
  40. }
  41. // JoinedAt returns when the subscriber joined the channel.
  42. func (s *Subscriber) JoinedAt() time.Time {
  43. return s.joinedAt
  44. }
  45. // Channel is the realtime hub of a space (or a standalone hub when created
  46. // with NewStandaloneChannel).
  47. type Channel struct {
  48. space *Space // nil for standalone channels (no ACL applied on Join)
  49. subscribers map[int]*Subscriber
  50. nextID int
  51. onJoin func(*Subscriber)
  52. onLeave func(*Subscriber)
  53. closed bool
  54. mu sync.Mutex
  55. }
  56. // Channel returns the space's realtime hub, creating it on first use. The
  57. // returned pointer is stable for the lifetime of the space.
  58. func (s *Space) Channel() *Channel {
  59. s.mu.Lock()
  60. defer s.mu.Unlock()
  61. if s.channel == nil {
  62. s.channel = &Channel{
  63. space: s,
  64. subscribers: make(map[int]*Subscriber),
  65. nextID: 1,
  66. closed: s.closed,
  67. }
  68. }
  69. return s.channel
  70. }
  71. // NewStandaloneChannel creates a hub that is not bound to any space: Join
  72. // applies no access control. Used by consumers that manage their own
  73. // membership rules (e.g. MeetRoom rooms without a space manager).
  74. func NewStandaloneChannel() *Channel {
  75. return &Channel{
  76. subscribers: make(map[int]*Subscriber),
  77. nextID: 1,
  78. }
  79. }
  80. // SetPresenceHooks installs callbacks fired after a subscriber joins or
  81. // leaves. Hooks run outside the channel lock; set them once, before the
  82. // first Join, from the subsystem that owns the channel.
  83. func (c *Channel) SetPresenceHooks(onJoin func(*Subscriber), onLeave func(*Subscriber)) {
  84. c.mu.Lock()
  85. c.onJoin = onJoin
  86. c.onLeave = onLeave
  87. c.mu.Unlock()
  88. }
  89. // Join registers username as a new subscriber. For space-bound channels the
  90. // space's read permission is enforced. The transport layer must drain the
  91. // returned subscriber's Send channel.
  92. func (c *Channel) Join(username string) (*Subscriber, error) {
  93. if c.space != nil && !c.space.CanRead(username) {
  94. return nil, ErrPermissionDenied
  95. }
  96. c.mu.Lock()
  97. if c.closed {
  98. c.mu.Unlock()
  99. return nil, ErrSpaceClosed
  100. }
  101. sub := &Subscriber{
  102. ID: c.nextID,
  103. Username: username,
  104. Send: make(chan []byte, SubscriberSendBuffer),
  105. joinedAt: time.Now(),
  106. }
  107. c.nextID++
  108. c.subscribers[sub.ID] = sub
  109. onJoin := c.onJoin
  110. c.mu.Unlock()
  111. if onJoin != nil {
  112. onJoin(sub)
  113. }
  114. return sub, nil
  115. }
  116. // Leave unregisters a subscriber and closes its send channel. Safe to call
  117. // twice or with an unknown ID.
  118. func (c *Channel) Leave(id int) {
  119. c.mu.Lock()
  120. sub, ok := c.subscribers[id]
  121. if ok {
  122. delete(c.subscribers, id)
  123. }
  124. onLeave := c.onLeave
  125. c.mu.Unlock()
  126. if !ok {
  127. return
  128. }
  129. if onLeave != nil {
  130. onLeave(sub)
  131. }
  132. sub.CloseSend()
  133. }
  134. // Broadcast queues msg to every subscriber except excludeID (pass a negative
  135. // value to send to everyone). Full send buffers drop the frame rather than
  136. // blocking the hub.
  137. func (c *Channel) Broadcast(msg []byte, excludeID int) {
  138. c.mu.Lock()
  139. defer c.mu.Unlock()
  140. for id, sub := range c.subscribers {
  141. if id == excludeID {
  142. continue
  143. }
  144. select {
  145. case sub.Send <- append([]byte(nil), msg...):
  146. default:
  147. }
  148. }
  149. }
  150. // SendTo queues msg to a single subscriber. It reports whether the
  151. // subscriber exists in the channel.
  152. func (c *Channel) SendTo(id int, msg []byte) bool {
  153. c.mu.Lock()
  154. defer c.mu.Unlock()
  155. sub, ok := c.subscribers[id]
  156. if !ok {
  157. return false
  158. }
  159. select {
  160. case sub.Send <- append([]byte(nil), msg...):
  161. default:
  162. }
  163. return true
  164. }
  165. // Get returns the subscriber with the given ID.
  166. func (c *Channel) Get(id int) (*Subscriber, bool) {
  167. c.mu.Lock()
  168. defer c.mu.Unlock()
  169. sub, ok := c.subscribers[id]
  170. return sub, ok
  171. }
  172. // Subscribers returns a snapshot of the current subscribers.
  173. func (c *Channel) Subscribers() []*Subscriber {
  174. c.mu.Lock()
  175. defer c.mu.Unlock()
  176. list := make([]*Subscriber, 0, len(c.subscribers))
  177. for _, sub := range c.subscribers {
  178. list = append(list, sub)
  179. }
  180. return list
  181. }
  182. // Count returns the number of connected subscribers.
  183. func (c *Channel) Count() int {
  184. c.mu.Lock()
  185. defer c.mu.Unlock()
  186. return len(c.subscribers)
  187. }
  188. // Close marks the channel closed, removes every subscriber and closes their
  189. // send channels. It returns the removed subscribers so the transport layer
  190. // can finish delivering queued frames before the connections drop. Safe to
  191. // call more than once.
  192. func (c *Channel) Close() []*Subscriber {
  193. c.mu.Lock()
  194. c.closed = true
  195. members := make([]*Subscriber, 0, len(c.subscribers))
  196. for _, sub := range c.subscribers {
  197. members = append(members, sub)
  198. }
  199. c.subscribers = make(map[int]*Subscriber)
  200. c.mu.Unlock()
  201. for _, sub := range members {
  202. sub.CloseSend()
  203. }
  204. return members
  205. }