OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / github.com / gorilla / websocket / prepared.go
diff --git a/vendor/github.com/gorilla/websocket/prepared.go b/vendor/github.com/gorilla/websocket/prepared.go
new file mode 100644 (file)
index 0000000..74ec565
--- /dev/null
@@ -0,0 +1,102 @@
+// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package websocket
+
+import (
+       "bytes"
+       "net"
+       "sync"
+       "time"
+)
+
+// PreparedMessage caches on the wire representations of a message payload.
+// Use PreparedMessage to efficiently send a message payload to multiple
+// connections. PreparedMessage is especially useful when compression is used
+// because the CPU and memory expensive compression operation can be executed
+// once for a given set of compression options.
+type PreparedMessage struct {
+       messageType int
+       data        []byte
+       mu          sync.Mutex
+       frames      map[prepareKey]*preparedFrame
+}
+
+// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage.
+type prepareKey struct {
+       isServer         bool
+       compress         bool
+       compressionLevel int
+}
+
+// preparedFrame contains data in wire representation.
+type preparedFrame struct {
+       once sync.Once
+       data []byte
+}
+
+// NewPreparedMessage returns an initialized PreparedMessage. You can then send
+// it to connection using WritePreparedMessage method. Valid wire
+// representation will be calculated lazily only once for a set of current
+// connection options.
+func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) {
+       pm := &PreparedMessage{
+               messageType: messageType,
+               frames:      make(map[prepareKey]*preparedFrame),
+               data:        data,
+       }
+
+       // Prepare a plain server frame.
+       _, frameData, err := pm.frame(prepareKey{isServer: true, compress: false})
+       if err != nil {
+               return nil, err
+       }
+
+       // To protect against caller modifying the data argument, remember the data
+       // copied to the plain server frame.
+       pm.data = frameData[len(frameData)-len(data):]
+       return pm, nil
+}
+
+func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) {
+       pm.mu.Lock()
+       frame, ok := pm.frames[key]
+       if !ok {
+               frame = &preparedFrame{}
+               pm.frames[key] = frame
+       }
+       pm.mu.Unlock()
+
+       var err error
+       frame.once.Do(func() {
+               // Prepare a frame using a 'fake' connection.
+               // TODO: Refactor code in conn.go to allow more direct construction of
+               // the frame.
+               mu := make(chan bool, 1)
+               mu <- true
+               var nc prepareConn
+               c := &Conn{
+                       conn:                   &nc,
+                       mu:                     mu,
+                       isServer:               key.isServer,
+                       compressionLevel:       key.compressionLevel,
+                       enableWriteCompression: true,
+                       writeBuf:               make([]byte, defaultWriteBufferSize+maxFrameHeaderSize),
+               }
+               if key.compress {
+                       c.newCompressionWriter = compressNoContextTakeover
+               }
+               err = c.WriteMessage(pm.messageType, pm.data)
+               frame.data = nc.buf.Bytes()
+       })
+       return pm.messageType, frame.data, err
+}
+
+type prepareConn struct {
+       buf bytes.Buffer
+       net.Conn
+}
+
+func (pc *prepareConn) Write(p []byte) (int, error)        { return pc.buf.Write(p) }
+func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil }