mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 03:22:24 -05:00 
			
		
		
		
	* start fixing up tests * fix up tests + automate with drone * fiddle with linting * messing about with drone.yml * some more fiddling * hmmm * add cache * add vendor directory * verbose * ci updates * update some little things * update sig
		
			
				
	
	
		
			102 lines
		
	
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			102 lines
		
	
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // 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 struct{}, 1)
 | |
| 		mu <- struct{}{}
 | |
| 		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 }
 |