github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/multiplexing/protocol.go (about)

     1  package multiplexing
     2  
     3  import (
     4  	"encoding/binary"
     5  	"io"
     6  	"math"
     7  
     8  	"github.com/mutagen-io/mutagen/pkg/multiplexing/ring"
     9  )
    10  
    11  // messageKind encodes a message kind on the wire.
    12  type messageKind byte
    13  
    14  const (
    15  	// messageKindMultiplexerHeartbeat indicates a multiplexer heartbeat
    16  	// message. The message is structured as follows:
    17  	// - Message kind (byte)
    18  	messageKindMultiplexerHeartbeat messageKind = iota
    19  	// messageKindStreamOpen indicates a stream open message. The message is
    20  	// structured as follows:
    21  	// - Message kind (byte)
    22  	// - Stream identifier (uvarint64)
    23  	// - Initial remote stream receive window size (uvarint64)
    24  	messageKindStreamOpen
    25  	// messageKindStreamOpen indicates a stream accept message. The message is
    26  	// structured as follows:
    27  	// - Message kind (byte)
    28  	// - Stream identifier (uvarint64)
    29  	// - Initial remote stream receive window size (uvarint64)
    30  	messageKindStreamAccept
    31  	// messageKindStreamData indicates a stream data message. The message is
    32  	// structured as follows:
    33  	// - Message kind (byte)
    34  	// - Stream identifier (uvarint64)
    35  	// - Data length (uint16 (network byte order))
    36  	// - Data (bytes)
    37  	messageKindStreamData
    38  	// messageKindStreamWindowIncrement indicates a stream receive window size
    39  	// increment message. The message is structured as follows:
    40  	// - Message kind (byte)
    41  	// - Stream identifier (uvarint64)
    42  	// - Increment amount (uvarint64)
    43  	messageKindStreamWindowIncrement
    44  	// messageKindStreamCloseWrite indicates a stream write close message. The
    45  	// message is structured as follows:
    46  	// - Message kind (byte)
    47  	// - Stream identifier (uvarint64)
    48  	messageKindStreamCloseWrite
    49  	// messageKindStreamClose indicates a stream close message. The message is
    50  	// structured as follows:
    51  	// - Message kind (byte)
    52  	// - Stream identifier (uvarint64)
    53  	messageKindStreamClose
    54  )
    55  
    56  const (
    57  	// messageKindStreamOpenMaximumSize is the maximum size of a stream open
    58  	// message.
    59  	messageKindStreamOpenMaximumSize = 1 + binary.MaxVarintLen64 + binary.MaxVarintLen64
    60  	// messageKindStreamAcceptMaximumSize is the maximum size of a stream accept
    61  	// message.
    62  	messageKindStreamAcceptMaximumSize = 1 + binary.MaxVarintLen64 + binary.MaxVarintLen64
    63  	// messageKindStreamDataMaximumSize is the maximum size of a stream data
    64  	// message.
    65  	messageKindStreamDataMaximumSize = 1 + binary.MaxVarintLen64 + 2 + math.MaxUint16
    66  	// messageKindStreamWindowIncrementMaximumSize is the maximum size of a
    67  	// stream window increment message.
    68  	messageKindStreamWindowIncrementMaximumSize = 1 + binary.MaxVarintLen64 + binary.MaxVarintLen64
    69  	// messageKindStreamCloseWriteMaximumSize is the maximum size of a stream
    70  	// close write message.
    71  	messageKindStreamCloseWriteMaximumSize = 1 + binary.MaxVarintLen64
    72  	// messageKindStreamCloseMaximumSize is the maximum size of a stream close
    73  	// message.
    74  	messageKindStreamCloseMaximumSize = 1 + binary.MaxVarintLen64
    75  
    76  	// maximumMessageSize is the maximum size of any single messsage.
    77  	maximumMessageSize = messageKindStreamDataMaximumSize
    78  
    79  	// maximumStreamDataBlockSize is the maximum size (in bytes) for a single
    80  	// block of stream data sent with a stream data message. It is determined
    81  	// by the use of a 16-bit unsigned integer for encoding its length.
    82  	maximumStreamDataBlockSize = math.MaxUint16
    83  )
    84  
    85  // messageBuffer is a reusable buffer type for encoding and transmitting
    86  // protocol messages.
    87  type messageBuffer struct {
    88  	// buffer is the underlying buffer used for storage.
    89  	buffer *ring.Buffer
    90  	// varint64Buffer is a reusable buffer for encoding variable length integers
    91  	// up to 64-bits. It is also used for encoding 16-bit unsigned integers to
    92  	// network byte order.
    93  	varint64Buffer []byte
    94  }
    95  
    96  // newMessageBuffer creates a new message buffer. It is guaranteed to have
    97  // enough capacity to write any single message.
    98  func newMessageBuffer() *messageBuffer {
    99  	return &messageBuffer{
   100  		buffer:         ring.NewBuffer(maximumMessageSize),
   101  		varint64Buffer: make([]byte, binary.MaxVarintLen64),
   102  	}
   103  }
   104  
   105  // ensureSufficientFreeSpace panics if the buffer doesn't contain at least the
   106  // specified amount of free space.
   107  func (b *messageBuffer) ensureSufficientFreeSpace(amount int) {
   108  	if b.buffer.Free() < amount {
   109  		panic("buffer not guaranteed to have sufficient free space")
   110  	}
   111  }
   112  
   113  // writeUvarint is an internal utility function used to write unsigned variable
   114  // length integers up to 64-bits.
   115  func (b *messageBuffer) writeUvarint(value uint64) {
   116  	length := binary.PutUvarint(b.varint64Buffer, value)
   117  	b.buffer.Write(b.varint64Buffer[:length])
   118  }
   119  
   120  // writeUint16 is an internal utility function used to write unsigned 16-bit
   121  // integers.
   122  func (b *messageBuffer) writeUint16(value uint16) {
   123  	binary.BigEndian.PutUint16(b.varint64Buffer[:2], value)
   124  	b.buffer.Write(b.varint64Buffer[:2])
   125  }
   126  
   127  // WriteTo implements io.WriterTo.WriteTo.
   128  func (b *messageBuffer) WriteTo(writer io.Writer) (int64, error) {
   129  	return b.buffer.WriteTo(writer)
   130  }
   131  
   132  // encodeOpenMessage encodes a stream open message to the message buffer. It
   133  // will panic if the buffer does not have sufficient free space.
   134  func (b *messageBuffer) encodeOpenMessage(stream, window uint64) {
   135  	b.ensureSufficientFreeSpace(messageKindStreamOpenMaximumSize)
   136  	b.buffer.WriteByte(byte(messageKindStreamOpen))
   137  	b.writeUvarint(stream)
   138  	b.writeUvarint(window)
   139  }
   140  
   141  // encodeAcceptMessage encodes a stream accept message to the message buffer. It
   142  // will panic if the buffer does not have sufficient free space.
   143  func (b *messageBuffer) encodeAcceptMessage(stream, window uint64) {
   144  	b.ensureSufficientFreeSpace(messageKindStreamAcceptMaximumSize)
   145  	b.buffer.WriteByte(byte(messageKindStreamAccept))
   146  	b.writeUvarint(stream)
   147  	b.writeUvarint(window)
   148  }
   149  
   150  // encodeStreamDataMessage encodes a stream data message to the buffer. It will
   151  // panic if the buffer does not have sufficient free space or if the data block
   152  // is larger than maximumStreamDataBlockSize.
   153  func (b *messageBuffer) encodeStreamDataMessage(stream uint64, data []byte) {
   154  	b.ensureSufficientFreeSpace(messageKindStreamDataMaximumSize)
   155  	if len(data) > maximumStreamDataBlockSize {
   156  		panic("data block too large")
   157  	}
   158  	b.buffer.WriteByte(byte(messageKindStreamData))
   159  	b.writeUvarint(stream)
   160  	b.writeUint16(uint16(len(data)))
   161  	b.buffer.Write(data)
   162  }
   163  
   164  // canEncodeStreamWindowIncrement returns whether or not a call to
   165  // encodeStreamWindowIncrement is guaranteed to have sufficient free space.
   166  func (b *messageBuffer) canEncodeStreamWindowIncrement() bool {
   167  	return b.buffer.Free() >= messageKindStreamWindowIncrementMaximumSize
   168  }
   169  
   170  // encodeStreamWindowIncrement encodes a stream window increment message to the
   171  // buffer. It will panic if the buffer does not have sufficient free space.
   172  func (b *messageBuffer) encodeStreamWindowIncrement(stream, amount uint64) {
   173  	b.ensureSufficientFreeSpace(messageKindStreamWindowIncrementMaximumSize)
   174  	b.buffer.WriteByte(byte(messageKindStreamWindowIncrement))
   175  	b.writeUvarint(stream)
   176  	b.writeUvarint(amount)
   177  }
   178  
   179  // canEncodeStreamCloseWrite returns whether or not a call to
   180  // encodeStreamCloseWrite is guaranteed to have sufficient free space.
   181  func (b *messageBuffer) canEncodeStreamCloseWrite() bool {
   182  	return b.buffer.Free() >= messageKindStreamCloseWriteMaximumSize
   183  }
   184  
   185  // encodeStreamCloseWrite encodes a stream half-closure message to the buffer.
   186  // It will panic if the buffer does not have sufficient free space.
   187  func (b *messageBuffer) encodeStreamCloseWrite(stream uint64) {
   188  	b.ensureSufficientFreeSpace(messageKindStreamCloseWriteMaximumSize)
   189  	b.buffer.WriteByte(byte(messageKindStreamCloseWrite))
   190  	b.writeUvarint(stream)
   191  }
   192  
   193  // canEncodeStreamClose returns whether or not a call to encodeStreamClose is
   194  // guaranteed to have sufficient free space.
   195  func (b *messageBuffer) canEncodeStreamClose() bool {
   196  	return b.buffer.Free() >= messageKindStreamCloseMaximumSize
   197  }
   198  
   199  // encodeStreamClose encodes a stream closure message to the buffer. It will
   200  // panic if the buffer does not have sufficient free space.
   201  func (b *messageBuffer) encodeStreamClose(stream uint64) {
   202  	b.ensureSufficientFreeSpace(messageKindStreamCloseMaximumSize)
   203  	b.buffer.WriteByte(byte(messageKindStreamClose))
   204  	b.writeUvarint(stream)
   205  }