github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/protocol/v1/request.go (about) 1 // Copyright (c) 2017-2022, R.I. Pienaar and the Choria Project contributors 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 package v1 6 7 import ( 8 "encoding/json" 9 "fmt" 10 "strings" 11 "sync" 12 "time" 13 14 "github.com/choria-io/go-choria/protocol" 15 ) 16 17 type Request struct { 18 Protocol protocol.ProtocolVersion `json:"protocol"` 19 MessageBody string `json:"message"` 20 Envelope *RequestEnvelope `json:"envelope"` 21 22 mu sync.Mutex 23 } 24 25 type RequestEnvelope struct { 26 RequestID string `json:"requestid"` 27 SenderID string `json:"senderid"` 28 CallerID string `json:"callerid"` 29 Collective string `json:"collective"` 30 Agent string `json:"agent"` 31 TTL int `json:"ttl"` 32 Time int64 `json:"time"` 33 Filter *protocol.Filter `json:"filter"` 34 35 seenBy [][3]string 36 federation *FederationTransportHeader 37 } 38 39 // RecordNetworkHop appends a hop onto the list of those who processed this message 40 func (r *Request) RecordNetworkHop(in string, processor string, out string) { 41 r.Envelope.seenBy = append(r.Envelope.seenBy, [3]string{in, processor, out}) 42 } 43 44 // NetworkHops returns a list of tuples this messaged traveled through 45 func (r *Request) NetworkHops() [][3]string { 46 return r.Envelope.seenBy 47 } 48 49 // FederationTargets retrieves the list of targets this message is destined for 50 func (r *Request) FederationTargets() (targets []string, federated bool) { 51 r.mu.Lock() 52 defer r.mu.Unlock() 53 54 if r.Envelope.federation == nil { 55 return nil, false 56 } 57 58 return r.Envelope.federation.Targets, true 59 } 60 61 // FederationReplyTo retrieves the reply to string set by the federation broker 62 func (r *Request) FederationReplyTo() (replyto string, federated bool) { 63 r.mu.Lock() 64 defer r.mu.Unlock() 65 66 if r.Envelope.federation == nil { 67 return "", false 68 } 69 70 return r.Envelope.federation.ReplyTo, true 71 } 72 73 // FederationRequestID retrieves the federation specific requestid 74 func (r *Request) FederationRequestID() (id string, federated bool) { 75 r.mu.Lock() 76 defer r.mu.Unlock() 77 78 if r.Envelope.federation == nil { 79 return "", false 80 } 81 82 return r.Envelope.federation.RequestID, true 83 } 84 85 // SetRequestID sets the request ID for this message 86 func (r *Request) SetRequestID(id string) { 87 r.mu.Lock() 88 defer r.mu.Unlock() 89 90 r.Envelope.RequestID = id 91 } 92 93 // SetFederationTargets sets the list of hosts this message should go to. 94 // 95 // Federation brokers will duplicate the message and send one for each target 96 func (r *Request) SetFederationTargets(targets []string) { 97 r.mu.Lock() 98 defer r.mu.Unlock() 99 100 if r.Envelope.federation == nil { 101 r.Envelope.federation = &FederationTransportHeader{} 102 } 103 104 r.Envelope.federation.Targets = targets 105 } 106 107 // SetFederationReplyTo stores the original reply-to destination in the federation headers 108 func (r *Request) SetFederationReplyTo(reply string) { 109 r.mu.Lock() 110 defer r.mu.Unlock() 111 112 if r.Envelope.federation == nil { 113 r.Envelope.federation = &FederationTransportHeader{} 114 } 115 116 r.Envelope.federation.ReplyTo = reply 117 } 118 119 // SetFederationRequestID sets the request ID for federation purposes 120 func (r *Request) SetFederationRequestID(id string) { 121 r.mu.Lock() 122 defer r.mu.Unlock() 123 124 if r.Envelope.federation == nil { 125 r.Envelope.federation = &FederationTransportHeader{} 126 } 127 128 r.Envelope.federation.RequestID = id 129 } 130 131 // IsFederated determines if this message is federated 132 func (r *Request) IsFederated() bool { 133 r.mu.Lock() 134 defer r.mu.Unlock() 135 136 return r.Envelope.federation != nil 137 } 138 139 // SetUnfederated removes any federation information from the message 140 func (r *Request) SetUnfederated() { 141 r.mu.Lock() 142 defer r.mu.Unlock() 143 144 r.Envelope.federation = nil 145 } 146 147 // SetMessage set the message body thats contained in this request. It should be JSON encoded text 148 func (r *Request) SetMessage(message []byte) { 149 r.mu.Lock() 150 defer r.mu.Unlock() 151 152 r.MessageBody = string(message) 153 } 154 155 // SetCallerID sets the caller id for this request 156 func (r *Request) SetCallerID(id string) { 157 r.mu.Lock() 158 defer r.mu.Unlock() 159 160 // TODO validate it 161 r.Envelope.CallerID = id 162 } 163 164 // SetCollective sets the collective this request is directed at 165 func (r *Request) SetCollective(collective string) { 166 r.mu.Lock() 167 defer r.mu.Unlock() 168 169 r.Envelope.Collective = collective 170 } 171 172 // SetAgent sets the agent this requires is created for 173 func (r *Request) SetAgent(agent string) { 174 r.mu.Lock() 175 defer r.mu.Unlock() 176 177 r.Envelope.Agent = agent 178 } 179 180 // SetTTL sets the validity period for this message 181 func (r *Request) SetTTL(ttl int) { 182 r.mu.Lock() 183 defer r.mu.Unlock() 184 185 r.Envelope.TTL = ttl 186 } 187 188 // Message retrieves the JSON encoded Message body 189 func (r *Request) Message() []byte { 190 r.mu.Lock() 191 defer r.mu.Unlock() 192 193 return []byte(r.MessageBody) 194 } 195 196 // RequestID retrieves the unique request ID 197 func (r *Request) RequestID() string { 198 r.mu.Lock() 199 defer r.mu.Unlock() 200 201 return r.Envelope.RequestID 202 } 203 204 // SenderID retrieves the sender id that sent the message 205 func (r *Request) SenderID() string { 206 r.mu.Lock() 207 defer r.mu.Unlock() 208 209 return r.Envelope.SenderID 210 } 211 212 // CallerID retrieves the caller id that sent the message 213 func (r *Request) CallerID() string { 214 r.mu.Lock() 215 defer r.mu.Unlock() 216 217 return r.Envelope.CallerID 218 } 219 220 // Collective retrieves the name of the sub collective this message is aimed at 221 func (r *Request) Collective() string { 222 r.mu.Lock() 223 defer r.mu.Unlock() 224 225 return r.Envelope.Collective 226 } 227 228 // Agent retrieves the agent name this message is for 229 func (r *Request) Agent() string { 230 return r.Envelope.Agent 231 } 232 233 // TTL retrieves the maximum allow lifetime of this message 234 func (r *Request) TTL() int { 235 r.mu.Lock() 236 defer r.mu.Unlock() 237 238 return r.Envelope.TTL 239 } 240 241 // Time retrieves the time this message was first made 242 func (r *Request) Time() time.Time { 243 r.mu.Lock() 244 defer r.mu.Unlock() 245 246 return time.Unix(r.Envelope.Time, 0) 247 } 248 249 // Filter retrieves the filter for the message. The boolean is true when the filter is not empty 250 func (r *Request) Filter() (filter *protocol.Filter, filtered bool) { 251 r.mu.Lock() 252 defer r.mu.Unlock() 253 254 return r.Envelope.Filter, !r.Envelope.Filter.Empty() 255 } 256 257 // NewFilter creates a new empty filter and sets it 258 func (r *Request) NewFilter() *protocol.Filter { 259 r.mu.Lock() 260 defer r.mu.Unlock() 261 262 r.Envelope.Filter = protocol.NewFilter() 263 264 return r.Envelope.Filter 265 } 266 267 // JSON creates a JSON encoded request 268 func (r *Request) JSON() ([]byte, error) { 269 r.mu.Lock() 270 j, err := json.Marshal(r) 271 r.mu.Unlock() 272 if err != nil { 273 protocolErrorCtr.Inc() 274 return nil, fmt.Errorf("could not JSON Marshal: %s", err) 275 } 276 277 if err = r.IsValidJSON(j); err != nil { 278 return nil, fmt.Errorf("serialized JSON produced from the Request does not pass validation: %s", err) 279 } 280 281 return j, nil 282 } 283 284 // SetFilter sets and overwrites the filter for a message with a new one 285 func (r *Request) SetFilter(filter *protocol.Filter) { 286 r.mu.Lock() 287 defer r.mu.Unlock() 288 289 r.Envelope.Filter = filter 290 } 291 292 // Version retrieves the protocol version for this message 293 func (r *Request) Version() protocol.ProtocolVersion { 294 r.mu.Lock() 295 defer r.mu.Unlock() 296 297 return r.Protocol 298 } 299 300 // IsValidJSON validates the given JSON data against the schema 301 func (r *Request) IsValidJSON(data []byte) error { 302 _, errors, err := schemaValidate(protocol.RequestV1, data) 303 if err != nil { 304 return fmt.Errorf("could not validate Request JSON data: %s", err) 305 } 306 307 if len(errors) != 0 { 308 return fmt.Errorf("supplied JSON document is not a valid Request message: %s", strings.Join(errors, ", ")) 309 } 310 311 return nil 312 } 313 314 // CallerPublicData is not supported for version 1 messages and is always an empty string 315 func (r *Request) CallerPublicData() string { 316 return "" 317 } 318 319 // SignerPublicData is not supported for version 1 messages and is always an empty string 320 func (r *Request) SignerPublicData() string { 321 return "" 322 }