websocket 增加多分组 fork https://github.com/olahol/melody
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

382 lines
11 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
9 years ago
9 years ago
10 years ago
10 years ago
9 years ago
9 years ago
10 years ago
10 years ago
10 years ago
9 years ago
8 years ago
10 years ago
10 years ago
5 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
8 years ago
10 years ago
10 years ago
9 years ago
8 years ago
10 years ago
10 years ago
9 years ago
9 years ago
8 years ago
9 years ago
8 years ago
9 years ago
9 years ago
8 years ago
8 years ago
5 years ago
5 years ago
  1. package melody
  2. import (
  3. "errors"
  4. "net/http"
  5. "sync"
  6. "github.com/gorilla/websocket"
  7. )
  8. // Close codes defined in RFC 6455, section 11.7.
  9. // Duplicate of codes from gorilla/websocket for convenience.
  10. const (
  11. CloseNormalClosure = 1000
  12. CloseGoingAway = 1001
  13. CloseProtocolError = 1002
  14. CloseUnsupportedData = 1003
  15. CloseNoStatusReceived = 1005
  16. CloseAbnormalClosure = 1006
  17. CloseInvalidFramePayloadData = 1007
  18. ClosePolicyViolation = 1008
  19. CloseMessageTooBig = 1009
  20. CloseMandatoryExtension = 1010
  21. CloseInternalServerErr = 1011
  22. CloseServiceRestart = 1012
  23. CloseTryAgainLater = 1013
  24. CloseTLSHandshake = 1015
  25. )
  26. // Duplicate of codes from gorilla/websocket for convenience.
  27. var validReceivedCloseCodes = map[int]bool{
  28. // see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number
  29. CloseNormalClosure: true,
  30. CloseGoingAway: true,
  31. CloseProtocolError: true,
  32. CloseUnsupportedData: true,
  33. CloseNoStatusReceived: false,
  34. CloseAbnormalClosure: false,
  35. CloseInvalidFramePayloadData: true,
  36. ClosePolicyViolation: true,
  37. CloseMessageTooBig: true,
  38. CloseMandatoryExtension: true,
  39. CloseInternalServerErr: true,
  40. CloseServiceRestart: true,
  41. CloseTryAgainLater: true,
  42. CloseTLSHandshake: false,
  43. }
  44. type handleMessageFunc func(*Session, []byte)
  45. type handleErrorFunc func(*Session, error)
  46. type handleCloseFunc func(*Session, int, string) error
  47. type handleSessionFunc func(*Session)
  48. type filterFunc func(*Session) bool
  49. // Melody implements a websocket manager.
  50. type Melody struct {
  51. Config *Config
  52. Upgrader *websocket.Upgrader
  53. messageHandler handleMessageFunc
  54. messageHandlerBinary handleMessageFunc
  55. messageSentHandler handleMessageFunc
  56. messageSentHandlerBinary handleMessageFunc
  57. errorHandler handleErrorFunc
  58. closeHandler handleCloseFunc
  59. connectHandler handleSessionFunc
  60. disconnectHandler handleSessionFunc
  61. pongHandler handleSessionFunc
  62. hub *hub
  63. }
  64. // New creates a new melody instance with default Upgrader and Config.
  65. func New() *Melody {
  66. upgrader := &websocket.Upgrader{
  67. ReadBufferSize: 1024,
  68. WriteBufferSize: 1024,
  69. CheckOrigin: func(r *http.Request) bool { return true },
  70. }
  71. hub := newHub()
  72. go hub.run()
  73. return &Melody{
  74. Config: newConfig(),
  75. Upgrader: upgrader,
  76. messageHandler: func(*Session, []byte) {},
  77. messageHandlerBinary: func(*Session, []byte) {},
  78. messageSentHandler: func(*Session, []byte) {},
  79. messageSentHandlerBinary: func(*Session, []byte) {},
  80. errorHandler: func(*Session, error) {},
  81. closeHandler: nil,
  82. connectHandler: func(*Session) {},
  83. disconnectHandler: func(*Session) {},
  84. pongHandler: func(*Session) {},
  85. hub: hub,
  86. }
  87. }
  88. // HandleConnect fires fn when a session connects.
  89. func (m *Melody) HandleConnect(fn func(*Session)) {
  90. m.connectHandler = fn
  91. }
  92. // HandleDisconnect fires fn when a session disconnects.
  93. func (m *Melody) HandleDisconnect(fn func(*Session)) {
  94. m.disconnectHandler = fn
  95. }
  96. // HandlePong fires fn when a pong is received from a session.
  97. func (m *Melody) HandlePong(fn func(*Session)) {
  98. m.pongHandler = fn
  99. }
  100. // HandleMessage fires fn when a text message comes in.
  101. func (m *Melody) HandleMessage(fn func(*Session, []byte)) {
  102. m.messageHandler = fn
  103. }
  104. // HandleMessageBinary fires fn when a binary message comes in.
  105. func (m *Melody) HandleMessageBinary(fn func(*Session, []byte)) {
  106. m.messageHandlerBinary = fn
  107. }
  108. // HandleSentMessage fires fn when a text message is successfully sent.
  109. func (m *Melody) HandleSentMessage(fn func(*Session, []byte)) {
  110. m.messageSentHandler = fn
  111. }
  112. // HandleSentMessageBinary fires fn when a binary message is successfully sent.
  113. func (m *Melody) HandleSentMessageBinary(fn func(*Session, []byte)) {
  114. m.messageSentHandlerBinary = fn
  115. }
  116. // HandleError fires fn when a session has an error.
  117. func (m *Melody) HandleError(fn func(*Session, error)) {
  118. m.errorHandler = fn
  119. }
  120. // HandleClose sets the handler for close messages received from the session.
  121. // The code argument to h is the received close code or CloseNoStatusReceived
  122. // if the close message is empty. The default close handler sends a close frame
  123. // back to the session.
  124. //
  125. // The application must read the connection to process close messages as
  126. // described in the section on Control Frames above.
  127. //
  128. // The connection read methods return a CloseError when a close frame is
  129. // received. Most applications should handle close messages as part of their
  130. // normal error handling. Applications should only set a close handler when the
  131. // application must perform some action before sending a close frame back to
  132. // the session.
  133. func (m *Melody) HandleClose(fn func(*Session, int, string) error) {
  134. if fn != nil {
  135. m.closeHandler = fn
  136. }
  137. }
  138. // HandleRequest upgrades http requests to websocket connections and dispatches them to be handled by the melody instance.
  139. func (m *Melody) HandleRequest(w http.ResponseWriter, r *http.Request) error {
  140. return m.HandleRequestWithKeys(w, r, nil)
  141. }
  142. // HandleRequestWithKeys does the same as HandleRequest but populates session.Keys with keys.
  143. func (m *Melody) HandleRequestWithKeys(w http.ResponseWriter, r *http.Request, keys map[string]interface{}) error {
  144. if m.hub.closed() {
  145. return errors.New("melody instance is closed")
  146. }
  147. conn, err := m.Upgrader.Upgrade(w, r, w.Header())
  148. if err != nil {
  149. return err
  150. }
  151. session := &Session{
  152. Request: r,
  153. Keys: keys,
  154. conn: conn,
  155. output: make(chan *envelope, m.Config.MessageBufferSize),
  156. melody: m,
  157. open: true,
  158. rwmutex: &sync.RWMutex{},
  159. // extends
  160. next: nil,
  161. prev: nil,
  162. channel: "",
  163. }
  164. m.hub.register <- session
  165. m.connectHandler(session)
  166. go session.writePump()
  167. session.readPump()
  168. if !m.hub.closed() {
  169. m.hub.unregister <- session
  170. }
  171. session.close()
  172. m.disconnectHandler(session)
  173. return nil
  174. }
  175. // Broadcast broadcasts a text message to all sessions.
  176. func (m *Melody) Broadcast(msg []byte) error {
  177. if m.hub.closed() {
  178. return errors.New("melody instance is closed")
  179. }
  180. message := &envelope{t: websocket.TextMessage, msg: msg}
  181. m.hub.broadcast <- message
  182. return nil
  183. }
  184. // BroadcastFilter broadcasts a text message to all sessions that fn returns true for.
  185. func (m *Melody) BroadcastFilter(msg []byte, fn func(*Session) bool) error {
  186. if m.hub.closed() {
  187. return errors.New("melody instance is closed")
  188. }
  189. message := &envelope{t: websocket.TextMessage, msg: msg, filter: fn}
  190. m.hub.broadcast <- message
  191. return nil
  192. }
  193. // BroadcastOthers broadcasts a text message to all sessions except session s.
  194. func (m *Melody) BroadcastOthers(msg []byte, s *Session) error {
  195. return m.BroadcastFilter(msg, func(q *Session) bool {
  196. return s != q
  197. })
  198. }
  199. // BroadcastMultiple broadcasts a text message to multiple sessions given in the sessions slice.
  200. func (m *Melody) BroadcastMultiple(msg []byte, sessions []*Session) error {
  201. for _, sess := range sessions {
  202. if writeErr := sess.Write(msg); writeErr != nil {
  203. return writeErr
  204. }
  205. }
  206. return nil
  207. }
  208. // BroadcastBinary broadcasts a binary message to all sessions.
  209. func (m *Melody) BroadcastBinary(msg []byte) error {
  210. if m.hub.closed() {
  211. return errors.New("melody instance is closed")
  212. }
  213. message := &envelope{t: websocket.BinaryMessage, msg: msg}
  214. m.hub.broadcast <- message
  215. return nil
  216. }
  217. // BroadcastBinaryFilter broadcasts a binary message to all sessions that fn returns true for.
  218. func (m *Melody) BroadcastBinaryFilter(msg []byte, fn func(*Session) bool) error {
  219. if m.hub.closed() {
  220. return errors.New("melody instance is closed")
  221. }
  222. message := &envelope{t: websocket.BinaryMessage, msg: msg, filter: fn}
  223. m.hub.broadcast <- message
  224. return nil
  225. }
  226. // BroadcastBinaryOthers broadcasts a binary message to all sessions except session s.
  227. func (m *Melody) BroadcastBinaryOthers(msg []byte, s *Session) error {
  228. return m.BroadcastBinaryFilter(msg, func(q *Session) bool {
  229. return s != q
  230. })
  231. }
  232. // Close closes the melody instance and all connected sessions.
  233. func (m *Melody) Close() error {
  234. if m.hub.closed() {
  235. return errors.New("melody instance is already closed")
  236. }
  237. m.hub.exit <- &envelope{t: websocket.CloseMessage, msg: []byte{}}
  238. return nil
  239. }
  240. // CloseWithMsg closes the melody instance with the given close payload and all connected sessions.
  241. // Use the FormatCloseMessage function to format a proper close message payload.
  242. func (m *Melody) CloseWithMsg(msg []byte) error {
  243. if m.hub.closed() {
  244. return errors.New("melody instance is already closed")
  245. }
  246. m.hub.exit <- &envelope{t: websocket.CloseMessage, msg: msg}
  247. return nil
  248. }
  249. // Len return the number of connected sessions.
  250. func (m *Melody) Len() int {
  251. return m.hub.len()
  252. }
  253. // IsClosed returns the status of the melody instance.
  254. func (m *Melody) IsClosed() bool {
  255. return m.hub.closed()
  256. }
  257. // FormatCloseMessage formats closeCode and text as a WebSocket close message.
  258. func FormatCloseMessage(closeCode int, text string) []byte {
  259. return websocket.FormatCloseMessage(closeCode, text)
  260. }
  261. // extends
  262. func (m *Melody) Channels() map[string]*Channel {
  263. return m.hub.channels
  264. }
  265. func (m *Melody)Channel(name string) *Channel {
  266. if ch, ok := m.hub.channels[name]; ok {
  267. return ch
  268. }
  269. return nil
  270. }
  271. func (m *Melody) Subscribe(s *Session, c string) error {
  272. if m.hub.closed() {
  273. return errors.New("melody instance is already closed")
  274. }
  275. return s.Subscribe(c)
  276. }
  277. func (m *Melody) Unsubscribe(s *Session, c string) error {
  278. if m.hub.closed() {
  279. return errors.New("melody instance is already closed")
  280. }
  281. return s.Subscribe(c)
  282. }
  283. func (m *Melody) Publish(msg []byte, c string) error {
  284. return m.PublishFilter(msg, c, nil)
  285. }
  286. func (m *Melody) PublishOthers(msg []byte, c string, s *Session) error {
  287. return m.PublishFilter(msg, c, func(session *Session) bool {
  288. return s != session
  289. })
  290. }
  291. func (m *Melody) PublishFilter(msg []byte, c string, fn func(*Session) bool) error {
  292. if m.hub.closed() {
  293. return errors.New("melody instance is already closed")
  294. }
  295. message := &envelope{t: websocket.TextMessage, msg: msg, c: c, filter: fn}
  296. m.hub.publish <- message
  297. return nil
  298. }
  299. func (m *Melody) PublishBinary(msg []byte, c string) error {
  300. return m.PublishBinaryFilter(msg, c, nil)
  301. }
  302. func (m *Melody) PublishBinaryOthers(msg []byte, c string, s *Session) error {
  303. return m.PublishFilter(msg, c, func(session *Session) bool {
  304. return s != session
  305. })
  306. }
  307. func (m *Melody) PublishBinaryFilter(msg []byte, c string, fn func(*Session) bool) error {
  308. if m.hub.closed() {
  309. return errors.New("melody instance is already closed")
  310. }
  311. message := &envelope{t: websocket.BinaryMessage, msg: msg, c: c, filter: fn}
  312. m.hub.publish <- message
  313. return nil
  314. }