handler.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. package wsutil
  2. import (
  3. "errors"
  4. "io"
  5. "io/ioutil"
  6. "strconv"
  7. "github.com/gobwas/pool/pbytes"
  8. "github.com/gobwas/ws"
  9. )
  10. // ClosedError returned when peer has closed the connection with appropriate
  11. // code and a textual reason.
  12. type ClosedError struct {
  13. Code ws.StatusCode
  14. Reason string
  15. }
  16. // Error implements error interface.
  17. func (err ClosedError) Error() string {
  18. return "ws closed: " + strconv.FormatUint(uint64(err.Code), 10) + " " + err.Reason
  19. }
  20. // ControlHandler contains logic of handling control frames.
  21. //
  22. // The intentional way to use it is to read the next frame header from the
  23. // connection, optionally check its validity via ws.CheckHeader() and if it is
  24. // not a ws.OpText of ws.OpBinary (or ws.OpContinuation) – pass it to Handle()
  25. // method.
  26. //
  27. // That is, passed header should be checked to get rid of unexpected errors.
  28. //
  29. // The Handle() method will read out all control frame payload (if any) and
  30. // write necessary bytes as a rfc compatible response.
  31. type ControlHandler struct {
  32. Src io.Reader
  33. Dst io.Writer
  34. State ws.State
  35. // DisableSrcCiphering disables unmasking payload data read from Src.
  36. // It is useful when wsutil.Reader is used or when frame payload already
  37. // pulled and ciphered out from the connection (and introduced by
  38. // bytes.Reader, for example).
  39. DisableSrcCiphering bool
  40. }
  41. // ErrNotControlFrame is returned by ControlHandler to indicate that given
  42. // header could not be handled.
  43. var ErrNotControlFrame = errors.New("not a control frame")
  44. // Handle handles control frames regarding to the c.State and writes responses
  45. // to the c.Dst when needed.
  46. //
  47. // It returns ErrNotControlFrame when given header is not of ws.OpClose,
  48. // ws.OpPing or ws.OpPong operation code.
  49. func (c ControlHandler) Handle(h ws.Header) error {
  50. switch h.OpCode {
  51. case ws.OpPing:
  52. return c.HandlePing(h)
  53. case ws.OpPong:
  54. return c.HandlePong(h)
  55. case ws.OpClose:
  56. return c.HandleClose(h)
  57. }
  58. return ErrNotControlFrame
  59. }
  60. // HandlePing handles ping frame and writes specification compatible response
  61. // to the c.Dst.
  62. func (c ControlHandler) HandlePing(h ws.Header) error {
  63. if h.Length == 0 {
  64. // The most common case when ping is empty.
  65. // Note that when sending masked frame the mask for empty payload is
  66. // just four zero bytes.
  67. return ws.WriteHeader(c.Dst, ws.Header{
  68. Fin: true,
  69. OpCode: ws.OpPong,
  70. Masked: c.State.ClientSide(),
  71. })
  72. }
  73. // In other way reply with Pong frame with copied payload.
  74. p := pbytes.GetLen(int(h.Length) + ws.HeaderSize(ws.Header{
  75. Length: h.Length,
  76. Masked: c.State.ClientSide(),
  77. }))
  78. defer pbytes.Put(p)
  79. // Deal with ciphering i/o:
  80. // Masking key is used to mask the "Payload data" defined in the same
  81. // section as frame-payload-data, which includes "Extension data" and
  82. // "Application data".
  83. //
  84. // See https://tools.ietf.org/html/rfc6455#section-5.3
  85. //
  86. // NOTE: We prefer ControlWriter with preallocated buffer to
  87. // ws.WriteHeader because it performs one syscall instead of two.
  88. w := NewControlWriterBuffer(c.Dst, c.State, ws.OpPong, p)
  89. r := c.Src
  90. if c.State.ServerSide() && !c.DisableSrcCiphering {
  91. r = NewCipherReader(r, h.Mask)
  92. }
  93. _, err := io.Copy(w, r)
  94. if err == nil {
  95. err = w.Flush()
  96. }
  97. return err
  98. }
  99. // HandlePong handles pong frame by discarding it.
  100. func (c ControlHandler) HandlePong(h ws.Header) error {
  101. if h.Length == 0 {
  102. return nil
  103. }
  104. buf := pbytes.GetLen(int(h.Length))
  105. defer pbytes.Put(buf)
  106. // Discard pong message according to the RFC6455:
  107. // A Pong frame MAY be sent unsolicited. This serves as a
  108. // unidirectional heartbeat. A response to an unsolicited Pong frame
  109. // is not expected.
  110. _, err := io.CopyBuffer(ioutil.Discard, c.Src, buf)
  111. return err
  112. }
  113. // HandleClose handles close frame, makes protocol validity checks and writes
  114. // specification compatible response to the c.Dst.
  115. func (c ControlHandler) HandleClose(h ws.Header) error {
  116. if h.Length == 0 {
  117. err := ws.WriteHeader(c.Dst, ws.Header{
  118. Fin: true,
  119. OpCode: ws.OpClose,
  120. Masked: c.State.ClientSide(),
  121. })
  122. if err != nil {
  123. return err
  124. }
  125. // Due to RFC, we should interpret the code as no status code
  126. // received:
  127. // If this Close control frame contains no status code, _The WebSocket
  128. // Connection Close Code_ is considered to be 1005.
  129. //
  130. // See https://tools.ietf.org/html/rfc6455#section-7.1.5
  131. return ClosedError{
  132. Code: ws.StatusNoStatusRcvd,
  133. }
  134. }
  135. // Prepare bytes both for reading reason and sending response.
  136. p := pbytes.GetLen(int(h.Length) + ws.HeaderSize(ws.Header{
  137. Length: h.Length,
  138. Masked: c.State.ClientSide(),
  139. }))
  140. defer pbytes.Put(p)
  141. // Get the subslice to read the frame payload out.
  142. subp := p[:h.Length]
  143. r := c.Src
  144. if c.State.ServerSide() && !c.DisableSrcCiphering {
  145. r = NewCipherReader(r, h.Mask)
  146. }
  147. if _, err := io.ReadFull(r, subp); err != nil {
  148. return err
  149. }
  150. code, reason := ws.ParseCloseFrameData(subp)
  151. if err := ws.CheckCloseFrameData(code, reason); err != nil {
  152. // Here we could not use the prepared bytes because there is no
  153. // guarantee that it may fit our protocol error closure code and a
  154. // reason.
  155. c.closeWithProtocolError(err)
  156. return err
  157. }
  158. // Deal with ciphering i/o:
  159. // Masking key is used to mask the "Payload data" defined in the same
  160. // section as frame-payload-data, which includes "Extension data" and
  161. // "Application data".
  162. //
  163. // See https://tools.ietf.org/html/rfc6455#section-5.3
  164. //
  165. // NOTE: We prefer ControlWriter with preallocated buffer to
  166. // ws.WriteHeader because it performs one syscall instead of two.
  167. w := NewControlWriterBuffer(c.Dst, c.State, ws.OpClose, p)
  168. // RFC6455#5.5.1:
  169. // If an endpoint receives a Close frame and did not previously
  170. // send a Close frame, the endpoint MUST send a Close frame in
  171. // response. (When sending a Close frame in response, the endpoint
  172. // typically echoes the status code it received.)
  173. _, err := w.Write(p[:2])
  174. if err != nil {
  175. return err
  176. }
  177. if err = w.Flush(); err != nil {
  178. return err
  179. }
  180. return ClosedError{
  181. Code: code,
  182. Reason: reason,
  183. }
  184. }
  185. func (c ControlHandler) closeWithProtocolError(reason error) error {
  186. f := ws.NewCloseFrame(ws.NewCloseFrameBody(
  187. ws.StatusProtocolError, reason.Error(),
  188. ))
  189. if c.State.ClientSide() {
  190. ws.MaskFrameInPlace(f)
  191. }
  192. return ws.WriteFrame(c.Dst, f)
  193. }