event.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package neffos
  2. import (
  3. "fmt"
  4. "io"
  5. "net"
  6. "os"
  7. "strings"
  8. )
  9. // MessageHandlerFunc is the definition type of the events' callback.
  10. // Its error can be written to the other side on specific events,
  11. // i.e on `OnNamespaceConnect` it will abort a remote namespace connection.
  12. // See examples for more.
  13. type MessageHandlerFunc func(*NSConn, Message) error
  14. var (
  15. // OnNamespaceConnect is the event name which its callback is fired right before namespace connect,
  16. // if non-nil error then the remote connection's `Conn.Connect` will fail and send that error text.
  17. // Connection is not ready to emit data to the namespace.
  18. OnNamespaceConnect = "_OnNamespaceConnect"
  19. // OnNamespaceConnected is the event name which its callback is fired after namespace successfully connected.
  20. // Connection is ready to emit data back to the namespace.
  21. OnNamespaceConnected = "_OnNamespaceConnected"
  22. // OnNamespaceDisconnect is the event name which its callback is fired when
  23. // remote namespace disconnection or local namespace disconnection is happening.
  24. // For server-side connections the reply matters, so if error returned then the client-side cannot disconnect yet,
  25. // for client-side the return value does not matter.
  26. OnNamespaceDisconnect = "_OnNamespaceDisconnect" // if allowed to connect then it's allowed to disconnect as well.
  27. // OnRoomJoin is the event name which its callback is fired right before room join.
  28. OnRoomJoin = "_OnRoomJoin" // able to check if allowed to join.
  29. // OnRoomJoined is the event name which its callback is fired after the connection has successfully joined to a room.
  30. OnRoomJoined = "_OnRoomJoined" // able to broadcast messages to room.
  31. // OnRoomLeave is the event name which its callback is fired right before room leave.
  32. OnRoomLeave = "_OnRoomLeave" // able to broadcast bye-bye messages to room.
  33. // OnRoomLeft is the event name which its callback is fired after the connection has successfully left from a room.
  34. OnRoomLeft = "_OnRoomLeft" // if allowed to join to a room, then its allowed to leave from it.
  35. // OnAnyEvent is the event name which its callback is fired when incoming message's event is not declared to the ConnHandler(`Events` or `Namespaces`).
  36. OnAnyEvent = "_OnAnyEvent" // when event no match.
  37. // OnNativeMessage is fired on incoming native/raw websocket messages.
  38. // If this event defined then an incoming message can pass the check (it's an invalid message format)
  39. // with just the Message's Body filled, the Event is "OnNativeMessage" and IsNative always true.
  40. // This event should be defined under an empty namespace in order this to work.
  41. OnNativeMessage = "_OnNativeMessage"
  42. )
  43. // IsSystemEvent reports whether the "event" is a system event,
  44. // OnNamespaceConnect, OnNamespaceConnected, OnNamespaceDisconnect,
  45. // OnRoomJoin, OnRoomJoined, OnRoomLeave and OnRoomLeft.
  46. func IsSystemEvent(event string) bool {
  47. switch event {
  48. case OnNamespaceConnect, OnNamespaceConnected, OnNamespaceDisconnect,
  49. OnRoomJoin, OnRoomJoined, OnRoomLeave, OnRoomLeft:
  50. return true
  51. default:
  52. return false
  53. }
  54. }
  55. // CloseError can be used to send and close a remote connection in the event callback's return statement.
  56. type CloseError struct {
  57. error
  58. Code int
  59. }
  60. func (err CloseError) Error() string {
  61. return fmt.Sprintf("[%d] %s", err.Code, err.error.Error())
  62. }
  63. // IsDisconnectError reports whether the "err" is a timeout or a closed connection error.
  64. func IsDisconnectError(err error) bool {
  65. if err == nil {
  66. return false
  67. }
  68. return IsCloseError(err) || IsTimeoutError(err)
  69. }
  70. func isManualCloseError(err error) bool {
  71. if _, ok := err.(CloseError); ok {
  72. return true
  73. }
  74. return false
  75. }
  76. // IsCloseError reports whether the "err" is a "closed by the remote host" network connection error.
  77. func IsCloseError(err error) bool {
  78. if err == nil {
  79. return false
  80. }
  81. if isManualCloseError(err) {
  82. return true
  83. }
  84. if err == io.ErrUnexpectedEOF || err == io.EOF {
  85. return true
  86. }
  87. if netErr, ok := err.(*net.OpError); ok {
  88. if netErr.Err == nil {
  89. return false
  90. }
  91. if sysErr, ok := netErr.Err.(*os.SyscallError); ok {
  92. return sysErr != nil
  93. // return strings.HasSuffix(sysErr.Err.Error(), "closed by the remote host.")
  94. }
  95. return strings.HasSuffix(err.Error(), "use of closed network connection")
  96. }
  97. return false
  98. }
  99. // IsTimeoutError reports whether the "err" is caused by a defined timeout.
  100. func IsTimeoutError(err error) bool {
  101. if err == nil {
  102. return false
  103. }
  104. if netErr, ok := err.(*net.OpError); ok {
  105. // poll.TimeoutError is the /internal/poll of the go language itself, we can't use it directly.
  106. return netErr.Timeout()
  107. }
  108. return false
  109. }
  110. type reply struct {
  111. Body []byte
  112. }
  113. func (r reply) Error() string {
  114. return ""
  115. }
  116. func isReply(err error) ([]byte, bool) {
  117. if err != nil {
  118. if r, ok := err.(reply); ok {
  119. return r.Body, true
  120. }
  121. }
  122. return nil, false
  123. }
  124. // Reply is a special type of custom error which sends a message back to the other side
  125. // with the exact same incoming Message's Namespace (and Room if specified)
  126. // except its body which would be the given "body".
  127. func Reply(body []byte) error {
  128. return reply{body}
  129. }