github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/grid/msg.go (about) 1 // Copyright (c) 2015-2023 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package grid 19 20 import ( 21 "encoding/binary" 22 "fmt" 23 "strings" 24 25 "github.com/tinylib/msgp/msgp" 26 "github.com/zeebo/xxh3" 27 ) 28 29 // Op is operation type. 30 // 31 //go:generate msgp -unexported -file=$GOFILE 32 //go:generate stringer -type=Op -output=msg_string.go -trimprefix=Op $GOFILE 33 34 // Op is operation type messages. 35 type Op uint8 36 37 // HandlerID is the ID for the handler of a specific type. 38 type HandlerID uint8 39 40 const ( 41 // OpConnect is a connect request. 42 OpConnect Op = iota + 1 43 44 // OpConnectResponse is a response to a connect request. 45 OpConnectResponse 46 47 // OpPing is a ping request. 48 // If a mux id is specified that mux is pinged. 49 // Clients send ping requests. 50 OpPing 51 52 // OpPong is a OpPing response returned by the server. 53 OpPong 54 55 // OpConnectMux will connect a new mux with optional payload. 56 OpConnectMux 57 58 // OpMuxConnectError is an error while connecting a mux. 59 OpMuxConnectError 60 61 // OpDisconnectClientMux instructs a client to disconnect a mux 62 OpDisconnectClientMux 63 64 // OpDisconnectServerMux instructs a server to disconnect (cancel) a server mux 65 OpDisconnectServerMux 66 67 // OpMuxClientMsg contains a message to a client Mux 68 OpMuxClientMsg 69 70 // OpMuxServerMsg contains a message to a server Mux 71 OpMuxServerMsg 72 73 // OpUnblockSrvMux contains a message that a server mux is unblocked with one. 74 // Only Stateful streams has flow control. 75 OpUnblockSrvMux 76 77 // OpUnblockClMux contains a message that a client mux is unblocked with one. 78 // Only Stateful streams has flow control. 79 OpUnblockClMux 80 81 // OpAckMux acknowledges a mux was created. 82 OpAckMux 83 84 // OpRequest is a single request + response. 85 // MuxID is returned in response. 86 OpRequest 87 88 // OpResponse is a response to a single request. 89 // FlagPayloadIsErr is used to signify that the payload is a string error converted to byte slice. 90 // When a response is received, the mux is already removed from the remote. 91 OpResponse 92 93 // OpDisconnect instructs that remote wants to disconnect 94 OpDisconnect 95 96 // OpMerged is several operations merged into one. 97 OpMerged 98 ) 99 100 const ( 101 // FlagCRCxxh3 indicates that, the lower 32 bits of xxhash3 of the serialized 102 // message will be sent after the serialized message as little endian. 103 FlagCRCxxh3 Flags = 1 << iota 104 105 // FlagEOF the stream (either direction) is at EOF. 106 FlagEOF 107 108 // FlagStateless indicates the message is stateless. 109 // This will retain clients across reconnections or 110 // if sequence numbers are unexpected. 111 FlagStateless 112 113 // FlagPayloadIsErr can be used by individual ops to signify that 114 // The payload is a string error converted to byte slice. 115 FlagPayloadIsErr 116 117 // FlagPayloadIsZero means that payload is 0-length slice and not nil. 118 FlagPayloadIsZero 119 120 // FlagSubroute indicates that the message has subroute. 121 // Subroute will be 32 bytes long and added before any CRC. 122 FlagSubroute 123 ) 124 125 // This struct cannot be changed and retain backwards compatibility. 126 // If changed, endpoint version must be bumped. 127 // 128 //msgp:tuple message 129 type message struct { 130 MuxID uint64 // Mux to receive message if any. 131 Seq uint32 // Sequence number. 132 DeadlineMS uint32 // If non-zero, milliseconds until deadline (max 1193h2m47.295s, ~49 days) 133 Handler HandlerID // ID of handler if invoking a remote handler. 134 Op Op // Operation. Other fields change based on this value. 135 Flags Flags // Optional flags. 136 Payload []byte // Optional payload. 137 } 138 139 // Flags is a set of flags set on a message. 140 type Flags uint8 141 142 func (m message) String() string { 143 var res []string 144 if m.MuxID != 0 { 145 res = append(res, fmt.Sprintf("MuxID: %v", m.MuxID)) 146 } 147 if m.Seq != 0 { 148 res = append(res, fmt.Sprintf("Seq: %v", m.Seq)) 149 } 150 if m.DeadlineMS != 0 { 151 res = append(res, fmt.Sprintf("Deadline: %vms", m.DeadlineMS)) 152 } 153 if m.Handler != handlerInvalid { 154 res = append(res, fmt.Sprintf("Handler: %v", m.Handler)) 155 } 156 if m.Op != 0 { 157 res = append(res, fmt.Sprintf("Op: %v", m.Op)) 158 } 159 res = append(res, fmt.Sprintf("Flags: %s", m.Flags.String())) 160 if len(m.Payload) != 0 { 161 res = append(res, fmt.Sprintf("Payload: %v", bytesOrLength(m.Payload))) 162 } 163 return "{" + strings.Join(res, ", ") + "}" 164 } 165 166 func (f Flags) String() string { 167 var res []string 168 if f&FlagCRCxxh3 != 0 { 169 res = append(res, "CRC") 170 } 171 if f&FlagEOF != 0 { 172 res = append(res, "EOF") 173 } 174 if f&FlagStateless != 0 { 175 res = append(res, "SL") 176 } 177 if f&FlagPayloadIsErr != 0 { 178 res = append(res, "ERR") 179 } 180 if f&FlagPayloadIsZero != 0 { 181 res = append(res, "ZERO") 182 } 183 if f&FlagSubroute != 0 { 184 res = append(res, "SUB") 185 } 186 return "[" + strings.Join(res, ",") + "]" 187 } 188 189 // Set one or more flags on f. 190 func (f *Flags) Set(flags Flags) { 191 *f |= flags 192 } 193 194 // Clear one or more flags on f. 195 func (f *Flags) Clear(flags Flags) { 196 *f &^= flags 197 } 198 199 // parse an incoming message. 200 func (m *message) parse(b []byte) (*subHandlerID, []byte, error) { 201 var sub *subHandlerID 202 if m.Payload == nil { 203 m.Payload = GetByteBuffer()[:0] 204 } 205 h, err := m.UnmarshalMsg(b) 206 if err != nil { 207 return nil, nil, fmt.Errorf("read write: %v", err) 208 } 209 if len(m.Payload) == 0 && m.Flags&FlagPayloadIsZero == 0 { 210 PutByteBuffer(m.Payload) 211 m.Payload = nil 212 } 213 if m.Flags&FlagCRCxxh3 != 0 { 214 const hashLen = 4 215 if len(h) < hashLen { 216 return nil, nil, fmt.Errorf("want crc len 4, got %v", len(h)) 217 } 218 got := uint32(xxh3.Hash(b[:len(b)-hashLen])) 219 want := binary.LittleEndian.Uint32(h[len(h)-hashLen:]) 220 if got != want { 221 return nil, nil, fmt.Errorf("crc mismatch: 0x%08x (given) != 0x%08x (bytes)", want, got) 222 } 223 h = h[:len(h)-hashLen] 224 } 225 // Extract subroute if any. 226 if m.Flags&FlagSubroute != 0 { 227 if len(h) < 32 { 228 return nil, nil, fmt.Errorf("want subroute len 32, got %v", len(h)) 229 } 230 subID := (*[32]byte)(h[len(h)-32:]) 231 sub = (*subHandlerID)(subID) 232 // Add if more modifications to h is needed 233 h = h[:len(h)-32] 234 } 235 return sub, h, nil 236 } 237 238 // setZeroPayloadFlag will clear or set the FlagPayloadIsZero if 239 // m.Payload is length 0, but not nil. 240 func (m *message) setZeroPayloadFlag() { 241 m.Flags &^= FlagPayloadIsZero 242 if len(m.Payload) == 0 && m.Payload != nil { 243 m.Flags |= FlagPayloadIsZero 244 } 245 } 246 247 type receiver interface { 248 msgp.Unmarshaler 249 Op() Op 250 } 251 252 type sender interface { 253 msgp.MarshalSizer 254 Op() Op 255 } 256 257 type connectReq struct { 258 ID [16]byte 259 Host string 260 } 261 262 func (connectReq) Op() Op { 263 return OpConnect 264 } 265 266 type connectResp struct { 267 ID [16]byte 268 Accepted bool 269 RejectedReason string 270 } 271 272 func (connectResp) Op() Op { 273 return OpConnectResponse 274 } 275 276 type muxConnectError struct { 277 Error string 278 } 279 280 func (muxConnectError) Op() Op { 281 return OpMuxConnectError 282 } 283 284 type pongMsg struct { 285 NotFound bool `msg:"nf"` 286 Err *string `msg:"e,allownil"` 287 } 288 289 func (pongMsg) Op() Op { 290 return OpPong 291 }