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 }