github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/protocol/v2/transport.go (about) 1 // Copyright (c) 2022, R.I. Pienaar and the Choria Project contributors 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 package v2 6 7 import ( 8 "encoding/json" 9 "fmt" 10 "strings" 11 "sync" 12 13 "github.com/choria-io/go-choria/protocol" 14 ) 15 16 type TransportMessage struct { 17 // The protocol version for this transport `io.choria.protocol.v2.transport` / protocol.TransportV2 18 Protocol protocol.ProtocolVersion `json:"protocol"` 19 // The payload to be transport, a Secure Request or Secure Reply 20 Data []byte `json:"data"` 21 // Optional headers 22 Headers *TransportHeaders `json:"headers,omitempty"` 23 24 mu sync.Mutex 25 } 26 27 type TransportHeaders struct { 28 // A transport specific response channel for this message, used in requests 29 ReplyTo string `json:"reply,omitempty"` 30 // The host that sent this message 31 Sender string `json:"sender,omitempty"` 32 // A trace of host/broker pairs that the message traversed 33 SeenBy [][3]string `json:"trace,omitempty"` 34 // Headers to assist federation 35 Federation *FederationTransportHeader `json:"federation,omitempty"` 36 } 37 38 type FederationTransportHeader struct { 39 // The request ID a federated message belongs to 40 RequestID string `json:"request,omitempty"` 41 // The original `reply` before federation 42 ReplyTo string `json:"reply,omitempty"` 43 // The identities who the federated message is for 44 Targets []string `json:"targets,omitempty"` 45 } 46 47 // NewTransportMessage creates a io.choria.protocol.v2.transport 48 func NewTransportMessage(sender string) (message protocol.TransportMessage, err error) { 49 message = &TransportMessage{ 50 Protocol: protocol.TransportV2, 51 Headers: &TransportHeaders{}, 52 } 53 54 message.SetSender(sender) 55 56 return message, nil 57 } 58 59 // NewTransportFromJSON creates a new TransportMessage from JSON 60 func NewTransportFromJSON(data []byte) (message protocol.TransportMessage, err error) { 61 message = &TransportMessage{ 62 Headers: &TransportHeaders{}, 63 } 64 65 err = message.IsValidJSON(data) 66 if err != nil { 67 return nil, err 68 } 69 70 err = json.Unmarshal(data, &message) 71 if err != nil { 72 return nil, err 73 } 74 75 return message, nil 76 } 77 78 func (m *TransportMessage) SetFederationRequestID(id string) { 79 m.mu.Lock() 80 defer m.mu.Unlock() 81 82 if m.Headers.Federation == nil { 83 m.Headers.Federation = &FederationTransportHeader{} 84 } 85 86 m.Headers.Federation.RequestID = id 87 } 88 89 func (m *TransportMessage) SetFederationReplyTo(reply string) { 90 m.mu.Lock() 91 defer m.mu.Unlock() 92 93 if m.Headers.Federation == nil { 94 m.Headers.Federation = &FederationTransportHeader{} 95 } 96 97 m.Headers.Federation.ReplyTo = reply 98 } 99 100 func (m *TransportMessage) SetFederationTargets(targets []string) { 101 m.mu.Lock() 102 defer m.mu.Unlock() 103 104 if m.Headers.Federation == nil { 105 m.Headers.Federation = &FederationTransportHeader{} 106 } 107 108 m.Headers.Federation.Targets = targets 109 } 110 111 func (m *TransportMessage) SetUnfederated() { 112 m.mu.Lock() 113 defer m.mu.Unlock() 114 115 m.Headers.Federation = nil 116 } 117 118 func (m *TransportMessage) FederationRequestID() (string, bool) { 119 m.mu.Lock() 120 defer m.mu.Unlock() 121 122 if m.Headers.Federation == nil { 123 return "", false 124 } 125 126 return m.Headers.Federation.RequestID, true 127 } 128 129 func (m *TransportMessage) FederationReplyTo() (string, bool) { 130 m.mu.Lock() 131 defer m.mu.Unlock() 132 133 if m.Headers.Federation == nil { 134 return "", false 135 } 136 137 return m.Headers.Federation.ReplyTo, true 138 } 139 140 func (m *TransportMessage) FederationTargets() ([]string, bool) { 141 m.mu.Lock() 142 defer m.mu.Unlock() 143 144 if m.Headers.Federation == nil { 145 return nil, false 146 } 147 148 return m.Headers.Federation.Targets, true 149 } 150 151 func (m *TransportMessage) RecordNetworkHop(in string, processor string, out string) { 152 m.mu.Lock() 153 defer m.mu.Unlock() 154 155 m.Headers.SeenBy = append(m.Headers.SeenBy, [3]string{in, processor, out}) 156 } 157 158 func (m *TransportMessage) NetworkHops() [][3]string { 159 m.mu.Lock() 160 defer m.mu.Unlock() 161 162 return m.Headers.SeenBy 163 } 164 165 func (m *TransportMessage) IsFederated() bool { 166 m.mu.Lock() 167 defer m.mu.Unlock() 168 169 return m.Headers.Federation != nil 170 } 171 172 func (m *TransportMessage) SetReplyData(reply protocol.SecureReply) error { 173 m.mu.Lock() 174 defer m.mu.Unlock() 175 176 j, err := reply.JSON() 177 if err != nil { 178 return err 179 } 180 181 m.Data = j 182 183 return nil 184 } 185 186 func (m *TransportMessage) SetRequestData(request protocol.SecureRequest) error { 187 m.mu.Lock() 188 defer m.mu.Unlock() 189 190 j, err := request.JSON() 191 if err != nil { 192 return err 193 } 194 195 m.Data = j 196 197 return nil 198 } 199 200 func (m *TransportMessage) SetReplyTo(reply string) { 201 m.mu.Lock() 202 defer m.mu.Unlock() 203 204 m.Headers.ReplyTo = reply 205 } 206 207 func (m *TransportMessage) SetSender(sender string) { 208 m.mu.Lock() 209 defer m.mu.Unlock() 210 211 m.Headers.Sender = sender 212 } 213 214 func (m *TransportMessage) ReplyTo() string { 215 m.mu.Lock() 216 defer m.mu.Unlock() 217 218 return m.Headers.ReplyTo 219 } 220 221 func (m *TransportMessage) SenderID() string { 222 m.mu.Lock() 223 defer m.mu.Unlock() 224 225 return m.Headers.Sender 226 } 227 228 func (m *TransportMessage) SeenBy() [][3]string { 229 m.mu.Lock() 230 defer m.mu.Unlock() 231 232 return m.Headers.SeenBy 233 } 234 235 func (m *TransportMessage) Message() ([]byte, error) { 236 m.mu.Lock() 237 defer m.mu.Unlock() 238 239 return m.Data, nil 240 } 241 242 func (m *TransportMessage) IsValidJSON(data []byte) error { 243 if !protocol.ClientStrictValidation { 244 return nil 245 } 246 247 _, errors, err := schemaValidate(protocol.TransportV2, data) 248 if err != nil { 249 return err 250 } 251 252 if len(errors) != 0 { 253 return fmt.Errorf("%w: %s", ErrInvalidJSON, strings.Join(errors, ", ")) 254 } 255 256 return nil 257 } 258 259 func (m *TransportMessage) JSON() ([]byte, error) { 260 m.mu.Lock() 261 j, err := json.Marshal(m) 262 m.mu.Unlock() 263 if err != nil { 264 return nil, err 265 } 266 267 if err = m.IsValidJSON(j); err != nil { 268 return nil, fmt.Errorf("%w: %s", ErrInvalidJSON, err) 269 } 270 271 return j, nil 272 } 273 274 func (m *TransportMessage) Version() protocol.ProtocolVersion { 275 m.mu.Lock() 276 defer m.mu.Unlock() 277 278 return m.Protocol 279 }