mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-03 19:52:24 -06: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 }
 |