1
0

prepared.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package websocket
  5. import (
  6. "bytes"
  7. "net"
  8. "sync"
  9. "time"
  10. )
  11. // PreparedMessage caches on the wire representations of a message payload.
  12. // Use PreparedMessage to efficiently send a message payload to multiple
  13. // connections. PreparedMessage is especially useful when compression is used
  14. // because the CPU and memory expensive compression operation can be executed
  15. // once for a given set of compression options.
  16. type PreparedMessage struct {
  17. messageType int
  18. data []byte
  19. err error
  20. mu sync.Mutex
  21. frames map[prepareKey]*preparedFrame
  22. }
  23. // prepareKey defines a unique set of options to cache prepared frames in PreparedMessage.
  24. type prepareKey struct {
  25. isServer bool
  26. compress bool
  27. compressionLevel int
  28. }
  29. // preparedFrame contains data in wire representation.
  30. type preparedFrame struct {
  31. once sync.Once
  32. data []byte
  33. }
  34. // NewPreparedMessage returns an initialized PreparedMessage. You can then send
  35. // it to connection using WritePreparedMessage method. Valid wire
  36. // representation will be calculated lazily only once for a set of current
  37. // connection options.
  38. func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) {
  39. pm := &PreparedMessage{
  40. messageType: messageType,
  41. frames: make(map[prepareKey]*preparedFrame),
  42. data: data,
  43. }
  44. // Prepare a plain server frame.
  45. _, frameData, err := pm.frame(prepareKey{isServer: true, compress: false})
  46. if err != nil {
  47. return nil, err
  48. }
  49. // To protect against caller modifying the data argument, remember the data
  50. // copied to the plain server frame.
  51. pm.data = frameData[len(frameData)-len(data):]
  52. return pm, nil
  53. }
  54. func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) {
  55. pm.mu.Lock()
  56. frame, ok := pm.frames[key]
  57. if !ok {
  58. frame = &preparedFrame{}
  59. pm.frames[key] = frame
  60. }
  61. pm.mu.Unlock()
  62. var err error
  63. frame.once.Do(func() {
  64. // Prepare a frame using a 'fake' connection.
  65. // TODO: Refactor code in conn.go to allow more direct construction of
  66. // the frame.
  67. mu := make(chan bool, 1)
  68. mu <- true
  69. var nc prepareConn
  70. c := &Conn{
  71. conn: &nc,
  72. mu: mu,
  73. isServer: key.isServer,
  74. compressionLevel: key.compressionLevel,
  75. enableWriteCompression: true,
  76. writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize),
  77. }
  78. if key.compress {
  79. c.newCompressionWriter = compressNoContextTakeover
  80. }
  81. err = c.WriteMessage(pm.messageType, pm.data)
  82. frame.data = nc.buf.Bytes()
  83. })
  84. return pm.messageType, frame.data, err
  85. }
  86. type prepareConn struct {
  87. buf bytes.Buffer
  88. net.Conn
  89. }
  90. func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) }
  91. func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil }