github.com/ava-labs/avalanchego@v1.11.11/network/p2p/router.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package p2p 5 6 import ( 7 "context" 8 "encoding/binary" 9 "errors" 10 "fmt" 11 "strconv" 12 "sync" 13 "time" 14 15 "github.com/prometheus/client_golang/prometheus" 16 "go.uber.org/zap" 17 18 "github.com/ava-labs/avalanchego/ids" 19 "github.com/ava-labs/avalanchego/message" 20 "github.com/ava-labs/avalanchego/snow/engine/common" 21 "github.com/ava-labs/avalanchego/utils/logging" 22 ) 23 24 var ( 25 ErrExistingAppProtocol = errors.New("existing app protocol") 26 ErrUnrequestedResponse = errors.New("unrequested response") 27 28 _ common.AppHandler = (*router)(nil) 29 ) 30 31 type pendingAppRequest struct { 32 handlerID string 33 callback AppResponseCallback 34 } 35 36 // meteredHandler emits metrics for a Handler 37 type meteredHandler struct { 38 *responder 39 metrics 40 } 41 42 type metrics struct { 43 msgTime *prometheus.GaugeVec 44 msgCount *prometheus.CounterVec 45 } 46 47 func (m *metrics) observe(labels prometheus.Labels, start time.Time) error { 48 metricTime, err := m.msgTime.GetMetricWith(labels) 49 if err != nil { 50 return err 51 } 52 53 metricCount, err := m.msgCount.GetMetricWith(labels) 54 if err != nil { 55 return err 56 } 57 58 metricTime.Add(float64(time.Since(start))) 59 metricCount.Inc() 60 return nil 61 } 62 63 // router routes incoming application messages to the corresponding registered 64 // app handler. App messages must be made using the registered handler's 65 // corresponding Client. 66 type router struct { 67 log logging.Logger 68 sender common.AppSender 69 metrics metrics 70 71 lock sync.RWMutex 72 handlers map[uint64]*meteredHandler 73 pendingAppRequests map[uint32]pendingAppRequest 74 requestID uint32 75 } 76 77 // newRouter returns a new instance of Router 78 func newRouter( 79 log logging.Logger, 80 sender common.AppSender, 81 metrics metrics, 82 ) *router { 83 return &router{ 84 log: log, 85 sender: sender, 86 metrics: metrics, 87 handlers: make(map[uint64]*meteredHandler), 88 pendingAppRequests: make(map[uint32]pendingAppRequest), 89 // invariant: sdk uses odd-numbered requestIDs 90 requestID: 1, 91 } 92 } 93 94 func (r *router) addHandler(handlerID uint64, handler Handler) error { 95 r.lock.Lock() 96 defer r.lock.Unlock() 97 98 if _, ok := r.handlers[handlerID]; ok { 99 return fmt.Errorf("failed to register handler id %d: %w", handlerID, ErrExistingAppProtocol) 100 } 101 102 r.handlers[handlerID] = &meteredHandler{ 103 responder: &responder{ 104 Handler: handler, 105 handlerID: handlerID, 106 log: r.log, 107 sender: r.sender, 108 }, 109 metrics: r.metrics, 110 } 111 112 return nil 113 } 114 115 // AppRequest routes an AppRequest to a Handler based on the handler prefix. The 116 // message is dropped if no matching handler can be found. 117 // 118 // Any error condition propagated outside Handler application logic is 119 // considered fatal 120 func (r *router) AppRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, deadline time.Time, request []byte) error { 121 start := time.Now() 122 parsedMsg, handler, handlerID, ok := r.parse(request) 123 if !ok { 124 r.log.Debug("received message for unregistered handler", 125 zap.Stringer("messageOp", message.AppRequestOp), 126 zap.Stringer("nodeID", nodeID), 127 zap.Uint32("requestID", requestID), 128 zap.Time("deadline", deadline), 129 zap.Binary("message", request), 130 ) 131 132 // Send an error back to the requesting peer. Invalid requests that we 133 // cannot parse a handler id for are handled the same way as requests 134 // for which we do not have a registered handler. 135 return r.sender.SendAppError(ctx, nodeID, requestID, ErrUnregisteredHandler.Code, ErrUnregisteredHandler.Message) 136 } 137 138 // call the corresponding handler and send back a response to nodeID 139 if err := handler.AppRequest(ctx, nodeID, requestID, deadline, parsedMsg); err != nil { 140 return err 141 } 142 143 return r.metrics.observe( 144 prometheus.Labels{ 145 opLabel: message.AppRequestOp.String(), 146 handlerLabel: handlerID, 147 }, 148 start, 149 ) 150 } 151 152 // AppRequestFailed routes an AppRequestFailed message to the callback 153 // corresponding to requestID. 154 // 155 // Any error condition propagated outside Handler application logic is 156 // considered fatal 157 func (r *router) AppRequestFailed(ctx context.Context, nodeID ids.NodeID, requestID uint32, appErr *common.AppError) error { 158 start := time.Now() 159 pending, ok := r.clearAppRequest(requestID) 160 if !ok { 161 // we should never receive a timeout without a corresponding requestID 162 return ErrUnrequestedResponse 163 } 164 165 pending.callback(ctx, nodeID, nil, appErr) 166 167 return r.metrics.observe( 168 prometheus.Labels{ 169 opLabel: message.AppErrorOp.String(), 170 handlerLabel: pending.handlerID, 171 }, 172 start, 173 ) 174 } 175 176 // AppResponse routes an AppResponse message to the callback corresponding to 177 // requestID. 178 // 179 // Any error condition propagated outside Handler application logic is 180 // considered fatal 181 func (r *router) AppResponse(ctx context.Context, nodeID ids.NodeID, requestID uint32, response []byte) error { 182 start := time.Now() 183 pending, ok := r.clearAppRequest(requestID) 184 if !ok { 185 // we should never receive a timeout without a corresponding requestID 186 return ErrUnrequestedResponse 187 } 188 189 pending.callback(ctx, nodeID, response, nil) 190 191 return r.metrics.observe( 192 prometheus.Labels{ 193 opLabel: message.AppResponseOp.String(), 194 handlerLabel: pending.handlerID, 195 }, 196 start, 197 ) 198 } 199 200 // AppGossip routes an AppGossip message to a Handler based on the handler 201 // prefix. The message is dropped if no matching handler can be found. 202 // 203 // Any error condition propagated outside Handler application logic is 204 // considered fatal 205 func (r *router) AppGossip(ctx context.Context, nodeID ids.NodeID, gossip []byte) error { 206 start := time.Now() 207 parsedMsg, handler, handlerID, ok := r.parse(gossip) 208 if !ok { 209 r.log.Debug("received message for unregistered handler", 210 zap.Stringer("messageOp", message.AppGossipOp), 211 zap.Stringer("nodeID", nodeID), 212 zap.Binary("message", gossip), 213 ) 214 return nil 215 } 216 217 handler.AppGossip(ctx, nodeID, parsedMsg) 218 219 return r.metrics.observe( 220 prometheus.Labels{ 221 opLabel: message.AppGossipOp.String(), 222 handlerLabel: handlerID, 223 }, 224 start, 225 ) 226 } 227 228 // Parse parses a gossip or request message and maps it to a corresponding 229 // handler if present. 230 // 231 // Returns: 232 // - The unprefixed protocol message. 233 // - The protocol responder. 234 // - The protocol metric name. 235 // - A boolean indicating that parsing succeeded. 236 // 237 // Invariant: Assumes [r.lock] isn't held. 238 func (r *router) parse(prefixedMsg []byte) ([]byte, *meteredHandler, string, bool) { 239 handlerID, msg, ok := ParseMessage(prefixedMsg) 240 if !ok { 241 return nil, nil, "", false 242 } 243 244 handlerStr := strconv.FormatUint(handlerID, 10) 245 246 r.lock.RLock() 247 defer r.lock.RUnlock() 248 249 handler, ok := r.handlers[handlerID] 250 return msg, handler, handlerStr, ok 251 } 252 253 // Invariant: Assumes [r.lock] isn't held. 254 func (r *router) clearAppRequest(requestID uint32) (pendingAppRequest, bool) { 255 r.lock.Lock() 256 defer r.lock.Unlock() 257 258 callback, ok := r.pendingAppRequests[requestID] 259 delete(r.pendingAppRequests, requestID) 260 return callback, ok 261 } 262 263 // Parse a gossip or request message. 264 // 265 // Returns: 266 // - The protocol ID. 267 // - The unprefixed protocol message. 268 // - A boolean indicating that parsing succeeded. 269 func ParseMessage(msg []byte) (uint64, []byte, bool) { 270 handlerID, bytesRead := binary.Uvarint(msg) 271 if bytesRead <= 0 { 272 return 0, nil, false 273 } 274 return handlerID, msg[bytesRead:], true 275 }