github.com/nats-io/nats-server/v2@v2.11.0-preview.2/server/gateway.go (about) 1 // Copyright 2018-2024 The NATS Authors 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package server 15 16 import ( 17 "bytes" 18 "crypto/sha256" 19 "crypto/tls" 20 "encoding/json" 21 "fmt" 22 "math/rand" 23 "net" 24 "net/url" 25 "sort" 26 "strconv" 27 "sync" 28 "sync/atomic" 29 "time" 30 ) 31 32 const ( 33 defaultSolicitGatewaysDelay = time.Second 34 defaultGatewayConnectDelay = time.Second 35 defaultGatewayReconnectDelay = time.Second 36 defaultGatewayRecentSubExpiration = 2 * time.Second 37 defaultGatewayMaxRUnsubBeforeSwitch = 1000 38 39 oldGWReplyPrefix = "$GR." 40 oldGWReplyPrefixLen = len(oldGWReplyPrefix) 41 oldGWReplyStart = oldGWReplyPrefixLen + 5 // len of prefix above + len of hash (4) + "." 42 43 // The new prefix is "_GR_.<cluster>.<server>." where <cluster> is 6 characters 44 // hash of origin cluster name and <server> is 6 characters hash of origin server pub key. 45 gwReplyPrefix = "_GR_." 46 gwReplyPrefixLen = len(gwReplyPrefix) 47 gwHashLen = 6 48 gwClusterOffset = gwReplyPrefixLen 49 gwServerOffset = gwClusterOffset + gwHashLen + 1 50 gwSubjectOffset = gwServerOffset + gwHashLen + 1 51 52 // Gateway connections send PINGs regardless of traffic. The interval is 53 // either Options.PingInterval or this value, whichever is the smallest. 54 gwMaxPingInterval = 15 * time.Second 55 ) 56 57 var ( 58 gatewayConnectDelay = defaultGatewayConnectDelay 59 gatewayReconnectDelay = defaultGatewayReconnectDelay 60 gatewayMaxRUnsubBeforeSwitch = defaultGatewayMaxRUnsubBeforeSwitch 61 gatewaySolicitDelay = int64(defaultSolicitGatewaysDelay) 62 gatewayMaxPingInterval = gwMaxPingInterval 63 ) 64 65 // Warning when user configures gateway TLS insecure 66 const gatewayTLSInsecureWarning = "TLS certificate chain and hostname of solicited gateways will not be verified. DO NOT USE IN PRODUCTION!" 67 68 // SetGatewaysSolicitDelay sets the initial delay before gateways 69 // connections are initiated. 70 // Used by tests. 71 func SetGatewaysSolicitDelay(delay time.Duration) { 72 atomic.StoreInt64(&gatewaySolicitDelay, int64(delay)) 73 } 74 75 // ResetGatewaysSolicitDelay resets the initial delay before gateways 76 // connections are initiated to its default values. 77 // Used by tests. 78 func ResetGatewaysSolicitDelay() { 79 atomic.StoreInt64(&gatewaySolicitDelay, int64(defaultSolicitGatewaysDelay)) 80 } 81 82 const ( 83 gatewayCmdGossip byte = 1 84 gatewayCmdAllSubsStart byte = 2 85 gatewayCmdAllSubsComplete byte = 3 86 ) 87 88 // GatewayInterestMode represents an account interest mode for a gateway connection 89 type GatewayInterestMode byte 90 91 // GatewayInterestMode values 92 const ( 93 // optimistic is the default mode where a cluster will send 94 // to a gateway unless it is been told that there is no interest 95 // (this is for plain subscribers only). 96 Optimistic GatewayInterestMode = iota 97 // transitioning is when a gateway has to send too many 98 // no interest on subjects to the remote and decides that it is 99 // now time to move to modeInterestOnly (this is on a per account 100 // basis). 101 Transitioning 102 // interestOnly means that a cluster sends all it subscriptions 103 // interest to the gateway, which in return does not send a message 104 // unless it knows that there is explicit interest. 105 InterestOnly 106 ) 107 108 func (im GatewayInterestMode) String() string { 109 switch im { 110 case Optimistic: 111 return "Optimistic" 112 case InterestOnly: 113 return "Interest-Only" 114 case Transitioning: 115 return "Transitioning" 116 default: 117 return "Unknown" 118 } 119 } 120 121 var gwDoNotForceInterestOnlyMode bool 122 123 // GatewayDoNotForceInterestOnlyMode is used ONLY in tests. 124 // DO NOT USE in normal code or if you embed the NATS Server. 125 func GatewayDoNotForceInterestOnlyMode(doNotForce bool) { 126 gwDoNotForceInterestOnlyMode = doNotForce 127 } 128 129 type srvGateway struct { 130 totalQSubs int64 //total number of queue subs in all remote gateways (used with atomic operations) 131 sync.RWMutex 132 enabled bool // Immutable, true if both a name and port are configured 133 name string // Name of the Gateway on this server 134 out map[string]*client // outbound gateways 135 outo []*client // outbound gateways maintained in an order suitable for sending msgs (currently based on RTT) 136 in map[uint64]*client // inbound gateways 137 remotes map[string]*gatewayCfg // Config of remote gateways 138 URLs refCountedUrlSet // Set of all Gateway URLs in the cluster 139 URL string // This server gateway URL (after possible random port is resolved) 140 info *Info // Gateway Info protocol 141 infoJSON []byte // Marshal'ed Info protocol 142 runknown bool // Rejects unknown (not configured) gateway connections 143 replyPfx []byte // Will be "$GNR.<1:reserved>.<8:cluster hash>.<8:server hash>." 144 145 // For backward compatibility 146 oldReplyPfx []byte 147 oldHash []byte 148 149 // We maintain the interest of subjects and queues per account. 150 // For a given account, entries in the map could be something like this: 151 // foo.bar {n: 3} // 3 subs on foo.bar 152 // foo.> {n: 6} // 6 subs on foo.> 153 // foo bar {n: 1, q: true} // 1 qsub on foo, queue bar 154 // foo baz {n: 3, q: true} // 3 qsubs on foo, queue baz 155 pasi struct { 156 // Protect map since accessed from different go-routine and avoid 157 // possible race resulting in RS+ being sent before RS- resulting 158 // in incorrect interest suppression. 159 // Will use while sending QSubs (on GW connection accept) and when 160 // switching to the send-all-subs mode. 161 sync.Mutex 162 m map[string]map[string]*sitally 163 } 164 165 // This is to track recent subscriptions for a given account 166 rsubs sync.Map 167 168 resolver netResolver // Used to resolve host name before calling net.Dial() 169 sqbsz int // Max buffer size to send queue subs protocol. Used for testing. 170 recSubExp time.Duration // For how long do we check if there is a subscription match for a message with reply 171 172 // These are used for routing of mapped replies. 173 sIDHash []byte // Server ID hash (6 bytes) 174 routesIDByHash sync.Map // Route's server ID is hashed (6 bytes) and stored in this map. 175 176 // If a server has its own configuration in the "Gateways" remotes configuration 177 // we will keep track of the URLs that are defined in the config so they can 178 // be reported in monitoring. 179 ownCfgURLs []string 180 } 181 182 // Subject interest tally. Also indicates if the key in the map is a 183 // queue or not. 184 type sitally struct { 185 n int32 // number of subscriptions directly matching 186 q bool // indicate that this is a queue 187 } 188 189 type gatewayCfg struct { 190 sync.RWMutex 191 *RemoteGatewayOpts 192 hash []byte 193 oldHash []byte 194 urls map[string]*url.URL 195 connAttempts int 196 tlsName string 197 implicit bool 198 varzUpdateURLs bool // Tells monitoring code to update URLs when varz is inspected. 199 } 200 201 // Struct for client's gateway related fields 202 type gateway struct { 203 name string 204 cfg *gatewayCfg 205 connectURL *url.URL // Needed when sending CONNECT after receiving INFO from remote 206 outsim *sync.Map // Per-account subject interest (or no-interest) (outbound conn) 207 insim map[string]*insie // Per-account subject no-interest sent or modeInterestOnly mode (inbound conn) 208 209 // This is an outbound GW connection 210 outbound bool 211 // Set/check in readLoop without lock. This is to know that an inbound has sent the CONNECT protocol first 212 connected bool 213 // Set to true if outbound is to a server that only knows about $GR, not $GNR 214 useOldPrefix bool 215 // If true, it indicates that the inbound side will switch any account to 216 // interest-only mode "immediately", so the outbound should disregard 217 // the optimistic mode when checking for interest. 218 interestOnlyMode bool 219 // Name of the remote server 220 remoteName string 221 } 222 223 // Outbound subject interest entry. 224 type outsie struct { 225 sync.RWMutex 226 // Indicate that all subs should be stored. This is 227 // set to true when receiving the command from the 228 // remote that we are about to receive all its subs. 229 mode GatewayInterestMode 230 // If not nil, used for no-interest for plain subs. 231 // If a subject is present in this map, it means that 232 // the remote is not interested in that subject. 233 // When we have received the command that says that 234 // the remote has sent all its subs, this is set to nil. 235 ni map[string]struct{} 236 // Contains queue subscriptions when in optimistic mode, 237 // and all subs when pk is > 0. 238 sl *Sublist 239 // Number of queue subs 240 qsubs int 241 } 242 243 // Inbound subject interest entry. 244 // If `ni` is not nil, it stores the subjects for which an 245 // RS- was sent to the remote gateway. When a subscription 246 // is created, this is used to know if we need to send 247 // an RS+ to clear the no-interest in the remote. 248 // When an account is switched to modeInterestOnly (we send 249 // all subs of an account to the remote), then `ni` is nil and 250 // when all subs have been sent, mode is set to modeInterestOnly 251 type insie struct { 252 ni map[string]struct{} // Record if RS- was sent for given subject 253 mode GatewayInterestMode 254 } 255 256 type gwReplyMap struct { 257 ms string 258 exp int64 259 } 260 261 type gwReplyMapping struct { 262 // Indicate if we should check the map or not. Since checking the map is done 263 // when processing inbound messages and requires the lock we want to 264 // check only when needed. This is set/get using atomic, so needs to 265 // be memory aligned. 266 check int32 267 // To keep track of gateway replies mapping 268 mapping map[string]*gwReplyMap 269 } 270 271 // Returns the corresponding gw routed subject, and `true` to indicate that a 272 // mapping was found. If no entry is found, the passed subject is returned 273 // as-is and `false` is returned to indicate that no mapping was found. 274 // Caller is responsible to ensure the locking. 275 func (g *gwReplyMapping) get(subject []byte) ([]byte, bool) { 276 rm, ok := g.mapping[string(subject)] 277 if !ok { 278 return subject, false 279 } 280 subj := []byte(rm.ms) 281 return subj, true 282 } 283 284 // clone returns a deep copy of the RemoteGatewayOpts object 285 func (r *RemoteGatewayOpts) clone() *RemoteGatewayOpts { 286 if r == nil { 287 return nil 288 } 289 clone := &RemoteGatewayOpts{ 290 Name: r.Name, 291 URLs: deepCopyURLs(r.URLs), 292 } 293 if r.TLSConfig != nil { 294 clone.TLSConfig = r.TLSConfig.Clone() 295 clone.TLSTimeout = r.TLSTimeout 296 } 297 return clone 298 } 299 300 // Ensure that gateway is properly configured. 301 func validateGatewayOptions(o *Options) error { 302 if o.Gateway.Name == "" && o.Gateway.Port == 0 { 303 return nil 304 } 305 if o.Gateway.Name == "" { 306 return fmt.Errorf("gateway has no name") 307 } 308 if o.Gateway.Port == 0 { 309 return fmt.Errorf("gateway %q has no port specified (select -1 for random port)", o.Gateway.Name) 310 } 311 for i, g := range o.Gateway.Gateways { 312 if g.Name == "" { 313 return fmt.Errorf("gateway in the list %d has no name", i) 314 } 315 if len(g.URLs) == 0 { 316 return fmt.Errorf("gateway %q has no URL", g.Name) 317 } 318 } 319 if err := validatePinnedCerts(o.Gateway.TLSPinnedCerts); err != nil { 320 return fmt.Errorf("gateway %q: %v", o.Gateway.Name, err) 321 } 322 return nil 323 } 324 325 // Computes a hash of 6 characters for the name. 326 // This will be used for routing of replies. 327 func getGWHash(name string) []byte { 328 return []byte(getHashSize(name, gwHashLen)) 329 } 330 331 func getOldHash(name string) []byte { 332 sha := sha256.New() 333 sha.Write([]byte(name)) 334 fullHash := []byte(fmt.Sprintf("%x", sha.Sum(nil))) 335 return fullHash[:4] 336 } 337 338 // Initialize the s.gateway structure. We do this even if the server 339 // does not have a gateway configured. In some part of the code, the 340 // server will check the number of outbound gateways, etc.. and so 341 // we don't have to check if s.gateway is nil or not. 342 func (s *Server) newGateway(opts *Options) error { 343 gateway := &srvGateway{ 344 name: opts.Gateway.Name, 345 out: make(map[string]*client), 346 outo: make([]*client, 0, 4), 347 in: make(map[uint64]*client), 348 remotes: make(map[string]*gatewayCfg), 349 URLs: make(refCountedUrlSet), 350 resolver: opts.Gateway.resolver, 351 runknown: opts.Gateway.RejectUnknown, 352 oldHash: getOldHash(opts.Gateway.Name), 353 } 354 gateway.Lock() 355 defer gateway.Unlock() 356 357 gateway.sIDHash = getGWHash(s.info.ID) 358 clusterHash := getGWHash(opts.Gateway.Name) 359 prefix := make([]byte, 0, gwSubjectOffset) 360 prefix = append(prefix, gwReplyPrefix...) 361 prefix = append(prefix, clusterHash...) 362 prefix = append(prefix, '.') 363 prefix = append(prefix, gateway.sIDHash...) 364 prefix = append(prefix, '.') 365 gateway.replyPfx = prefix 366 367 prefix = make([]byte, 0, oldGWReplyStart) 368 prefix = append(prefix, oldGWReplyPrefix...) 369 prefix = append(prefix, gateway.oldHash...) 370 prefix = append(prefix, '.') 371 gateway.oldReplyPfx = prefix 372 373 gateway.pasi.m = make(map[string]map[string]*sitally) 374 375 if gateway.resolver == nil { 376 gateway.resolver = netResolver(net.DefaultResolver) 377 } 378 379 // Create remote gateways 380 for _, rgo := range opts.Gateway.Gateways { 381 // Ignore if there is a remote gateway with our name. 382 if rgo.Name == gateway.name { 383 gateway.ownCfgURLs = getURLsAsString(rgo.URLs) 384 continue 385 } 386 cfg := &gatewayCfg{ 387 RemoteGatewayOpts: rgo.clone(), 388 hash: getGWHash(rgo.Name), 389 oldHash: getOldHash(rgo.Name), 390 urls: make(map[string]*url.URL, len(rgo.URLs)), 391 } 392 if opts.Gateway.TLSConfig != nil && cfg.TLSConfig == nil { 393 cfg.TLSConfig = opts.Gateway.TLSConfig.Clone() 394 } 395 if cfg.TLSTimeout == 0 { 396 cfg.TLSTimeout = opts.Gateway.TLSTimeout 397 } 398 for _, u := range rgo.URLs { 399 // For TLS, look for a hostname that we can use for TLSConfig.ServerName 400 cfg.saveTLSHostname(u) 401 cfg.urls[u.Host] = u 402 } 403 gateway.remotes[cfg.Name] = cfg 404 } 405 406 gateway.sqbsz = opts.Gateway.sendQSubsBufSize 407 if gateway.sqbsz == 0 { 408 gateway.sqbsz = maxBufSize 409 } 410 gateway.recSubExp = defaultGatewayRecentSubExpiration 411 412 gateway.enabled = opts.Gateway.Name != "" && opts.Gateway.Port != 0 413 s.gateway = gateway 414 return nil 415 } 416 417 // Update remote gateways TLS configurations after a config reload. 418 func (g *srvGateway) updateRemotesTLSConfig(opts *Options) { 419 g.Lock() 420 defer g.Unlock() 421 422 for _, ro := range opts.Gateway.Gateways { 423 if ro.Name == g.name { 424 continue 425 } 426 if cfg, ok := g.remotes[ro.Name]; ok { 427 cfg.Lock() 428 // If TLS config is in remote, use that one, otherwise, 429 // use the TLS config from the main block. 430 if ro.TLSConfig != nil { 431 cfg.TLSConfig = ro.TLSConfig.Clone() 432 } else if opts.Gateway.TLSConfig != nil { 433 cfg.TLSConfig = opts.Gateway.TLSConfig.Clone() 434 } 435 436 // Ensure that OCSP callbacks are always setup after a reload if needed. 437 mustStaple := opts.OCSPConfig != nil && opts.OCSPConfig.Mode == OCSPModeAlways 438 if mustStaple && opts.Gateway.TLSConfig != nil { 439 clientCB := opts.Gateway.TLSConfig.GetClientCertificate 440 verifyCB := opts.Gateway.TLSConfig.VerifyConnection 441 if mustStaple && cfg.TLSConfig != nil { 442 if clientCB != nil && cfg.TLSConfig.GetClientCertificate == nil { 443 cfg.TLSConfig.GetClientCertificate = clientCB 444 } 445 if verifyCB != nil && cfg.TLSConfig.VerifyConnection == nil { 446 cfg.TLSConfig.VerifyConnection = verifyCB 447 } 448 } 449 } 450 451 cfg.Unlock() 452 } 453 } 454 } 455 456 // Returns if this server rejects connections from gateways that are not 457 // explicitly configured. 458 func (g *srvGateway) rejectUnknown() bool { 459 g.RLock() 460 reject := g.runknown 461 g.RUnlock() 462 return reject 463 } 464 465 // Starts the gateways accept loop and solicit explicit gateways 466 // after an initial delay. This delay is meant to give a chance to 467 // the cluster to form and this server gathers gateway URLs for this 468 // cluster in order to send that as part of the connect/info process. 469 func (s *Server) startGateways() { 470 s.startGatewayAcceptLoop() 471 472 // Delay start of creation of gateways to give a chance 473 // to the local cluster to form. 474 s.startGoRoutine(func() { 475 defer s.grWG.Done() 476 477 dur := s.getOpts().gatewaysSolicitDelay 478 if dur == 0 { 479 dur = time.Duration(atomic.LoadInt64(&gatewaySolicitDelay)) 480 } 481 482 select { 483 case <-time.After(dur): 484 s.solicitGateways() 485 case <-s.quitCh: 486 return 487 } 488 }) 489 } 490 491 // This starts the gateway accept loop in a go routine, unless it 492 // is detected that the server has already been shutdown. 493 func (s *Server) startGatewayAcceptLoop() { 494 if s.isShuttingDown() { 495 return 496 } 497 498 // Snapshot server options. 499 opts := s.getOpts() 500 501 port := opts.Gateway.Port 502 if port == -1 { 503 port = 0 504 } 505 506 s.mu.Lock() 507 hp := net.JoinHostPort(opts.Gateway.Host, strconv.Itoa(port)) 508 l, e := natsListen("tcp", hp) 509 s.gatewayListenerErr = e 510 if e != nil { 511 s.mu.Unlock() 512 s.Fatalf("Error listening on gateway port: %d - %v", opts.Gateway.Port, e) 513 return 514 } 515 s.Noticef("Gateway name is %s", s.getGatewayName()) 516 s.Noticef("Listening for gateways connections on %s", 517 net.JoinHostPort(opts.Gateway.Host, strconv.Itoa(l.Addr().(*net.TCPAddr).Port))) 518 519 tlsReq := opts.Gateway.TLSConfig != nil 520 authRequired := opts.Gateway.Username != "" 521 info := &Info{ 522 ID: s.info.ID, 523 Name: opts.ServerName, 524 Version: s.info.Version, 525 AuthRequired: authRequired, 526 TLSRequired: tlsReq, 527 TLSVerify: tlsReq, 528 MaxPayload: s.info.MaxPayload, 529 Gateway: opts.Gateway.Name, 530 GatewayNRP: true, 531 Headers: s.supportsHeaders(), 532 Proto: s.getServerProto(), 533 } 534 // Unless in some tests we want to keep the old behavior, we are now 535 // (since v2.9.0) indicate that this server will switch all accounts 536 // to InterestOnly mode when accepting an inbound or when a new 537 // account is fetched. 538 if !gwDoNotForceInterestOnlyMode { 539 info.GatewayIOM = true 540 } 541 542 // If we have selected a random port... 543 if port == 0 { 544 // Write resolved port back to options. 545 opts.Gateway.Port = l.Addr().(*net.TCPAddr).Port 546 } 547 // Possibly override Host/Port based on Gateway.Advertise 548 if err := s.setGatewayInfoHostPort(info, opts); err != nil { 549 s.Fatalf("Error setting gateway INFO with Gateway.Advertise value of %s, err=%v", opts.Gateway.Advertise, err) 550 l.Close() 551 s.mu.Unlock() 552 return 553 } 554 // Setup state that can enable shutdown 555 s.gatewayListener = l 556 557 // Warn if insecure is configured in the main Gateway configuration 558 // or any of the RemoteGateway's. This means that we need to check 559 // remotes even if TLS would not be configured for the accept. 560 warn := tlsReq && opts.Gateway.TLSConfig.InsecureSkipVerify 561 if !warn { 562 for _, g := range opts.Gateway.Gateways { 563 if g.TLSConfig != nil && g.TLSConfig.InsecureSkipVerify { 564 warn = true 565 break 566 } 567 } 568 } 569 if warn { 570 s.Warnf(gatewayTLSInsecureWarning) 571 } 572 go s.acceptConnections(l, "Gateway", func(conn net.Conn) { s.createGateway(nil, nil, conn) }, nil) 573 s.mu.Unlock() 574 } 575 576 // Similar to setInfoHostPortAndGenerateJSON, but for gatewayInfo. 577 func (s *Server) setGatewayInfoHostPort(info *Info, o *Options) error { 578 gw := s.gateway 579 gw.Lock() 580 defer gw.Unlock() 581 gw.URLs.removeUrl(gw.URL) 582 if o.Gateway.Advertise != "" { 583 advHost, advPort, err := parseHostPort(o.Gateway.Advertise, o.Gateway.Port) 584 if err != nil { 585 return err 586 } 587 info.Host = advHost 588 info.Port = advPort 589 } else { 590 info.Host = o.Gateway.Host 591 info.Port = o.Gateway.Port 592 // If the host is "0.0.0.0" or "::" we need to resolve to a public IP. 593 // This will return at most 1 IP. 594 hostIsIPAny, ips, err := s.getNonLocalIPsIfHostIsIPAny(info.Host, false) 595 if err != nil { 596 return err 597 } 598 if hostIsIPAny { 599 if len(ips) == 0 { 600 // TODO(ik): Should we fail here (prevent starting)? If not, we 601 // are going to "advertise" the 0.0.0.0:<port> url, which means 602 // that remote are going to try to connect to 0.0.0.0:<port>, 603 // which means a connect to loopback address, which is going 604 // to fail with either TLS error, conn refused if the remote 605 // is using different gateway port than this one, or error 606 // saying that it tried to connect to itself. 607 s.Errorf("Could not find any non-local IP for gateway %q with listen specification %q", 608 gw.name, info.Host) 609 } else { 610 // Take the first from the list... 611 info.Host = ips[0] 612 } 613 } 614 } 615 gw.URL = net.JoinHostPort(info.Host, strconv.Itoa(info.Port)) 616 if o.Gateway.Advertise != "" { 617 s.Noticef("Advertise address for gateway %q is set to %s", gw.name, gw.URL) 618 } else { 619 s.Noticef("Address for gateway %q is %s", gw.name, gw.URL) 620 } 621 gw.URLs[gw.URL]++ 622 gw.info = info 623 info.GatewayURL = gw.URL 624 // (re)generate the gatewayInfoJSON byte array 625 gw.generateInfoJSON() 626 return nil 627 } 628 629 // Generates the Gateway INFO protocol. 630 // The gateway lock is held on entry 631 func (g *srvGateway) generateInfoJSON() { 632 // We could be here when processing a route INFO that has a gateway URL, 633 // but this server is not configured for gateways, so simply ignore here. 634 // The configuration mismatch is reported somewhere else. 635 if !g.enabled || g.info == nil { 636 return 637 } 638 g.info.GatewayURLs = g.URLs.getAsStringSlice() 639 b, err := json.Marshal(g.info) 640 if err != nil { 641 panic(err) 642 } 643 g.infoJSON = []byte(fmt.Sprintf(InfoProto, b)) 644 } 645 646 // Goes through the list of registered gateways and try to connect to those. 647 // The list (remotes) is initially containing the explicit remote gateways, 648 // but the list is augmented with any implicit (discovered) gateway. Therefore, 649 // this function only solicit explicit ones. 650 func (s *Server) solicitGateways() { 651 gw := s.gateway 652 gw.RLock() 653 defer gw.RUnlock() 654 for _, cfg := range gw.remotes { 655 // Since we delay the creation of gateways, it is 656 // possible that server starts to receive inbound from 657 // other clusters and in turn create outbounds. So here 658 // we create only the ones that are configured. 659 if !cfg.isImplicit() { 660 cfg := cfg // Create new instance for the goroutine. 661 s.startGoRoutine(func() { 662 s.solicitGateway(cfg, true) 663 s.grWG.Done() 664 }) 665 } 666 } 667 } 668 669 // Reconnect to the gateway after a little wait period. For explicit 670 // gateways, we also wait for the default reconnect time. 671 func (s *Server) reconnectGateway(cfg *gatewayCfg) { 672 defer s.grWG.Done() 673 674 delay := time.Duration(rand.Intn(100)) * time.Millisecond 675 if !cfg.isImplicit() { 676 delay += gatewayReconnectDelay 677 } 678 select { 679 case <-time.After(delay): 680 case <-s.quitCh: 681 return 682 } 683 s.solicitGateway(cfg, false) 684 } 685 686 // This function will loop trying to connect to any URL attached 687 // to the given Gateway. It will return once a connection has been created. 688 func (s *Server) solicitGateway(cfg *gatewayCfg, firstConnect bool) { 689 var ( 690 opts = s.getOpts() 691 isImplicit = cfg.isImplicit() 692 attempts int 693 typeStr string 694 ) 695 if isImplicit { 696 typeStr = "implicit" 697 } else { 698 typeStr = "explicit" 699 } 700 701 const connFmt = "Connecting to %s gateway %q (%s) at %s (attempt %v)" 702 const connErrFmt = "Error connecting to %s gateway %q (%s) at %s (attempt %v): %v" 703 704 for s.isRunning() { 705 urls := cfg.getURLs() 706 if len(urls) == 0 { 707 break 708 } 709 attempts++ 710 report := s.shouldReportConnectErr(firstConnect, attempts) 711 // Iteration is random 712 for _, u := range urls { 713 address, err := s.getRandomIP(s.gateway.resolver, u.Host, nil) 714 if err != nil { 715 s.Errorf("Error getting IP for %s gateway %q (%s): %v", typeStr, cfg.Name, u.Host, err) 716 continue 717 } 718 if report { 719 s.Noticef(connFmt, typeStr, cfg.Name, u.Host, address, attempts) 720 } else { 721 s.Debugf(connFmt, typeStr, cfg.Name, u.Host, address, attempts) 722 } 723 conn, err := natsDialTimeout("tcp", address, DEFAULT_ROUTE_DIAL) 724 if err == nil { 725 // We could connect, create the gateway connection and return. 726 s.createGateway(cfg, u, conn) 727 return 728 } 729 if report { 730 s.Errorf(connErrFmt, typeStr, cfg.Name, u.Host, address, attempts, err) 731 } else { 732 s.Debugf(connErrFmt, typeStr, cfg.Name, u.Host, address, attempts, err) 733 } 734 // Break this loop if server is being shutdown... 735 if !s.isRunning() { 736 break 737 } 738 } 739 if isImplicit { 740 if opts.Gateway.ConnectRetries == 0 || attempts > opts.Gateway.ConnectRetries { 741 s.gateway.Lock() 742 // We could have just accepted an inbound for this remote gateway. 743 // So if there is an inbound, let's try again to connect. 744 if s.gateway.hasInbound(cfg.Name) { 745 s.gateway.Unlock() 746 continue 747 } 748 delete(s.gateway.remotes, cfg.Name) 749 s.gateway.Unlock() 750 return 751 } 752 } 753 select { 754 case <-s.quitCh: 755 return 756 case <-time.After(gatewayConnectDelay): 757 continue 758 } 759 } 760 } 761 762 // Returns true if there is an inbound for the given `name`. 763 // Lock held on entry. 764 func (g *srvGateway) hasInbound(name string) bool { 765 for _, ig := range g.in { 766 ig.mu.Lock() 767 igname := ig.gw.name 768 ig.mu.Unlock() 769 if igname == name { 770 return true 771 } 772 } 773 return false 774 } 775 776 // Called when a gateway connection is either accepted or solicited. 777 // If accepted, the gateway is marked as inbound. 778 // If solicited, the gateway is marked as outbound. 779 func (s *Server) createGateway(cfg *gatewayCfg, url *url.URL, conn net.Conn) { 780 // Snapshot server options. 781 opts := s.getOpts() 782 783 now := time.Now() 784 c := &client{srv: s, nc: conn, start: now, last: now, kind: GATEWAY} 785 786 // Are we creating the gateway based on the configuration 787 solicit := cfg != nil 788 var tlsRequired bool 789 790 s.gateway.RLock() 791 infoJSON := s.gateway.infoJSON 792 s.gateway.RUnlock() 793 794 // Perform some initialization under the client lock 795 c.mu.Lock() 796 c.initClient() 797 c.gw = &gateway{} 798 if solicit { 799 // This is an outbound gateway connection 800 cfg.RLock() 801 tlsRequired = cfg.TLSConfig != nil 802 cfgName := cfg.Name 803 cfg.RUnlock() 804 c.gw.outbound = true 805 c.gw.name = cfgName 806 c.gw.cfg = cfg 807 cfg.bumpConnAttempts() 808 // Since we are delaying the connect until after receiving 809 // the remote's INFO protocol, save the URL we need to connect to. 810 c.gw.connectURL = url 811 812 c.Noticef("Creating outbound gateway connection to %q", cfgName) 813 } else { 814 c.flags.set(expectConnect) 815 // Inbound gateway connection 816 c.Noticef("Processing inbound gateway connection") 817 // Check if TLS is required for inbound GW connections. 818 tlsRequired = opts.Gateway.TLSConfig != nil 819 // We expect a CONNECT from the accepted connection. 820 c.setAuthTimer(secondsToDuration(opts.Gateway.AuthTimeout)) 821 } 822 823 // Check for TLS 824 if tlsRequired { 825 var tlsConfig *tls.Config 826 var tlsName string 827 var timeout float64 828 829 if solicit { 830 var ( 831 mustStaple = opts.OCSPConfig != nil && opts.OCSPConfig.Mode == OCSPModeAlways 832 clientCB func(*tls.CertificateRequestInfo) (*tls.Certificate, error) 833 verifyCB func(tls.ConnectionState) error 834 ) 835 // Snapshot callbacks for OCSP outside an ongoing reload which might be happening. 836 if mustStaple { 837 s.reloadMu.RLock() 838 s.optsMu.RLock() 839 clientCB = s.opts.Gateway.TLSConfig.GetClientCertificate 840 verifyCB = s.opts.Gateway.TLSConfig.VerifyConnection 841 s.optsMu.RUnlock() 842 s.reloadMu.RUnlock() 843 } 844 845 cfg.RLock() 846 tlsName = cfg.tlsName 847 tlsConfig = cfg.TLSConfig.Clone() 848 timeout = cfg.TLSTimeout 849 850 // Ensure that OCSP callbacks are always setup on gateway reconnect when OCSP policy is set to always. 851 if mustStaple { 852 if clientCB != nil && tlsConfig.GetClientCertificate == nil { 853 tlsConfig.GetClientCertificate = clientCB 854 } 855 if verifyCB != nil && tlsConfig.VerifyConnection == nil { 856 tlsConfig.VerifyConnection = verifyCB 857 } 858 } 859 cfg.RUnlock() 860 } else { 861 tlsConfig = opts.Gateway.TLSConfig 862 timeout = opts.Gateway.TLSTimeout 863 } 864 865 // Perform (either server or client side) TLS handshake. 866 if resetTLSName, err := c.doTLSHandshake("gateway", solicit, url, tlsConfig, tlsName, timeout, opts.Gateway.TLSPinnedCerts); err != nil { 867 if resetTLSName { 868 cfg.Lock() 869 cfg.tlsName = _EMPTY_ 870 cfg.Unlock() 871 } 872 c.mu.Unlock() 873 return 874 } 875 } 876 877 // Do final client initialization 878 c.in.pacache = make(map[string]*perAccountCache) 879 if solicit { 880 // This is an outbound gateway connection 881 c.gw.outsim = &sync.Map{} 882 } else { 883 // Inbound gateway connection 884 c.gw.insim = make(map[string]*insie) 885 } 886 887 // Register in temp map for now until gateway properly registered 888 // in out or in gateways. 889 if !s.addToTempClients(c.cid, c) { 890 c.mu.Unlock() 891 c.closeConnection(ServerShutdown) 892 return 893 } 894 895 // Only send if we accept a connection. Will send CONNECT+INFO as an 896 // outbound only after processing peer's INFO protocol. 897 if !solicit { 898 c.enqueueProto(infoJSON) 899 } 900 901 // Spin up the read loop. 902 s.startGoRoutine(func() { c.readLoop(nil) }) 903 904 // Spin up the write loop. 905 s.startGoRoutine(func() { c.writeLoop() }) 906 907 if tlsRequired { 908 c.Debugf("TLS handshake complete") 909 cs := c.nc.(*tls.Conn).ConnectionState() 910 c.Debugf("TLS version %s, cipher suite %s", tlsVersion(cs.Version), tlsCipher(cs.CipherSuite)) 911 } 912 913 c.mu.Unlock() 914 915 // Announce ourselves again to new connections. 916 if solicit && s.EventsEnabled() { 917 s.sendStatszUpdate() 918 } 919 } 920 921 // Builds and sends the CONNECT protocol for a gateway. 922 // Client lock held on entry. 923 func (c *client) sendGatewayConnect(opts *Options) { 924 // FIXME: This can race with updateRemotesTLSConfig 925 tlsRequired := c.gw.cfg.TLSConfig != nil 926 url := c.gw.connectURL 927 c.gw.connectURL = nil 928 var user, pass string 929 if userInfo := url.User; userInfo != nil { 930 user = userInfo.Username() 931 pass, _ = userInfo.Password() 932 } else if opts != nil { 933 user = opts.Gateway.Username 934 pass = opts.Gateway.Password 935 } 936 cinfo := connectInfo{ 937 Verbose: false, 938 Pedantic: false, 939 User: user, 940 Pass: pass, 941 TLS: tlsRequired, 942 Name: c.srv.info.ID, 943 Gateway: c.srv.gateway.name, 944 } 945 b, err := json.Marshal(cinfo) 946 if err != nil { 947 panic(err) 948 } 949 c.enqueueProto([]byte(fmt.Sprintf(ConProto, b))) 950 } 951 952 // Process the CONNECT protocol from a gateway connection. 953 // Returns an error to the connection if the CONNECT is not from a gateway 954 // (for instance a client or route connecting to the gateway port), or 955 // if the destination does not match the gateway name of this server. 956 // 957 // <Invoked from inbound connection's readLoop> 958 func (c *client) processGatewayConnect(arg []byte) error { 959 connect := &connectInfo{} 960 if err := json.Unmarshal(arg, connect); err != nil { 961 return err 962 } 963 964 // Coming from a client or a route, reject 965 if connect.Gateway == "" { 966 c.sendErrAndErr(ErrClientOrRouteConnectedToGatewayPort.Error()) 967 c.closeConnection(WrongPort) 968 return ErrClientOrRouteConnectedToGatewayPort 969 } 970 971 c.mu.Lock() 972 s := c.srv 973 c.mu.Unlock() 974 975 // If we reject unknown gateways, make sure we have it configured, 976 // otherwise return an error. 977 if s.gateway.rejectUnknown() && s.getRemoteGateway(connect.Gateway) == nil { 978 c.Errorf("Rejecting connection from gateway %q", connect.Gateway) 979 c.sendErr(fmt.Sprintf("Connection to gateway %q rejected", s.getGatewayName())) 980 c.closeConnection(WrongGateway) 981 return ErrWrongGateway 982 } 983 984 c.mu.Lock() 985 c.gw.connected = true 986 // Set the Ping timer after sending connect and info. 987 c.setFirstPingTimer() 988 c.mu.Unlock() 989 990 return nil 991 } 992 993 // Process the INFO protocol from a gateway connection. 994 // 995 // If the gateway connection is an outbound (this server initiated the connection), 996 // this function checks that the incoming INFO contains the Gateway field. If empty, 997 // it means that this is a response from an older server or that this server connected 998 // to the wrong port. 999 // The outbound gateway may also receive a gossip INFO protocol from the remote gateway, 1000 // indicating other gateways that the remote knows about. This server will try to connect 1001 // to those gateways (if not explicitly configured or already implicitly connected). 1002 // In both cases (explicit or implicit), the local cluster is notified about the existence 1003 // of this new gateway. This allows servers in the cluster to ensure that they have an 1004 // outbound connection to this gateway. 1005 // 1006 // For an inbound gateway, the gateway is simply registered and the info protocol 1007 // is saved to be used after processing the CONNECT. 1008 // 1009 // <Invoked from both inbound/outbound readLoop's connection> 1010 func (c *client) processGatewayInfo(info *Info) { 1011 var ( 1012 gwName string 1013 cfg *gatewayCfg 1014 ) 1015 c.mu.Lock() 1016 s := c.srv 1017 cid := c.cid 1018 1019 // Check if this is the first INFO. (this call sets the flag if not already set). 1020 isFirstINFO := c.flags.setIfNotSet(infoReceived) 1021 1022 isOutbound := c.gw.outbound 1023 if isOutbound { 1024 gwName = c.gw.name 1025 cfg = c.gw.cfg 1026 } else if isFirstINFO { 1027 c.gw.name = info.Gateway 1028 } 1029 if isFirstINFO { 1030 c.opts.Name = info.ID 1031 // Get the protocol version from the INFO protocol. This will be checked 1032 // to see if this connection supports message tracing for instance. 1033 c.opts.Protocol = info.Proto 1034 c.gw.remoteName = info.Name 1035 } 1036 c.mu.Unlock() 1037 1038 // For an outbound connection... 1039 if isOutbound { 1040 // Check content of INFO for fields indicating that it comes from a gateway. 1041 // If we incorrectly connect to the wrong port (client or route), we won't 1042 // have the Gateway field set. 1043 if info.Gateway == "" { 1044 c.sendErrAndErr(fmt.Sprintf("Attempt to connect to gateway %q using wrong port", gwName)) 1045 c.closeConnection(WrongPort) 1046 return 1047 } 1048 // Check that the gateway name we got is what we expect 1049 if info.Gateway != gwName { 1050 // Unless this is the very first INFO, it may be ok if this is 1051 // a gossip request to connect to other gateways. 1052 if !isFirstINFO && info.GatewayCmd == gatewayCmdGossip { 1053 // If we are configured to reject unknown, do not attempt to 1054 // connect to one that we don't have configured. 1055 if s.gateway.rejectUnknown() && s.getRemoteGateway(info.Gateway) == nil { 1056 return 1057 } 1058 s.processImplicitGateway(info) 1059 return 1060 } 1061 // Otherwise, this is a failure... 1062 // We are reporting this error in the log... 1063 c.Errorf("Failing connection to gateway %q, remote gateway name is %q", 1064 gwName, info.Gateway) 1065 // ...and sending this back to the remote so that the error 1066 // makes more sense in the remote server's log. 1067 c.sendErr(fmt.Sprintf("Connection from %q rejected, wanted to connect to %q, this is %q", 1068 s.getGatewayName(), gwName, info.Gateway)) 1069 c.closeConnection(WrongGateway) 1070 return 1071 } 1072 1073 // Check for duplicate server name with servers in our cluster 1074 if s.isDuplicateServerName(info.Name) { 1075 c.Errorf("Remote server has a duplicate name: %q", info.Name) 1076 c.closeConnection(DuplicateServerName) 1077 return 1078 } 1079 1080 // Possibly add URLs that we get from the INFO protocol. 1081 if len(info.GatewayURLs) > 0 { 1082 cfg.updateURLs(info.GatewayURLs) 1083 } 1084 1085 // If this is the first INFO, send our connect 1086 if isFirstINFO { 1087 s.gateway.RLock() 1088 infoJSON := s.gateway.infoJSON 1089 s.gateway.RUnlock() 1090 1091 supportsHeaders := s.supportsHeaders() 1092 opts := s.getOpts() 1093 1094 // Note, if we want to support NKeys, then we would get the nonce 1095 // from this INFO protocol and can sign it in the CONNECT we are 1096 // going to send now. 1097 c.mu.Lock() 1098 c.gw.interestOnlyMode = info.GatewayIOM 1099 c.sendGatewayConnect(opts) 1100 c.Debugf("Gateway connect protocol sent to %q", gwName) 1101 // Send INFO too 1102 c.enqueueProto(infoJSON) 1103 c.gw.useOldPrefix = !info.GatewayNRP 1104 c.headers = supportsHeaders && info.Headers 1105 c.mu.Unlock() 1106 1107 // Register as an outbound gateway.. if we had a protocol to ack our connect, 1108 // then we should do that when process that ack. 1109 if s.registerOutboundGatewayConnection(gwName, c) { 1110 c.Noticef("Outbound gateway connection to %q (%s) registered", gwName, info.ID) 1111 // Now that the outbound gateway is registered, we can remove from temp map. 1112 s.removeFromTempClients(cid) 1113 // Set the Ping timer after sending connect and info. 1114 c.mu.Lock() 1115 c.setFirstPingTimer() 1116 c.mu.Unlock() 1117 } else { 1118 // There was a bug that would cause a connection to possibly 1119 // be called twice resulting in reconnection of twice the 1120 // same outbound connection. The issue is fixed, but adding 1121 // defensive code above that if we did not register this connection 1122 // because we already have an outbound for this name, then 1123 // close this connection (and make sure it does not try to reconnect) 1124 c.mu.Lock() 1125 c.flags.set(noReconnect) 1126 c.mu.Unlock() 1127 c.closeConnection(WrongGateway) 1128 return 1129 } 1130 } else if info.GatewayCmd > 0 { 1131 switch info.GatewayCmd { 1132 case gatewayCmdAllSubsStart: 1133 c.gatewayAllSubsReceiveStart(info) 1134 return 1135 case gatewayCmdAllSubsComplete: 1136 c.gatewayAllSubsReceiveComplete(info) 1137 return 1138 default: 1139 s.Warnf("Received unknown command %v from gateway %q", info.GatewayCmd, gwName) 1140 return 1141 } 1142 } 1143 1144 // Flood local cluster with information about this gateway. 1145 // Servers in this cluster will ensure that they have (or otherwise create) 1146 // an outbound connection to this gateway. 1147 s.forwardNewGatewayToLocalCluster(info) 1148 1149 } else if isFirstINFO { 1150 // This is the first INFO of an inbound connection... 1151 1152 // Check for duplicate server name with servers in our cluster 1153 if s.isDuplicateServerName(info.Name) { 1154 c.Errorf("Remote server has a duplicate name: %q", info.Name) 1155 c.closeConnection(DuplicateServerName) 1156 return 1157 } 1158 1159 s.registerInboundGatewayConnection(cid, c) 1160 c.Noticef("Inbound gateway connection from %q (%s) registered", info.Gateway, info.ID) 1161 1162 // Now that it is registered, we can remove from temp map. 1163 s.removeFromTempClients(cid) 1164 1165 // Send our QSubs. 1166 s.sendQueueSubsToGateway(c) 1167 1168 // Initiate outbound connection. This function will behave correctly if 1169 // we have already one. 1170 s.processImplicitGateway(info) 1171 1172 // Send back to the server that initiated this gateway connection the 1173 // list of all remote gateways known on this server. 1174 s.gossipGatewaysToInboundGateway(info.Gateway, c) 1175 1176 // Now make sure if we have any knowledge of connected leafnodes that we resend the 1177 // connect events to switch those accounts into interest only mode. 1178 s.mu.Lock() 1179 s.ensureGWsInterestOnlyForLeafNodes() 1180 s.mu.Unlock() 1181 js := s.js.Load() 1182 1183 // If running in some tests, maintain the original behavior. 1184 if gwDoNotForceInterestOnlyMode && js != nil { 1185 // Switch JetStream accounts to interest-only mode. 1186 var accounts []string 1187 js.mu.Lock() 1188 if len(js.accounts) > 0 { 1189 accounts = make([]string, 0, len(js.accounts)) 1190 for accName := range js.accounts { 1191 accounts = append(accounts, accName) 1192 } 1193 } 1194 js.mu.Unlock() 1195 for _, accName := range accounts { 1196 if acc, err := s.LookupAccount(accName); err == nil && acc != nil { 1197 if acc.JetStreamEnabled() { 1198 s.switchAccountToInterestMode(acc.GetName()) 1199 } 1200 } 1201 } 1202 } else if !gwDoNotForceInterestOnlyMode { 1203 // Starting 2.9.0, we are phasing out the optimistic mode, so change 1204 // all accounts to interest-only mode, unless instructed not to do so 1205 // in some tests. 1206 s.accounts.Range(func(_, v any) bool { 1207 acc := v.(*Account) 1208 s.switchAccountToInterestMode(acc.GetName()) 1209 return true 1210 }) 1211 } 1212 } 1213 } 1214 1215 // Sends to the given inbound gateway connection a gossip INFO protocol 1216 // for each gateway known by this server. This allows for a "full mesh" 1217 // of gateways. 1218 func (s *Server) gossipGatewaysToInboundGateway(gwName string, c *client) { 1219 gw := s.gateway 1220 gw.RLock() 1221 defer gw.RUnlock() 1222 for gwCfgName, cfg := range gw.remotes { 1223 // Skip the gateway that we just created 1224 if gwCfgName == gwName { 1225 continue 1226 } 1227 info := Info{ 1228 ID: s.info.ID, 1229 GatewayCmd: gatewayCmdGossip, 1230 } 1231 urls := cfg.getURLsAsStrings() 1232 if len(urls) > 0 { 1233 info.Gateway = gwCfgName 1234 info.GatewayURLs = urls 1235 b, _ := json.Marshal(&info) 1236 c.mu.Lock() 1237 c.enqueueProto([]byte(fmt.Sprintf(InfoProto, b))) 1238 c.mu.Unlock() 1239 } 1240 } 1241 } 1242 1243 // Sends the INFO protocol of a gateway to all routes known by this server. 1244 func (s *Server) forwardNewGatewayToLocalCluster(oinfo *Info) { 1245 // Need to protect s.routes here, so use server's lock 1246 s.mu.Lock() 1247 defer s.mu.Unlock() 1248 1249 // We don't really need the ID to be set, but, we need to make sure 1250 // that it is not set to the server ID so that if we were to connect 1251 // to an older server that does not expect a "gateway" INFO, it 1252 // would think that it needs to create an implicit route (since info.ID 1253 // would not match the route's remoteID), but will fail to do so because 1254 // the sent protocol will not have host/port defined. 1255 info := &Info{ 1256 ID: "GW" + s.info.ID, 1257 Name: s.getOpts().ServerName, 1258 Gateway: oinfo.Gateway, 1259 GatewayURLs: oinfo.GatewayURLs, 1260 GatewayCmd: gatewayCmdGossip, 1261 } 1262 b, _ := json.Marshal(info) 1263 infoJSON := []byte(fmt.Sprintf(InfoProto, b)) 1264 1265 s.forEachRemote(func(r *client) { 1266 r.mu.Lock() 1267 r.enqueueProto(infoJSON) 1268 r.mu.Unlock() 1269 }) 1270 } 1271 1272 // Sends queue subscriptions interest to remote gateway. 1273 // This is sent from the inbound side, that is, the side that receives 1274 // messages from the remote's outbound connection. This side is 1275 // the one sending the subscription interest. 1276 func (s *Server) sendQueueSubsToGateway(c *client) { 1277 s.sendSubsToGateway(c, _EMPTY_) 1278 } 1279 1280 // Sends all subscriptions for the given account to the remove gateway 1281 // This is sent from the inbound side, that is, the side that receives 1282 // messages from the remote's outbound connection. This side is 1283 // the one sending the subscription interest. 1284 func (s *Server) sendAccountSubsToGateway(c *client, accName string) { 1285 s.sendSubsToGateway(c, accName) 1286 } 1287 1288 func gwBuildSubProto(buf *bytes.Buffer, accName string, acc map[string]*sitally, doQueues bool) { 1289 for saq, si := range acc { 1290 if doQueues && si.q || !doQueues && !si.q { 1291 buf.Write(rSubBytes) 1292 buf.WriteString(accName) 1293 buf.WriteByte(' ') 1294 // For queue subs (si.q is true), saq will be 1295 // subject + ' ' + queue, for plain subs, this is 1296 // just the subject. 1297 buf.WriteString(saq) 1298 if doQueues { 1299 buf.WriteString(" 1") 1300 } 1301 buf.WriteString(CR_LF) 1302 } 1303 } 1304 } 1305 1306 // Sends subscriptions to remote gateway. 1307 func (s *Server) sendSubsToGateway(c *client, accountName string) { 1308 var ( 1309 bufa = [32 * 1024]byte{} 1310 bbuf = bytes.NewBuffer(bufa[:0]) 1311 ) 1312 1313 gw := s.gateway 1314 1315 // This needs to run under this lock for the whole duration 1316 gw.pasi.Lock() 1317 defer gw.pasi.Unlock() 1318 1319 // If account is specified... 1320 if accountName != _EMPTY_ { 1321 // Simply send all plain subs (no queues) for this specific account 1322 gwBuildSubProto(bbuf, accountName, gw.pasi.m[accountName], false) 1323 // Instruct to send all subs (RS+/-) for this account from now on. 1324 c.mu.Lock() 1325 e := c.gw.insim[accountName] 1326 if e == nil { 1327 e = &insie{} 1328 c.gw.insim[accountName] = e 1329 } 1330 e.mode = InterestOnly 1331 c.mu.Unlock() 1332 } else { 1333 // Send queues for all accounts 1334 for accName, acc := range gw.pasi.m { 1335 gwBuildSubProto(bbuf, accName, acc, true) 1336 } 1337 } 1338 1339 buf := bbuf.Bytes() 1340 1341 // Nothing to send. 1342 if len(buf) == 0 { 1343 return 1344 } 1345 if len(buf) > cap(bufa) { 1346 s.Debugf("Sending subscriptions to %q, buffer size: %v", c.gw.name, len(buf)) 1347 } 1348 // Send 1349 c.mu.Lock() 1350 c.enqueueProto(buf) 1351 c.Debugf("Sent queue subscriptions to gateway") 1352 c.mu.Unlock() 1353 } 1354 1355 // This is invoked when getting an INFO protocol for gateway on the ROUTER port. 1356 // This function will then execute appropriate function based on the command 1357 // contained in the protocol. 1358 // <Invoked from a route connection's readLoop> 1359 func (s *Server) processGatewayInfoFromRoute(info *Info, routeSrvID string) { 1360 switch info.GatewayCmd { 1361 case gatewayCmdGossip: 1362 s.processImplicitGateway(info) 1363 default: 1364 s.Errorf("Unknown command %d from server %v", info.GatewayCmd, routeSrvID) 1365 } 1366 } 1367 1368 // Sends INFO protocols to the given route connection for each known Gateway. 1369 // These will be processed by the route and delegated to the gateway code to 1370 // invoke processImplicitGateway. 1371 func (s *Server) sendGatewayConfigsToRoute(route *client) { 1372 gw := s.gateway 1373 gw.RLock() 1374 // Send only to gateways for which we have actual outbound connection to. 1375 if len(gw.out) == 0 { 1376 gw.RUnlock() 1377 return 1378 } 1379 // Collect gateway configs for which we have an outbound connection. 1380 gwCfgsa := [16]*gatewayCfg{} 1381 gwCfgs := gwCfgsa[:0] 1382 for _, c := range gw.out { 1383 c.mu.Lock() 1384 if c.gw.cfg != nil { 1385 gwCfgs = append(gwCfgs, c.gw.cfg) 1386 } 1387 c.mu.Unlock() 1388 } 1389 gw.RUnlock() 1390 if len(gwCfgs) == 0 { 1391 return 1392 } 1393 1394 // Check forwardNewGatewayToLocalCluster() as to why we set ID this way. 1395 info := Info{ 1396 ID: "GW" + s.info.ID, 1397 GatewayCmd: gatewayCmdGossip, 1398 } 1399 for _, cfg := range gwCfgs { 1400 urls := cfg.getURLsAsStrings() 1401 if len(urls) > 0 { 1402 info.Gateway = cfg.Name 1403 info.GatewayURLs = urls 1404 b, _ := json.Marshal(&info) 1405 route.mu.Lock() 1406 route.enqueueProto([]byte(fmt.Sprintf(InfoProto, b))) 1407 route.mu.Unlock() 1408 } 1409 } 1410 } 1411 1412 // Initiates a gateway connection using the info contained in the INFO protocol. 1413 // If a gateway with the same name is already registered (either because explicitly 1414 // configured, or already implicitly connected), this function will augmment the 1415 // remote URLs with URLs present in the info protocol and return. 1416 // Otherwise, this function will register this remote (to prevent multiple connections 1417 // to the same remote) and call solicitGateway (which will run in a different go-routine). 1418 func (s *Server) processImplicitGateway(info *Info) { 1419 s.gateway.Lock() 1420 defer s.gateway.Unlock() 1421 // Name of the gateway to connect to is the Info.Gateway field. 1422 gwName := info.Gateway 1423 // If this is our name, bail. 1424 if gwName == s.gateway.name { 1425 return 1426 } 1427 // Check if we already have this config, and if so, we are done 1428 cfg := s.gateway.remotes[gwName] 1429 if cfg != nil { 1430 // However, possibly augment the list of URLs with the given 1431 // info.GatewayURLs content. 1432 cfg.Lock() 1433 cfg.addURLs(info.GatewayURLs) 1434 cfg.Unlock() 1435 return 1436 } 1437 opts := s.getOpts() 1438 cfg = &gatewayCfg{ 1439 RemoteGatewayOpts: &RemoteGatewayOpts{Name: gwName}, 1440 hash: getGWHash(gwName), 1441 oldHash: getOldHash(gwName), 1442 urls: make(map[string]*url.URL, len(info.GatewayURLs)), 1443 implicit: true, 1444 } 1445 if opts.Gateway.TLSConfig != nil { 1446 cfg.TLSConfig = opts.Gateway.TLSConfig.Clone() 1447 cfg.TLSTimeout = opts.Gateway.TLSTimeout 1448 } 1449 1450 // Since we know we don't have URLs (no config, so just based on what we 1451 // get from INFO), directly call addURLs(). We don't need locking since 1452 // we just created that structure and no one else has access to it yet. 1453 cfg.addURLs(info.GatewayURLs) 1454 // If there is no URL, we can't proceed. 1455 if len(cfg.urls) == 0 { 1456 return 1457 } 1458 s.gateway.remotes[gwName] = cfg 1459 s.startGoRoutine(func() { 1460 s.solicitGateway(cfg, true) 1461 s.grWG.Done() 1462 }) 1463 } 1464 1465 // NumOutboundGateways is public here mostly for testing. 1466 func (s *Server) NumOutboundGateways() int { 1467 return s.numOutboundGateways() 1468 } 1469 1470 // Returns the number of outbound gateway connections 1471 func (s *Server) numOutboundGateways() int { 1472 s.gateway.RLock() 1473 n := len(s.gateway.out) 1474 s.gateway.RUnlock() 1475 return n 1476 } 1477 1478 // Returns the number of inbound gateway connections 1479 func (s *Server) numInboundGateways() int { 1480 s.gateway.RLock() 1481 n := len(s.gateway.in) 1482 s.gateway.RUnlock() 1483 return n 1484 } 1485 1486 // Returns the remoteGateway (if any) that has the given `name` 1487 func (s *Server) getRemoteGateway(name string) *gatewayCfg { 1488 s.gateway.RLock() 1489 cfg := s.gateway.remotes[name] 1490 s.gateway.RUnlock() 1491 return cfg 1492 } 1493 1494 // Used in tests 1495 func (g *gatewayCfg) bumpConnAttempts() { 1496 g.Lock() 1497 g.connAttempts++ 1498 g.Unlock() 1499 } 1500 1501 // Used in tests 1502 func (g *gatewayCfg) getConnAttempts() int { 1503 g.Lock() 1504 ca := g.connAttempts 1505 g.Unlock() 1506 return ca 1507 } 1508 1509 // Used in tests 1510 func (g *gatewayCfg) resetConnAttempts() { 1511 g.Lock() 1512 g.connAttempts = 0 1513 g.Unlock() 1514 } 1515 1516 // Returns if this remote gateway is implicit or not. 1517 func (g *gatewayCfg) isImplicit() bool { 1518 g.RLock() 1519 ii := g.implicit 1520 g.RUnlock() 1521 return ii 1522 } 1523 1524 // getURLs returns an array of URLs in random order suitable for 1525 // an iteration to try to connect. 1526 func (g *gatewayCfg) getURLs() []*url.URL { 1527 g.RLock() 1528 a := make([]*url.URL, 0, len(g.urls)) 1529 for _, u := range g.urls { 1530 a = append(a, u) 1531 } 1532 g.RUnlock() 1533 // Map iteration is random, but not that good with small maps. 1534 rand.Shuffle(len(a), func(i, j int) { 1535 a[i], a[j] = a[j], a[i] 1536 }) 1537 return a 1538 } 1539 1540 // Similar to getURLs but returns the urls as an array of strings. 1541 func (g *gatewayCfg) getURLsAsStrings() []string { 1542 g.RLock() 1543 a := make([]string, 0, len(g.urls)) 1544 for _, u := range g.urls { 1545 a = append(a, u.Host) 1546 } 1547 g.RUnlock() 1548 return a 1549 } 1550 1551 // updateURLs creates the urls map with the content of the config's URLs array 1552 // and the given array that we get from the INFO protocol. 1553 func (g *gatewayCfg) updateURLs(infoURLs []string) { 1554 g.Lock() 1555 // Clear the map... 1556 g.urls = make(map[string]*url.URL, len(g.URLs)+len(infoURLs)) 1557 // Add the urls from the config URLs array. 1558 for _, u := range g.URLs { 1559 g.urls[u.Host] = u 1560 } 1561 // Then add the ones from the infoURLs array we got. 1562 g.addURLs(infoURLs) 1563 // The call above will set varzUpdateURLs only when finding ULRs in infoURLs 1564 // that are not present in the config. That does not cover the case where 1565 // previously "discovered" URLs are now gone. We could check "before" size 1566 // of g.urls and if bigger than current size, set the boolean to true. 1567 // Not worth it... simply set this to true to allow a refresh of gateway 1568 // URLs in varz. 1569 g.varzUpdateURLs = true 1570 g.Unlock() 1571 } 1572 1573 // Saves the hostname of the given URL (if not already done). 1574 // This may be used as the ServerName of the TLSConfig when initiating a 1575 // TLS connection. 1576 // Write lock held on entry. 1577 func (g *gatewayCfg) saveTLSHostname(u *url.URL) { 1578 if g.TLSConfig != nil && g.tlsName == "" && net.ParseIP(u.Hostname()) == nil { 1579 g.tlsName = u.Hostname() 1580 } 1581 } 1582 1583 // add URLs from the given array to the urls map only if not already present. 1584 // remoteGateway write lock is assumed to be held on entry. 1585 // Write lock is held on entry. 1586 func (g *gatewayCfg) addURLs(infoURLs []string) { 1587 var scheme string 1588 if g.TLSConfig != nil { 1589 scheme = "tls" 1590 } else { 1591 scheme = "nats" 1592 } 1593 for _, iu := range infoURLs { 1594 if _, present := g.urls[iu]; !present { 1595 // Urls in Info.GatewayURLs come without scheme. Add it to parse 1596 // the url (otherwise it fails). 1597 if u, err := url.Parse(fmt.Sprintf("%s://%s", scheme, iu)); err == nil { 1598 // Also, if a tlsName has not been set yet and we are dealing 1599 // with a hostname and not a bare IP, save the hostname. 1600 g.saveTLSHostname(u) 1601 // Use u.Host for the key. 1602 g.urls[u.Host] = u 1603 // Signal that we have updated the list. Used by monitoring code. 1604 g.varzUpdateURLs = true 1605 } 1606 } 1607 } 1608 } 1609 1610 // Adds this URL to the set of Gateway URLs. 1611 // Returns true if the URL has been added, false otherwise. 1612 // Server lock held on entry 1613 func (s *Server) addGatewayURL(urlStr string) bool { 1614 s.gateway.Lock() 1615 added := s.gateway.URLs.addUrl(urlStr) 1616 if added { 1617 s.gateway.generateInfoJSON() 1618 } 1619 s.gateway.Unlock() 1620 return added 1621 } 1622 1623 // Removes this URL from the set of gateway URLs. 1624 // Returns true if the URL has been removed, false otherwise. 1625 // Server lock held on entry 1626 func (s *Server) removeGatewayURL(urlStr string) bool { 1627 if s.isShuttingDown() { 1628 return false 1629 } 1630 s.gateway.Lock() 1631 removed := s.gateway.URLs.removeUrl(urlStr) 1632 if removed { 1633 s.gateway.generateInfoJSON() 1634 } 1635 s.gateway.Unlock() 1636 return removed 1637 } 1638 1639 // Sends a Gateway's INFO to all inbound GW connections. 1640 // Server lock is held on entry 1641 func (s *Server) sendAsyncGatewayInfo() { 1642 s.gateway.RLock() 1643 for _, ig := range s.gateway.in { 1644 ig.mu.Lock() 1645 ig.enqueueProto(s.gateway.infoJSON) 1646 ig.mu.Unlock() 1647 } 1648 s.gateway.RUnlock() 1649 } 1650 1651 // This returns the URL of the Gateway listen spec, or empty string 1652 // if the server has no gateway configured. 1653 func (s *Server) getGatewayURL() string { 1654 s.gateway.RLock() 1655 url := s.gateway.URL 1656 s.gateway.RUnlock() 1657 return url 1658 } 1659 1660 // Returns this server gateway name. 1661 // Same than calling s.gateway.getName() 1662 func (s *Server) getGatewayName() string { 1663 // This is immutable 1664 return s.gateway.name 1665 } 1666 1667 // All gateway connections (outbound and inbound) are put in the given map. 1668 func (s *Server) getAllGatewayConnections(conns map[uint64]*client) { 1669 gw := s.gateway 1670 gw.RLock() 1671 for _, c := range gw.out { 1672 c.mu.Lock() 1673 cid := c.cid 1674 c.mu.Unlock() 1675 conns[cid] = c 1676 } 1677 for cid, c := range gw.in { 1678 conns[cid] = c 1679 } 1680 gw.RUnlock() 1681 } 1682 1683 // Register the given gateway connection (*client) in the inbound gateways 1684 // map. The key is the connection ID (like for clients and routes). 1685 func (s *Server) registerInboundGatewayConnection(cid uint64, gwc *client) { 1686 s.gateway.Lock() 1687 s.gateway.in[cid] = gwc 1688 s.gateway.Unlock() 1689 } 1690 1691 // Register the given gateway connection (*client) in the outbound gateways 1692 // map with the given name as the key. 1693 func (s *Server) registerOutboundGatewayConnection(name string, gwc *client) bool { 1694 s.gateway.Lock() 1695 if _, exist := s.gateway.out[name]; exist { 1696 s.gateway.Unlock() 1697 return false 1698 } 1699 s.gateway.out[name] = gwc 1700 s.gateway.outo = append(s.gateway.outo, gwc) 1701 s.gateway.orderOutboundConnectionsLocked() 1702 s.gateway.Unlock() 1703 return true 1704 } 1705 1706 // Returns the outbound gateway connection (*client) with the given name, 1707 // or nil if not found 1708 func (s *Server) getOutboundGatewayConnection(name string) *client { 1709 s.gateway.RLock() 1710 gwc := s.gateway.out[name] 1711 s.gateway.RUnlock() 1712 return gwc 1713 } 1714 1715 // Returns all outbound gateway connections in the provided array. 1716 // The order of the gateways is suited for the sending of a message. 1717 // Current ordering is based on individual gateway's RTT value. 1718 func (s *Server) getOutboundGatewayConnections(a *[]*client) { 1719 s.gateway.RLock() 1720 for i := 0; i < len(s.gateway.outo); i++ { 1721 *a = append(*a, s.gateway.outo[i]) 1722 } 1723 s.gateway.RUnlock() 1724 } 1725 1726 // Orders the array of outbound connections. 1727 // Current ordering is by lowest RTT. 1728 // Gateway write lock is held on entry 1729 func (g *srvGateway) orderOutboundConnectionsLocked() { 1730 // Order the gateways by lowest RTT 1731 sort.Slice(g.outo, func(i, j int) bool { 1732 return g.outo[i].getRTTValue() < g.outo[j].getRTTValue() 1733 }) 1734 } 1735 1736 // Orders the array of outbound connections. 1737 // Current ordering is by lowest RTT. 1738 func (g *srvGateway) orderOutboundConnections() { 1739 g.Lock() 1740 g.orderOutboundConnectionsLocked() 1741 g.Unlock() 1742 } 1743 1744 // Returns all inbound gateway connections in the provided array 1745 func (s *Server) getInboundGatewayConnections(a *[]*client) { 1746 s.gateway.RLock() 1747 for _, gwc := range s.gateway.in { 1748 *a = append(*a, gwc) 1749 } 1750 s.gateway.RUnlock() 1751 } 1752 1753 // This is invoked when a gateway connection is closed and the server 1754 // is removing this connection from its state. 1755 func (s *Server) removeRemoteGatewayConnection(c *client) { 1756 c.mu.Lock() 1757 cid := c.cid 1758 isOutbound := c.gw.outbound 1759 gwName := c.gw.name 1760 if isOutbound && c.gw.outsim != nil { 1761 // We do this to allow the GC to release this connection. 1762 // Since the map is used by the rest of the code without client lock, 1763 // we can't simply set it to nil, instead, just make sure we empty it. 1764 c.gw.outsim.Range(func(k, _ any) bool { 1765 c.gw.outsim.Delete(k) 1766 return true 1767 }) 1768 } 1769 c.mu.Unlock() 1770 1771 gw := s.gateway 1772 gw.Lock() 1773 if isOutbound { 1774 delete(gw.out, gwName) 1775 louto := len(gw.outo) 1776 reorder := false 1777 for i := 0; i < len(gw.outo); i++ { 1778 if gw.outo[i] == c { 1779 // If last, simply remove and no need to reorder 1780 if i != louto-1 { 1781 gw.outo[i] = gw.outo[louto-1] 1782 reorder = true 1783 } 1784 gw.outo = gw.outo[:louto-1] 1785 } 1786 } 1787 if reorder { 1788 gw.orderOutboundConnectionsLocked() 1789 } 1790 } else { 1791 delete(gw.in, cid) 1792 } 1793 gw.Unlock() 1794 s.removeFromTempClients(cid) 1795 1796 if isOutbound { 1797 // Update number of totalQSubs for this gateway 1798 qSubsRemoved := int64(0) 1799 c.mu.Lock() 1800 for _, sub := range c.subs { 1801 if sub.queue != nil { 1802 qSubsRemoved++ 1803 } 1804 } 1805 c.subs = nil 1806 c.mu.Unlock() 1807 // Update total count of qsubs in remote gateways. 1808 atomic.AddInt64(&c.srv.gateway.totalQSubs, -qSubsRemoved) 1809 1810 } else { 1811 var subsa [1024]*subscription 1812 var subs = subsa[:0] 1813 1814 // For inbound GW connection, if we have subs, those are 1815 // local subs on "_R_." subjects. 1816 c.mu.Lock() 1817 for _, sub := range c.subs { 1818 subs = append(subs, sub) 1819 } 1820 c.subs = nil 1821 c.mu.Unlock() 1822 for _, sub := range subs { 1823 c.removeReplySub(sub) 1824 } 1825 } 1826 } 1827 1828 // GatewayAddr returns the net.Addr object for the gateway listener. 1829 func (s *Server) GatewayAddr() *net.TCPAddr { 1830 s.mu.Lock() 1831 defer s.mu.Unlock() 1832 if s.gatewayListener == nil { 1833 return nil 1834 } 1835 return s.gatewayListener.Addr().(*net.TCPAddr) 1836 } 1837 1838 // A- protocol received from the remote after sending messages 1839 // on an account that it has no interest in. Mark this account 1840 // with a "no interest" marker to prevent further messages send. 1841 // <Invoked from outbound connection's readLoop> 1842 func (c *client) processGatewayAccountUnsub(accName string) { 1843 // Just to indicate activity around "subscriptions" events. 1844 c.in.subs++ 1845 // This account may have an entry because of queue subs. 1846 // If that's the case, we can reset the no-interest map, 1847 // but not set the entry to nil. 1848 setToNil := true 1849 if ei, ok := c.gw.outsim.Load(accName); ei != nil { 1850 e := ei.(*outsie) 1851 e.Lock() 1852 // Reset the no-interest map if we have queue subs 1853 // and don't set the entry to nil. 1854 if e.qsubs > 0 { 1855 e.ni = make(map[string]struct{}) 1856 setToNil = false 1857 } 1858 e.Unlock() 1859 } else if ok { 1860 // Already set to nil, so skip 1861 setToNil = false 1862 } 1863 if setToNil { 1864 c.gw.outsim.Store(accName, nil) 1865 } 1866 } 1867 1868 // A+ protocol received from remote gateway if it had previously 1869 // sent an A-. Clear the "no interest" marker for this account. 1870 // <Invoked from outbound connection's readLoop> 1871 func (c *client) processGatewayAccountSub(accName string) error { 1872 // Just to indicate activity around "subscriptions" events. 1873 c.in.subs++ 1874 // If this account has an entry because of queue subs, we 1875 // can't delete the entry. 1876 remove := true 1877 if ei, ok := c.gw.outsim.Load(accName); ei != nil { 1878 e := ei.(*outsie) 1879 e.Lock() 1880 if e.qsubs > 0 { 1881 remove = false 1882 } 1883 e.Unlock() 1884 } else if !ok { 1885 // There is no entry, so skip 1886 remove = false 1887 } 1888 if remove { 1889 c.gw.outsim.Delete(accName) 1890 } 1891 return nil 1892 } 1893 1894 // RS- protocol received from the remote after sending messages 1895 // on a subject that it has no interest in (but knows about the 1896 // account). Mark this subject with a "no interest" marker to 1897 // prevent further messages being sent. 1898 // If in modeInterestOnly or for a queue sub, remove from 1899 // the sublist if present. 1900 // <Invoked from outbound connection's readLoop> 1901 func (c *client) processGatewayRUnsub(arg []byte) error { 1902 accName, subject, queue, err := c.parseUnsubProto(arg) 1903 if err != nil { 1904 return fmt.Errorf("processGatewaySubjectUnsub %s", err.Error()) 1905 } 1906 1907 var ( 1908 e *outsie 1909 useSl bool 1910 newe bool 1911 callUpdate bool 1912 srv *Server 1913 sub *subscription 1914 ) 1915 1916 // Possibly execute this on exit after all locks have been released. 1917 // If callUpdate is true, srv and sub will be not nil. 1918 defer func() { 1919 if callUpdate { 1920 srv.updateInterestForAccountOnGateway(accName, sub, -1) 1921 } 1922 }() 1923 1924 c.mu.Lock() 1925 if c.gw.outsim == nil { 1926 c.Errorf("Received RS- from gateway on inbound connection") 1927 c.mu.Unlock() 1928 c.closeConnection(ProtocolViolation) 1929 return nil 1930 } 1931 defer c.mu.Unlock() 1932 // If closed, c.subs map will be nil, so bail out. 1933 if c.isClosed() { 1934 return nil 1935 } 1936 1937 ei, _ := c.gw.outsim.Load(accName) 1938 if ei != nil { 1939 e = ei.(*outsie) 1940 e.Lock() 1941 defer e.Unlock() 1942 // If there is an entry, for plain sub we need 1943 // to know if we should store the sub 1944 useSl = queue != nil || e.mode != Optimistic 1945 } else if queue != nil { 1946 // should not even happen... 1947 c.Debugf("Received RS- without prior RS+ for subject %q, queue %q", subject, queue) 1948 return nil 1949 } else { 1950 // Plain sub, assume optimistic sends, create entry. 1951 e = &outsie{ni: make(map[string]struct{}), sl: NewSublistWithCache()} 1952 newe = true 1953 } 1954 // This is when a sub or queue sub is supposed to be in 1955 // the sublist. Look for it and remove. 1956 if useSl { 1957 var ok bool 1958 key := arg 1959 // m[string()] does not cause mem allocation 1960 sub, ok = c.subs[string(key)] 1961 // if RS- for a sub that we don't have, just ignore. 1962 if !ok { 1963 return nil 1964 } 1965 if e.sl.Remove(sub) == nil { 1966 delete(c.subs, bytesToString(key)) 1967 if queue != nil { 1968 e.qsubs-- 1969 atomic.AddInt64(&c.srv.gateway.totalQSubs, -1) 1970 } 1971 // If last, we can remove the whole entry only 1972 // when in optimistic mode and there is no element 1973 // in the `ni` map. 1974 if e.sl.Count() == 0 && e.mode == Optimistic && len(e.ni) == 0 { 1975 c.gw.outsim.Delete(accName) 1976 } 1977 } 1978 // We are going to call updateInterestForAccountOnGateway on exit. 1979 srv = c.srv 1980 callUpdate = true 1981 } else { 1982 e.ni[string(subject)] = struct{}{} 1983 if newe { 1984 c.gw.outsim.Store(accName, e) 1985 } 1986 } 1987 return nil 1988 } 1989 1990 // For plain subs, RS+ protocol received from remote gateway if it 1991 // had previously sent a RS-. Clear the "no interest" marker for 1992 // this subject (under this account). 1993 // For queue subs, or if in modeInterestOnly, register interest 1994 // from remote gateway. 1995 // <Invoked from outbound connection's readLoop> 1996 func (c *client) processGatewayRSub(arg []byte) error { 1997 // Indicate activity. 1998 c.in.subs++ 1999 2000 var ( 2001 queue []byte 2002 qw int32 2003 ) 2004 2005 args := splitArg(arg) 2006 switch len(args) { 2007 case 2: 2008 case 4: 2009 queue = args[2] 2010 qw = int32(parseSize(args[3])) 2011 default: 2012 return fmt.Errorf("processGatewaySubjectSub Parse Error: '%s'", arg) 2013 } 2014 accName := args[0] 2015 subject := args[1] 2016 2017 var ( 2018 e *outsie 2019 useSl bool 2020 newe bool 2021 callUpdate bool 2022 srv *Server 2023 sub *subscription 2024 ) 2025 2026 // Possibly execute this on exit after all locks have been released. 2027 // If callUpdate is true, srv and sub will be not nil. 2028 defer func() { 2029 if callUpdate { 2030 srv.updateInterestForAccountOnGateway(string(accName), sub, 1) 2031 } 2032 }() 2033 2034 c.mu.Lock() 2035 if c.gw.outsim == nil { 2036 c.Errorf("Received RS+ from gateway on inbound connection") 2037 c.mu.Unlock() 2038 c.closeConnection(ProtocolViolation) 2039 return nil 2040 } 2041 defer c.mu.Unlock() 2042 // If closed, c.subs map will be nil, so bail out. 2043 if c.isClosed() { 2044 return nil 2045 } 2046 2047 ei, _ := c.gw.outsim.Load(bytesToString(accName)) 2048 // We should always have an existing entry for plain subs because 2049 // in optimistic mode we would have received RS- first, and 2050 // in full knowledge, we are receiving RS+ for an account after 2051 // getting many RS- from the remote.. 2052 if ei != nil { 2053 e = ei.(*outsie) 2054 e.Lock() 2055 defer e.Unlock() 2056 useSl = queue != nil || e.mode != Optimistic 2057 } else if queue == nil { 2058 return nil 2059 } else { 2060 e = &outsie{ni: make(map[string]struct{}), sl: NewSublistWithCache()} 2061 newe = true 2062 useSl = true 2063 } 2064 if useSl { 2065 var key []byte 2066 // We store remote subs by account/subject[/queue]. 2067 // For queue, remove the trailing weight 2068 if queue != nil { 2069 key = arg[:len(arg)-len(args[3])-1] 2070 } else { 2071 key = arg 2072 } 2073 // If RS+ for a sub that we already have, ignore. 2074 // (m[string()] does not allocate memory) 2075 if _, ok := c.subs[string(key)]; ok { 2076 return nil 2077 } 2078 // new subscription. copy subject (and queue) to 2079 // not reference the underlying possibly big buffer. 2080 var csubject []byte 2081 var cqueue []byte 2082 if queue != nil { 2083 // make single allocation and use different slices 2084 // to point to subject and queue name. 2085 cbuf := make([]byte, len(subject)+1+len(queue)) 2086 copy(cbuf, key[len(accName)+1:]) 2087 csubject = cbuf[:len(subject)] 2088 cqueue = cbuf[len(subject)+1:] 2089 } else { 2090 csubject = make([]byte, len(subject)) 2091 copy(csubject, subject) 2092 } 2093 sub = &subscription{client: c, subject: csubject, queue: cqueue, qw: qw} 2094 // If no error inserting in sublist... 2095 if e.sl.Insert(sub) == nil { 2096 c.subs[string(key)] = sub 2097 if queue != nil { 2098 e.qsubs++ 2099 atomic.AddInt64(&c.srv.gateway.totalQSubs, 1) 2100 } 2101 if newe { 2102 c.gw.outsim.Store(string(accName), e) 2103 } 2104 } 2105 // We are going to call updateInterestForAccountOnGateway on exit. 2106 srv = c.srv 2107 callUpdate = true 2108 } else { 2109 subj := bytesToString(subject) 2110 // If this is an RS+ for a wc subject, then 2111 // remove from the no interest map all subjects 2112 // that are a subset of this wc subject. 2113 if subjectHasWildcard(subj) { 2114 for k := range e.ni { 2115 if subjectIsSubsetMatch(k, subj) { 2116 delete(e.ni, k) 2117 } 2118 } 2119 } else { 2120 delete(e.ni, subj) 2121 } 2122 } 2123 return nil 2124 } 2125 2126 // Returns true if this gateway has possible interest in the 2127 // given account/subject (which means, it does not have a registered 2128 // no-interest on the account and/or subject) and the sublist result 2129 // for queue subscriptions. 2130 // <Outbound connection: invoked when client message is published, 2131 // so from any client connection's readLoop> 2132 func (c *client) gatewayInterest(acc, subj string) (bool, *SublistResult) { 2133 ei, accountInMap := c.gw.outsim.Load(acc) 2134 // If there is an entry for this account and ei is nil, 2135 // it means that the remote is not interested at all in 2136 // this account and we could not possibly have queue subs. 2137 if accountInMap && ei == nil { 2138 return false, nil 2139 } 2140 // Assume interest if account not in map, unless we support 2141 // only interest-only mode. 2142 psi := !accountInMap && !c.gw.interestOnlyMode 2143 var r *SublistResult 2144 if accountInMap { 2145 // If in map, check for subs interest with sublist. 2146 e := ei.(*outsie) 2147 e.RLock() 2148 // Unless each side has agreed on interest-only mode, 2149 // we may be in transition to modeInterestOnly 2150 // but until e.ni is nil, use it to know if we 2151 // should suppress interest or not. 2152 if !c.gw.interestOnlyMode && e.ni != nil { 2153 if _, inMap := e.ni[subj]; !inMap { 2154 psi = true 2155 } 2156 } 2157 // If we are in modeInterestOnly (e.ni will be nil) 2158 // or if we have queue subs, we also need to check sl.Match. 2159 if e.ni == nil || e.qsubs > 0 { 2160 r = e.sl.Match(subj) 2161 if len(r.psubs) > 0 { 2162 psi = true 2163 } 2164 } 2165 e.RUnlock() 2166 // Since callers may just check if the sublist result is nil or not, 2167 // make sure that if what is returned by sl.Match() is the emptyResult, then 2168 // we return nil to the caller. 2169 if r == emptyResult { 2170 r = nil 2171 } 2172 } 2173 return psi, r 2174 } 2175 2176 // switchAccountToInterestMode will switch an account over to interestMode. 2177 // Lock should NOT be held. 2178 func (s *Server) switchAccountToInterestMode(accName string) { 2179 gwsa := [16]*client{} 2180 gws := gwsa[:0] 2181 s.getInboundGatewayConnections(&gws) 2182 2183 for _, gin := range gws { 2184 var e *insie 2185 var ok bool 2186 2187 gin.mu.Lock() 2188 if e, ok = gin.gw.insim[accName]; !ok || e == nil { 2189 e = &insie{} 2190 gin.gw.insim[accName] = e 2191 } 2192 // Do it only if we are in Optimistic mode 2193 if e.mode == Optimistic { 2194 gin.gatewaySwitchAccountToSendAllSubs(e, accName) 2195 } 2196 gin.mu.Unlock() 2197 } 2198 } 2199 2200 // This is invoked when registering (or unregistering) the first 2201 // (or last) subscription on a given account/subject. For each 2202 // GWs inbound connections, we will check if we need to send an RS+ or A+ 2203 // protocol. 2204 func (s *Server) maybeSendSubOrUnsubToGateways(accName string, sub *subscription, added bool) { 2205 if sub.queue != nil { 2206 return 2207 } 2208 gwsa := [16]*client{} 2209 gws := gwsa[:0] 2210 s.getInboundGatewayConnections(&gws) 2211 if len(gws) == 0 { 2212 return 2213 } 2214 var ( 2215 rsProtoa [512]byte 2216 rsProto []byte 2217 accProtoa [256]byte 2218 accProto []byte 2219 proto []byte 2220 subject = bytesToString(sub.subject) 2221 hasWC = subjectHasWildcard(subject) 2222 ) 2223 for _, c := range gws { 2224 proto = nil 2225 c.mu.Lock() 2226 e, inMap := c.gw.insim[accName] 2227 // If there is a inbound subject interest entry... 2228 if e != nil { 2229 sendProto := false 2230 // In optimistic mode, we care only about possibly sending RS+ (or A+) 2231 // so if e.ni is not nil we do things only when adding a new subscription. 2232 if e.ni != nil && added { 2233 // For wildcard subjects, we will remove from our no-interest 2234 // map, all subjects that are a subset of this wc subject, but we 2235 // still send the wc subject and let the remote do its own cleanup. 2236 if hasWC { 2237 for enis := range e.ni { 2238 if subjectIsSubsetMatch(enis, subject) { 2239 delete(e.ni, enis) 2240 sendProto = true 2241 } 2242 } 2243 } else if _, noInterest := e.ni[subject]; noInterest { 2244 delete(e.ni, subject) 2245 sendProto = true 2246 } 2247 } else if e.mode == InterestOnly { 2248 // We are in the mode where we always send RS+/- protocols. 2249 sendProto = true 2250 } 2251 if sendProto { 2252 if rsProto == nil { 2253 // Construct the RS+/- only once 2254 proto = rsProtoa[:0] 2255 if added { 2256 proto = append(proto, rSubBytes...) 2257 } else { 2258 proto = append(proto, rUnsubBytes...) 2259 } 2260 proto = append(proto, accName...) 2261 proto = append(proto, ' ') 2262 proto = append(proto, sub.subject...) 2263 proto = append(proto, CR_LF...) 2264 rsProto = proto 2265 } else { 2266 // Point to the already constructed RS+/- 2267 proto = rsProto 2268 } 2269 } 2270 } else if added && inMap { 2271 // Here, we have a `nil` entry for this account in 2272 // the map, which means that we have previously sent 2273 // an A-. We have a new subscription, so we need to 2274 // send an A+ and delete the entry from the map so 2275 // that we do this only once. 2276 delete(c.gw.insim, accName) 2277 if accProto == nil { 2278 // Construct the A+ only once 2279 proto = accProtoa[:0] 2280 proto = append(proto, aSubBytes...) 2281 proto = append(proto, accName...) 2282 proto = append(proto, CR_LF...) 2283 accProto = proto 2284 } else { 2285 // Point to the already constructed A+ 2286 proto = accProto 2287 } 2288 } 2289 if proto != nil { 2290 c.enqueueProto(proto) 2291 if c.trace { 2292 c.traceOutOp("", proto[:len(proto)-LEN_CR_LF]) 2293 } 2294 } 2295 c.mu.Unlock() 2296 } 2297 } 2298 2299 // This is invoked when the first (or last) queue subscription on a 2300 // given subject/group is registered (or unregistered). Sent to all 2301 // inbound gateways. 2302 func (s *Server) sendQueueSubOrUnsubToGateways(accName string, qsub *subscription, added bool) { 2303 if qsub.queue == nil { 2304 return 2305 } 2306 2307 gwsa := [16]*client{} 2308 gws := gwsa[:0] 2309 s.getInboundGatewayConnections(&gws) 2310 if len(gws) == 0 { 2311 return 2312 } 2313 2314 var protoa [512]byte 2315 var proto []byte 2316 for _, c := range gws { 2317 if proto == nil { 2318 proto = protoa[:0] 2319 if added { 2320 proto = append(proto, rSubBytes...) 2321 } else { 2322 proto = append(proto, rUnsubBytes...) 2323 } 2324 proto = append(proto, accName...) 2325 proto = append(proto, ' ') 2326 proto = append(proto, qsub.subject...) 2327 proto = append(proto, ' ') 2328 proto = append(proto, qsub.queue...) 2329 if added { 2330 // For now, just use 1 for the weight 2331 proto = append(proto, ' ', '1') 2332 } 2333 proto = append(proto, CR_LF...) 2334 } 2335 c.mu.Lock() 2336 // If we add a queue sub, and we had previously sent an A-, 2337 // we don't need to send an A+ here, but we need to clear 2338 // the fact that we did sent the A- so that we don't send 2339 // an A+ when we will get the first non-queue sub registered. 2340 if added { 2341 if ei, ok := c.gw.insim[accName]; ok && ei == nil { 2342 delete(c.gw.insim, accName) 2343 } 2344 } 2345 c.enqueueProto(proto) 2346 if c.trace { 2347 c.traceOutOp("", proto[:len(proto)-LEN_CR_LF]) 2348 } 2349 c.mu.Unlock() 2350 } 2351 } 2352 2353 // This is invoked when a subscription (plain or queue) is 2354 // added/removed locally or in our cluster. We use ref counting 2355 // to know when to update the inbound gateways. 2356 // <Invoked from client or route connection's readLoop or when such 2357 // connection is closed> 2358 func (s *Server) gatewayUpdateSubInterest(accName string, sub *subscription, change int32) { 2359 if sub.si { 2360 return 2361 } 2362 2363 var ( 2364 keya [1024]byte 2365 key = keya[:0] 2366 entry *sitally 2367 isNew bool 2368 ) 2369 2370 s.gateway.pasi.Lock() 2371 defer s.gateway.pasi.Unlock() 2372 2373 accMap := s.gateway.pasi.m 2374 2375 // First see if we have the account 2376 st := accMap[accName] 2377 if st == nil { 2378 // Ignore remove of something we don't have 2379 if change < 0 { 2380 return 2381 } 2382 st = make(map[string]*sitally) 2383 accMap[accName] = st 2384 isNew = true 2385 } 2386 // Lookup: build the key as subject[+' '+queue] 2387 key = append(key, sub.subject...) 2388 if sub.queue != nil { 2389 key = append(key, ' ') 2390 key = append(key, sub.queue...) 2391 } 2392 if !isNew { 2393 entry = st[string(key)] 2394 } 2395 first := false 2396 last := false 2397 if entry == nil { 2398 // Ignore remove of something we don't have 2399 if change < 0 { 2400 return 2401 } 2402 entry = &sitally{n: 1, q: sub.queue != nil} 2403 st[string(key)] = entry 2404 first = true 2405 } else { 2406 entry.n += change 2407 if entry.n <= 0 { 2408 delete(st, bytesToString(key)) 2409 last = true 2410 if len(st) == 0 { 2411 delete(accMap, accName) 2412 } 2413 } 2414 } 2415 if sub.client != nil { 2416 rsubs := &s.gateway.rsubs 2417 acc := sub.client.acc 2418 sli, _ := rsubs.Load(acc) 2419 if change > 0 { 2420 var sl *Sublist 2421 if sli == nil { 2422 sl = NewSublistNoCache() 2423 rsubs.Store(acc, sl) 2424 } else { 2425 sl = sli.(*Sublist) 2426 } 2427 sl.Insert(sub) 2428 time.AfterFunc(s.gateway.recSubExp, func() { 2429 sl.Remove(sub) 2430 }) 2431 } else if sli != nil { 2432 sl := sli.(*Sublist) 2433 sl.Remove(sub) 2434 if sl.Count() == 0 { 2435 rsubs.Delete(acc) 2436 } 2437 } 2438 } 2439 if first || last { 2440 if entry.q { 2441 s.sendQueueSubOrUnsubToGateways(accName, sub, first) 2442 } else { 2443 s.maybeSendSubOrUnsubToGateways(accName, sub, first) 2444 } 2445 } 2446 } 2447 2448 // Returns true if the given subject is a GW routed reply subject, 2449 // that is, starts with $GNR and is long enough to contain cluster/server hash 2450 // and subject. 2451 func isGWRoutedReply(subj []byte) bool { 2452 return len(subj) > gwSubjectOffset && bytesToString(subj[:gwReplyPrefixLen]) == gwReplyPrefix 2453 } 2454 2455 // Same than isGWRoutedReply but accepts the old prefix $GR and returns 2456 // a boolean indicating if this is the old prefix 2457 func isGWRoutedSubjectAndIsOldPrefix(subj []byte) (bool, bool) { 2458 if isGWRoutedReply(subj) { 2459 return true, false 2460 } 2461 if len(subj) > oldGWReplyStart && bytesToString(subj[:oldGWReplyPrefixLen]) == oldGWReplyPrefix { 2462 return true, true 2463 } 2464 return false, false 2465 } 2466 2467 // Returns true if subject starts with "$GNR.". This is to check that 2468 // clients can't publish on this subject. 2469 func hasGWRoutedReplyPrefix(subj []byte) bool { 2470 return len(subj) > gwReplyPrefixLen && bytesToString(subj[:gwReplyPrefixLen]) == gwReplyPrefix 2471 } 2472 2473 // Evaluates if the given reply should be mapped or not. 2474 func (g *srvGateway) shouldMapReplyForGatewaySend(acc *Account, reply []byte) bool { 2475 // If for this account there is a recent matching subscription interest 2476 // then we will map. 2477 sli, _ := g.rsubs.Load(acc) 2478 if sli == nil { 2479 return false 2480 } 2481 sl := sli.(*Sublist) 2482 if sl.Count() > 0 { 2483 if r := sl.Match(string(reply)); len(r.psubs)+len(r.qsubs) > 0 { 2484 return true 2485 } 2486 } 2487 2488 return false 2489 } 2490 2491 var subPool = &sync.Pool{ 2492 New: func() any { 2493 return &subscription{} 2494 }, 2495 } 2496 2497 // May send a message to all outbound gateways. It is possible 2498 // that the message is not sent to a given gateway if for instance 2499 // it is known that this gateway has no interest in the account or 2500 // subject, etc.. 2501 // <Invoked from any client connection's readLoop> 2502 func (c *client) sendMsgToGateways(acc *Account, msg, subject, reply []byte, qgroups [][]byte) bool { 2503 // We had some times when we were sending across a GW with no subject, and the other side would break 2504 // due to parser error. These need to be fixed upstream but also double check here. 2505 if len(subject) == 0 { 2506 return false 2507 } 2508 gwsa := [16]*client{} 2509 gws := gwsa[:0] 2510 // This is in fast path, so avoid calling functions when possible. 2511 // Get the outbound connections in place instead of calling 2512 // getOutboundGatewayConnections(). 2513 srv := c.srv 2514 gw := srv.gateway 2515 gw.RLock() 2516 for i := 0; i < len(gw.outo); i++ { 2517 gws = append(gws, gw.outo[i]) 2518 } 2519 thisClusterReplyPrefix := gw.replyPfx 2520 thisClusterOldReplyPrefix := gw.oldReplyPfx 2521 gw.RUnlock() 2522 if len(gws) == 0 { 2523 return false 2524 } 2525 2526 mt, _ := c.isMsgTraceEnabled() 2527 if mt != nil { 2528 pa := c.pa 2529 msg = mt.setOriginAccountHeaderIfNeeded(c, acc, msg) 2530 defer func() { c.pa = pa }() 2531 } 2532 2533 var ( 2534 queuesa = [512]byte{} 2535 queues = queuesa[:0] 2536 accName = acc.Name 2537 mreplya [256]byte 2538 mreply []byte 2539 dstHash []byte 2540 checkReply = len(reply) > 0 2541 didDeliver bool 2542 prodIsMQTT = c.isMqtt() 2543 dlvMsgs int64 2544 ) 2545 2546 // Get a subscription from the pool 2547 sub := subPool.Get().(*subscription) 2548 2549 // Check if the subject is on the reply prefix, if so, we 2550 // need to send that message directly to the origin cluster. 2551 directSend, old := isGWRoutedSubjectAndIsOldPrefix(subject) 2552 if directSend { 2553 if old { 2554 dstHash = subject[oldGWReplyPrefixLen : oldGWReplyStart-1] 2555 } else { 2556 dstHash = subject[gwClusterOffset : gwClusterOffset+gwHashLen] 2557 } 2558 } 2559 for i := 0; i < len(gws); i++ { 2560 gwc := gws[i] 2561 if directSend { 2562 gwc.mu.Lock() 2563 var ok bool 2564 if gwc.gw.cfg != nil { 2565 if old { 2566 ok = bytes.Equal(dstHash, gwc.gw.cfg.oldHash) 2567 } else { 2568 ok = bytes.Equal(dstHash, gwc.gw.cfg.hash) 2569 } 2570 } 2571 gwc.mu.Unlock() 2572 if !ok { 2573 continue 2574 } 2575 } else { 2576 // Plain sub interest and queue sub results for this account/subject 2577 psi, qr := gwc.gatewayInterest(accName, string(subject)) 2578 if !psi && qr == nil { 2579 continue 2580 } 2581 queues = queuesa[:0] 2582 if qr != nil { 2583 for i := 0; i < len(qr.qsubs); i++ { 2584 qsubs := qr.qsubs[i] 2585 if len(qsubs) > 0 { 2586 queue := qsubs[0].queue 2587 add := true 2588 for _, qn := range qgroups { 2589 if bytes.Equal(queue, qn) { 2590 add = false 2591 break 2592 } 2593 } 2594 if add { 2595 qgroups = append(qgroups, queue) 2596 queues = append(queues, queue...) 2597 queues = append(queues, ' ') 2598 } 2599 } 2600 } 2601 } 2602 if !psi && len(queues) == 0 { 2603 continue 2604 } 2605 } 2606 if checkReply { 2607 // Check/map only once 2608 checkReply = false 2609 // Assume we will use original 2610 mreply = reply 2611 // Decide if we should map. 2612 if gw.shouldMapReplyForGatewaySend(acc, reply) { 2613 mreply = mreplya[:0] 2614 gwc.mu.Lock() 2615 useOldPrefix := gwc.gw.useOldPrefix 2616 gwc.mu.Unlock() 2617 if useOldPrefix { 2618 mreply = append(mreply, thisClusterOldReplyPrefix...) 2619 } else { 2620 mreply = append(mreply, thisClusterReplyPrefix...) 2621 } 2622 mreply = append(mreply, reply...) 2623 } 2624 } 2625 2626 if mt != nil { 2627 msg = mt.setHopHeader(c, msg) 2628 } 2629 2630 // Setup the message header. 2631 // Make sure we are an 'R' proto by default 2632 c.msgb[0] = 'R' 2633 mh := c.msgb[:msgHeadProtoLen] 2634 mh = append(mh, accName...) 2635 mh = append(mh, ' ') 2636 mh = append(mh, subject...) 2637 mh = append(mh, ' ') 2638 if len(queues) > 0 { 2639 if len(reply) > 0 { 2640 mh = append(mh, "+ "...) // Signal that there is a reply. 2641 mh = append(mh, mreply...) 2642 mh = append(mh, ' ') 2643 } else { 2644 mh = append(mh, "| "...) // Only queues 2645 } 2646 mh = append(mh, queues...) 2647 } else if len(reply) > 0 { 2648 mh = append(mh, mreply...) 2649 mh = append(mh, ' ') 2650 } 2651 // Headers 2652 hasHeader := c.pa.hdr > 0 2653 canReceiveHeader := gwc.headers 2654 2655 if hasHeader { 2656 if canReceiveHeader { 2657 mh[0] = 'H' 2658 mh = append(mh, c.pa.hdb...) 2659 mh = append(mh, ' ') 2660 mh = append(mh, c.pa.szb...) 2661 } else { 2662 // If we are here we need to truncate the payload size 2663 nsz := strconv.Itoa(c.pa.size - c.pa.hdr) 2664 mh = append(mh, nsz...) 2665 } 2666 } else { 2667 mh = append(mh, c.pa.szb...) 2668 } 2669 2670 mh = append(mh, CR_LF...) 2671 2672 // We reuse the subscription object that we pass to deliverMsg. 2673 // So set/reset important fields. 2674 sub.nm, sub.max = 0, 0 2675 sub.client = gwc 2676 sub.subject = subject 2677 if c.deliverMsg(prodIsMQTT, sub, acc, subject, mreply, mh, msg, false) { 2678 // We don't count internal deliveries so count only if sub.icb is nil 2679 if sub.icb == nil { 2680 dlvMsgs++ 2681 } 2682 didDeliver = true 2683 } 2684 } 2685 if dlvMsgs > 0 { 2686 totalBytes := dlvMsgs * int64(len(msg)) 2687 // For non MQTT producers, remove the CR_LF * number of messages 2688 if !prodIsMQTT { 2689 totalBytes -= dlvMsgs * int64(LEN_CR_LF) 2690 } 2691 if acc != nil { 2692 atomic.AddInt64(&acc.outMsgs, dlvMsgs) 2693 atomic.AddInt64(&acc.outBytes, totalBytes) 2694 } 2695 atomic.AddInt64(&srv.outMsgs, dlvMsgs) 2696 atomic.AddInt64(&srv.outBytes, totalBytes) 2697 } 2698 // Done with subscription, put back to pool. We don't need 2699 // to reset content since we explicitly set when using it. 2700 // However, make sure to not hold a reference to a connection. 2701 sub.client = nil 2702 subPool.Put(sub) 2703 return didDeliver 2704 } 2705 2706 // Possibly sends an A- to the remote gateway `c`. 2707 // Invoked when processing an inbound message and the account is not found. 2708 // A check under a lock that protects processing of SUBs and UNSUBs is 2709 // done to make sure that we don't send the A- if a subscription has just 2710 // been created at the same time, which would otherwise results in the 2711 // remote never sending messages on this account until a new subscription 2712 // is created. 2713 func (s *Server) gatewayHandleAccountNoInterest(c *client, accName []byte) { 2714 // Check and possibly send the A- under this lock. 2715 s.gateway.pasi.Lock() 2716 defer s.gateway.pasi.Unlock() 2717 2718 si, inMap := s.gateway.pasi.m[string(accName)] 2719 if inMap && si != nil && len(si) > 0 { 2720 return 2721 } 2722 c.sendAccountUnsubToGateway(accName) 2723 } 2724 2725 // Helper that sends an A- to this remote gateway if not already done. 2726 // This function should not be invoked directly but instead be invoked 2727 // by functions holding the gateway.pasi's Lock. 2728 func (c *client) sendAccountUnsubToGateway(accName []byte) { 2729 // Check if we have sent the A- or not. 2730 c.mu.Lock() 2731 e, sent := c.gw.insim[string(accName)] 2732 if e != nil || !sent { 2733 // Add a nil value to indicate that we have sent an A- 2734 // so that we know to send A+ when needed. 2735 c.gw.insim[string(accName)] = nil 2736 var protoa [256]byte 2737 proto := protoa[:0] 2738 proto = append(proto, aUnsubBytes...) 2739 proto = append(proto, accName...) 2740 proto = append(proto, CR_LF...) 2741 c.enqueueProto(proto) 2742 if c.trace { 2743 c.traceOutOp("", proto[:len(proto)-LEN_CR_LF]) 2744 } 2745 } 2746 c.mu.Unlock() 2747 } 2748 2749 // Possibly sends an A- for this account or RS- for this subject. 2750 // Invoked when processing an inbound message and the account is found 2751 // but there is no interest on this subject. 2752 // A test is done under a lock that protects processing of SUBs and UNSUBs 2753 // and if there is no subscription at this time, we send an A-. If there 2754 // is at least a subscription, but no interest on this subject, we send 2755 // an RS- for this subject (if not already done). 2756 func (s *Server) gatewayHandleSubjectNoInterest(c *client, acc *Account, accName, subject []byte) { 2757 s.gateway.pasi.Lock() 2758 defer s.gateway.pasi.Unlock() 2759 2760 // If there is no subscription for this account, we would normally 2761 // send an A-, however, if this account has the internal subscription 2762 // for service reply, send a specific RS- for the subject instead. 2763 // Need to grab the lock here since sublist can change during reload. 2764 acc.mu.RLock() 2765 hasSubs := acc.sl.Count() > 0 || acc.siReply != nil 2766 acc.mu.RUnlock() 2767 2768 // If there is at least a subscription, possibly send RS- 2769 if hasSubs { 2770 sendProto := false 2771 c.mu.Lock() 2772 // Send an RS- protocol if not already done and only if 2773 // not in the modeInterestOnly. 2774 e := c.gw.insim[string(accName)] 2775 if e == nil { 2776 e = &insie{ni: make(map[string]struct{})} 2777 e.ni[string(subject)] = struct{}{} 2778 c.gw.insim[string(accName)] = e 2779 sendProto = true 2780 } else if e.ni != nil { 2781 // If we are not in modeInterestOnly, check if we 2782 // have already sent an RS- 2783 if _, alreadySent := e.ni[string(subject)]; !alreadySent { 2784 // TODO(ik): pick some threshold as to when 2785 // we need to switch mode 2786 if len(e.ni) >= gatewayMaxRUnsubBeforeSwitch { 2787 // If too many RS-, switch to all-subs-mode. 2788 c.gatewaySwitchAccountToSendAllSubs(e, string(accName)) 2789 } else { 2790 e.ni[string(subject)] = struct{}{} 2791 sendProto = true 2792 } 2793 } 2794 } 2795 if sendProto { 2796 var ( 2797 protoa = [512]byte{} 2798 proto = protoa[:0] 2799 ) 2800 proto = append(proto, rUnsubBytes...) 2801 proto = append(proto, accName...) 2802 proto = append(proto, ' ') 2803 proto = append(proto, subject...) 2804 proto = append(proto, CR_LF...) 2805 c.enqueueProto(proto) 2806 if c.trace { 2807 c.traceOutOp("", proto[:len(proto)-LEN_CR_LF]) 2808 } 2809 } 2810 c.mu.Unlock() 2811 } else { 2812 // There is not a single subscription, send an A- (if not already done). 2813 c.sendAccountUnsubToGateway([]byte(acc.Name)) 2814 } 2815 } 2816 2817 // Returns the cluster hash from the gateway reply prefix 2818 func (g *srvGateway) getClusterHash() []byte { 2819 g.RLock() 2820 clusterHash := g.replyPfx[gwClusterOffset : gwClusterOffset+gwHashLen] 2821 g.RUnlock() 2822 return clusterHash 2823 } 2824 2825 // Store this route in map with the key being the remote server's name hash 2826 // and the remote server's ID hash used by gateway replies mapping routing. 2827 func (s *Server) storeRouteByHash(srvIDHash string, c *client) { 2828 if !s.gateway.enabled { 2829 return 2830 } 2831 s.gateway.routesIDByHash.Store(srvIDHash, c) 2832 } 2833 2834 // Remove the route with the given keys from the map. 2835 func (s *Server) removeRouteByHash(srvIDHash string) { 2836 if !s.gateway.enabled { 2837 return 2838 } 2839 s.gateway.routesIDByHash.Delete(srvIDHash) 2840 } 2841 2842 // Returns the route with given hash or nil if not found. 2843 // This is for gateways only. 2844 func (s *Server) getRouteByHash(hash, accName []byte) (*client, bool) { 2845 id := bytesToString(hash) 2846 var perAccount bool 2847 if v, ok := s.accRouteByHash.Load(bytesToString(accName)); ok { 2848 if v == nil { 2849 id += bytesToString(accName) 2850 perAccount = true 2851 } else { 2852 id += strconv.Itoa(v.(int)) 2853 } 2854 } 2855 if v, ok := s.gateway.routesIDByHash.Load(id); ok { 2856 return v.(*client), perAccount 2857 } else if !perAccount { 2858 // Check if we have a "no pool" connection at index 0. 2859 if v, ok := s.gateway.routesIDByHash.Load(bytesToString(hash) + "0"); ok { 2860 if r := v.(*client); r != nil { 2861 r.mu.Lock() 2862 noPool := r.route.noPool 2863 r.mu.Unlock() 2864 if noPool { 2865 return r, false 2866 } 2867 } 2868 } 2869 } 2870 return nil, perAccount 2871 } 2872 2873 // Returns the subject from the routed reply 2874 func getSubjectFromGWRoutedReply(reply []byte, isOldPrefix bool) []byte { 2875 if isOldPrefix { 2876 return reply[oldGWReplyStart:] 2877 } 2878 return reply[gwSubjectOffset:] 2879 } 2880 2881 // This should be invoked only from processInboundGatewayMsg() or 2882 // processInboundRoutedMsg() and is checking if the subject 2883 // (c.pa.subject) has the _GR_ prefix. If so, this is processed 2884 // as a GW reply and `true` is returned to indicate to the caller 2885 // that it should stop processing. 2886 // If gateway is not enabled on this server or if the subject 2887 // does not start with _GR_, `false` is returned and caller should 2888 // process message as usual. 2889 func (c *client) handleGatewayReply(msg []byte) (processed bool) { 2890 // Do not handle GW prefixed messages if this server does not have 2891 // gateway enabled or if the subject does not start with the previx. 2892 if !c.srv.gateway.enabled { 2893 return false 2894 } 2895 isGWPrefix, oldPrefix := isGWRoutedSubjectAndIsOldPrefix(c.pa.subject) 2896 if !isGWPrefix { 2897 return false 2898 } 2899 // Save original subject (in case we have to forward) 2900 orgSubject := c.pa.subject 2901 2902 var clusterHash []byte 2903 var srvHash []byte 2904 var subject []byte 2905 2906 if oldPrefix { 2907 clusterHash = c.pa.subject[oldGWReplyPrefixLen : oldGWReplyStart-1] 2908 // Check if this reply is intended for our cluster. 2909 if !bytes.Equal(clusterHash, c.srv.gateway.oldHash) { 2910 // We could report, for now, just drop. 2911 return true 2912 } 2913 subject = c.pa.subject[oldGWReplyStart:] 2914 } else { 2915 clusterHash = c.pa.subject[gwClusterOffset : gwClusterOffset+gwHashLen] 2916 // Check if this reply is intended for our cluster. 2917 if !bytes.Equal(clusterHash, c.srv.gateway.getClusterHash()) { 2918 // We could report, for now, just drop. 2919 return true 2920 } 2921 srvHash = c.pa.subject[gwServerOffset : gwServerOffset+gwHashLen] 2922 subject = c.pa.subject[gwSubjectOffset:] 2923 } 2924 2925 var route *client 2926 var perAccount bool 2927 2928 // If the origin is not this server, get the route this should be sent to. 2929 if c.kind == GATEWAY && srvHash != nil && !bytes.Equal(srvHash, c.srv.gateway.sIDHash) { 2930 route, perAccount = c.srv.getRouteByHash(srvHash, c.pa.account) 2931 // This will be possibly nil, and in this case we will try to process 2932 // the interest from this server. 2933 } 2934 2935 // Adjust the subject 2936 c.pa.subject = subject 2937 2938 // Use a stack buffer to rewrite c.pa.cache since we only need it for 2939 // getAccAndResultFromCache() 2940 var _pacache [256]byte 2941 pacache := _pacache[:0] 2942 // For routes that are dedicated to an account, do not put the account 2943 // name in the pacache. 2944 if c.kind == GATEWAY || (c.kind == ROUTER && c.route != nil && len(c.route.accName) == 0) { 2945 pacache = append(pacache, c.pa.account...) 2946 pacache = append(pacache, ' ') 2947 } 2948 pacache = append(pacache, c.pa.subject...) 2949 c.pa.pacache = pacache 2950 2951 acc, r := c.getAccAndResultFromCache() 2952 if acc == nil { 2953 typeConn := "routed" 2954 if c.kind == GATEWAY { 2955 typeConn = "gateway" 2956 } 2957 c.Debugf("Unknown account %q for %s message on subject: %q", c.pa.account, typeConn, c.pa.subject) 2958 if c.kind == GATEWAY { 2959 c.srv.gatewayHandleAccountNoInterest(c, c.pa.account) 2960 } 2961 return true 2962 } 2963 2964 // If route is nil, we will process the incoming message locally. 2965 if route == nil { 2966 // Check if this is a service reply subject (_R_) 2967 isServiceReply := isServiceReply(c.pa.subject) 2968 2969 var queues [][]byte 2970 if len(r.psubs)+len(r.qsubs) > 0 { 2971 flags := pmrCollectQueueNames | pmrIgnoreEmptyQueueFilter 2972 // If this message came from a ROUTE, allow to pick queue subs 2973 // only if the message was directly sent by the "gateway" server 2974 // in our cluster that received it. 2975 if c.kind == ROUTER { 2976 flags |= pmrAllowSendFromRouteToRoute 2977 } 2978 _, queues = c.processMsgResults(acc, r, msg, nil, c.pa.subject, c.pa.reply, flags) 2979 } 2980 // Since this was a reply that made it to the origin cluster, 2981 // we now need to send the message with the real subject to 2982 // gateways in case they have interest on that reply subject. 2983 if !isServiceReply { 2984 c.sendMsgToGateways(acc, msg, c.pa.subject, c.pa.reply, queues) 2985 } 2986 } else if c.kind == GATEWAY { 2987 // Only if we are a gateway connection should we try to route 2988 // to the server where the request originated. 2989 var bufa [256]byte 2990 var buf = bufa[:0] 2991 buf = append(buf, msgHeadProto...) 2992 if !perAccount { 2993 buf = append(buf, acc.Name...) 2994 buf = append(buf, ' ') 2995 } 2996 buf = append(buf, orgSubject...) 2997 buf = append(buf, ' ') 2998 if len(c.pa.reply) > 0 { 2999 buf = append(buf, c.pa.reply...) 3000 buf = append(buf, ' ') 3001 } 3002 szb := c.pa.szb 3003 if c.pa.hdr >= 0 { 3004 if route.headers { 3005 buf[0] = 'H' 3006 buf = append(buf, c.pa.hdb...) 3007 buf = append(buf, ' ') 3008 } else { 3009 szb = []byte(strconv.Itoa(c.pa.size - c.pa.hdr)) 3010 msg = msg[c.pa.hdr:] 3011 } 3012 } 3013 buf = append(buf, szb...) 3014 mhEnd := len(buf) 3015 buf = append(buf, _CRLF_...) 3016 buf = append(buf, msg...) 3017 3018 route.mu.Lock() 3019 route.enqueueProto(buf) 3020 if route.trace { 3021 route.traceOutOp("", buf[:mhEnd]) 3022 } 3023 route.mu.Unlock() 3024 } 3025 return true 3026 } 3027 3028 // Process a message coming from a remote gateway. Send to any sub/qsub 3029 // in our cluster that is matching. When receiving a message for an 3030 // account or subject for which there is no interest in this cluster 3031 // an A-/RS- protocol may be send back. 3032 // <Invoked from inbound connection's readLoop> 3033 func (c *client) processInboundGatewayMsg(msg []byte) { 3034 // Update statistics 3035 c.in.msgs++ 3036 // The msg includes the CR_LF, so pull back out for accounting. 3037 c.in.bytes += int32(len(msg) - LEN_CR_LF) 3038 3039 if c.opts.Verbose { 3040 c.sendOK() 3041 } 3042 3043 // Mostly under testing scenarios. 3044 if c.srv == nil { 3045 return 3046 } 3047 3048 // If the subject (c.pa.subject) has the gateway prefix, this function will 3049 // handle it. 3050 if c.handleGatewayReply(msg) { 3051 // We are done here. 3052 return 3053 } 3054 3055 acc, r := c.getAccAndResultFromCache() 3056 if acc == nil { 3057 c.Debugf("Unknown account %q for gateway message on subject: %q", c.pa.account, c.pa.subject) 3058 c.srv.gatewayHandleAccountNoInterest(c, c.pa.account) 3059 return 3060 } 3061 3062 // Check if this is a service reply subject (_R_) 3063 noInterest := len(r.psubs) == 0 3064 checkNoInterest := true 3065 if acc.NumServiceImports() > 0 { 3066 if isServiceReply(c.pa.subject) { 3067 checkNoInterest = false 3068 } else { 3069 // We need to eliminate the subject interest from the service imports here to 3070 // make sure we send the proper no interest if the service import is the only interest. 3071 noInterest = true 3072 for _, sub := range r.psubs { 3073 // sub.si indicates that this is a subscription for service import, and is immutable. 3074 // So sub.si is false, then this is a subscription for something else, so there is 3075 // actually proper interest. 3076 if !sub.si { 3077 noInterest = false 3078 break 3079 } 3080 } 3081 } 3082 } 3083 if checkNoInterest && noInterest { 3084 // If there is no interest on plain subs, possibly send an RS-, 3085 // even if there is qsubs interest. 3086 c.srv.gatewayHandleSubjectNoInterest(c, acc, c.pa.account, c.pa.subject) 3087 3088 // If there is also no queue filter, then no point in continuing 3089 // (even if r.qsubs i > 0). 3090 if len(c.pa.queues) == 0 { 3091 return 3092 } 3093 } 3094 c.processMsgResults(acc, r, msg, nil, c.pa.subject, c.pa.reply, pmrNoFlag) 3095 } 3096 3097 // Indicates that the remote which we are sending messages to 3098 // has decided to send us all its subs interest so that we 3099 // stop doing optimistic sends. 3100 // <Invoked from outbound connection's readLoop> 3101 func (c *client) gatewayAllSubsReceiveStart(info *Info) { 3102 account := getAccountFromGatewayCommand(c, info, "start") 3103 if account == "" { 3104 return 3105 } 3106 3107 c.Debugf("Gateway %q: switching account %q to %s mode", 3108 info.Gateway, account, InterestOnly) 3109 3110 // Since the remote would send us this start command 3111 // only after sending us too many RS- for this account, 3112 // we should always have an entry here. 3113 // TODO(ik): Should we close connection with protocol violation 3114 // error if that happens? 3115 ei, _ := c.gw.outsim.Load(account) 3116 if ei != nil { 3117 e := ei.(*outsie) 3118 e.Lock() 3119 e.mode = Transitioning 3120 e.Unlock() 3121 } else { 3122 e := &outsie{sl: NewSublistWithCache()} 3123 e.mode = Transitioning 3124 c.mu.Lock() 3125 c.gw.outsim.Store(account, e) 3126 c.mu.Unlock() 3127 } 3128 } 3129 3130 // Indicates that the remote has finished sending all its 3131 // subscriptions and we should now not send unless we know 3132 // there is explicit interest. 3133 // <Invoked from outbound connection's readLoop> 3134 func (c *client) gatewayAllSubsReceiveComplete(info *Info) { 3135 account := getAccountFromGatewayCommand(c, info, "complete") 3136 if account == _EMPTY_ { 3137 return 3138 } 3139 // Done receiving all subs from remote. Set the `ni` 3140 // map to nil so that gatewayInterest() no longer 3141 // uses it. 3142 ei, _ := c.gw.outsim.Load(account) 3143 if ei != nil { 3144 e := ei.(*outsie) 3145 // Needs locking here since `ni` is checked by 3146 // many go-routines calling gatewayInterest() 3147 e.Lock() 3148 e.ni = nil 3149 e.mode = InterestOnly 3150 e.Unlock() 3151 3152 c.Debugf("Gateway %q: switching account %q to %s mode complete", 3153 info.Gateway, account, InterestOnly) 3154 } 3155 } 3156 3157 // small helper to get the account name from the INFO command. 3158 func getAccountFromGatewayCommand(c *client, info *Info, cmd string) string { 3159 if info.GatewayCmdPayload == nil { 3160 c.sendErrAndErr(fmt.Sprintf("Account absent from receive-all-subscriptions-%s command", cmd)) 3161 c.closeConnection(ProtocolViolation) 3162 return _EMPTY_ 3163 } 3164 return string(info.GatewayCmdPayload) 3165 } 3166 3167 // Switch to send-all-subs mode for the given gateway and account. 3168 // This is invoked when processing an inbound message and we 3169 // reach a point where we had to send a lot of RS- for this 3170 // account. We will send an INFO protocol to indicate that we 3171 // start sending all our subs (for this account), followed by 3172 // all subs (RS+) and finally an INFO to indicate the end of it. 3173 // The remote will then send messages only if it finds explicit 3174 // interest in the sublist created based on all RS+ that we just 3175 // sent. 3176 // The client's lock is held on entry. 3177 // <Invoked from inbound connection's readLoop> 3178 func (c *client) gatewaySwitchAccountToSendAllSubs(e *insie, accName string) { 3179 // Set this map to nil so that the no-interest is no longer checked. 3180 e.ni = nil 3181 // Switch mode to transitioning to prevent switchAccountToInterestMode 3182 // to possibly call this function multiple times. 3183 e.mode = Transitioning 3184 s := c.srv 3185 3186 remoteGWName := c.gw.name 3187 c.Debugf("Gateway %q: switching account %q to %s mode", 3188 remoteGWName, accName, InterestOnly) 3189 3190 // Function that will create an INFO protocol 3191 // and set proper command. 3192 sendCmd := func(cmd byte, useLock bool) { 3193 // Use bare server info and simply set the 3194 // gateway name and command 3195 info := Info{ 3196 Gateway: s.gateway.name, 3197 GatewayCmd: cmd, 3198 GatewayCmdPayload: stringToBytes(accName), 3199 } 3200 3201 b, _ := json.Marshal(&info) 3202 infoJSON := []byte(fmt.Sprintf(InfoProto, b)) 3203 if useLock { 3204 c.mu.Lock() 3205 } 3206 c.enqueueProto(infoJSON) 3207 if useLock { 3208 c.mu.Unlock() 3209 } 3210 } 3211 // Send the start command. When remote receives this, 3212 // it may continue to send optimistic messages, but 3213 // it will start to register RS+/RS- in sublist instead 3214 // of noInterest map. 3215 sendCmd(gatewayCmdAllSubsStart, false) 3216 3217 // Execute this in separate go-routine as to not block 3218 // the readLoop (which may cause the otherside to close 3219 // the connection due to slow consumer) 3220 s.startGoRoutine(func() { 3221 defer s.grWG.Done() 3222 3223 s.sendAccountSubsToGateway(c, accName) 3224 // Send the complete command. When the remote receives 3225 // this, it will not send a message unless it has a 3226 // matching sub from us. 3227 sendCmd(gatewayCmdAllSubsComplete, true) 3228 3229 c.Debugf("Gateway %q: switching account %q to %s mode complete", 3230 remoteGWName, accName, InterestOnly) 3231 }) 3232 } 3233 3234 // Keeps track of the routed reply to be used when/if application sends back a 3235 // message on the reply without the prefix. 3236 // If `client` is not nil, it will be stored in the client gwReplyMapping structure, 3237 // and client lock is held on entry. 3238 // If `client` is nil, the mapping is stored in the client's account's gwReplyMapping 3239 // structure. Account lock will be explicitly acquired. 3240 // This is a server receiver because we use a timer interval that is avail in 3241 // Server.gateway object. 3242 func (s *Server) trackGWReply(c *client, acc *Account, reply, routedReply []byte) { 3243 var l sync.Locker 3244 var g *gwReplyMapping 3245 if acc != nil { 3246 acc.mu.Lock() 3247 defer acc.mu.Unlock() 3248 g = &acc.gwReplyMapping 3249 l = &acc.mu 3250 } else { 3251 g = &c.gwReplyMapping 3252 l = &c.mu 3253 } 3254 ttl := s.gateway.recSubExp 3255 wasEmpty := len(g.mapping) == 0 3256 if g.mapping == nil { 3257 g.mapping = make(map[string]*gwReplyMap) 3258 } 3259 // The reason we pass both `reply` and `routedReply`, is that in some cases, 3260 // `routedReply` may have a deliver subject appended, something look like: 3261 // "_GR_.xxx.yyy.$JS.ACK.$MQTT_msgs.someid.1.1.1.1620086713306484000.0@$MQTT.msgs.foo" 3262 // but `reply` has already been cleaned up (delivery subject removed from tail): 3263 // "$JS.ACK.$MQTT_msgs.someid.1.1.1.1620086713306484000.0" 3264 // So we will use that knowledge so we don't have to make any cleaning here. 3265 routedReply = routedReply[:gwSubjectOffset+len(reply)] 3266 // We need to make a copy so that we don't reference the underlying 3267 // read buffer. 3268 ms := string(routedReply) 3269 grm := &gwReplyMap{ms: ms, exp: time.Now().Add(ttl).UnixNano()} 3270 // If we are here with the same key but different mapped replies 3271 // (say $GNR._.A.srv1.bar and then $GNR._.B.srv2.bar), we need to 3272 // store it otherwise we would take the risk of the reply not 3273 // making it back. 3274 g.mapping[ms[gwSubjectOffset:]] = grm 3275 if wasEmpty { 3276 atomic.StoreInt32(&g.check, 1) 3277 s.gwrm.m.Store(g, l) 3278 if atomic.CompareAndSwapInt32(&s.gwrm.w, 0, 1) { 3279 select { 3280 case s.gwrm.ch <- ttl: 3281 default: 3282 } 3283 } 3284 } 3285 } 3286 3287 // Starts a long lived go routine that is responsible to 3288 // remove GW reply mapping that have expired. 3289 func (s *Server) startGWReplyMapExpiration() { 3290 s.mu.Lock() 3291 s.gwrm.ch = make(chan time.Duration, 1) 3292 s.mu.Unlock() 3293 s.startGoRoutine(func() { 3294 defer s.grWG.Done() 3295 3296 t := time.NewTimer(time.Hour) 3297 var ttl time.Duration 3298 for { 3299 select { 3300 case <-t.C: 3301 if ttl == 0 { 3302 t.Reset(time.Hour) 3303 continue 3304 } 3305 now := time.Now().UnixNano() 3306 mapEmpty := true 3307 s.gwrm.m.Range(func(k, v any) bool { 3308 g := k.(*gwReplyMapping) 3309 l := v.(sync.Locker) 3310 l.Lock() 3311 for k, grm := range g.mapping { 3312 if grm.exp <= now { 3313 delete(g.mapping, k) 3314 if len(g.mapping) == 0 { 3315 atomic.StoreInt32(&g.check, 0) 3316 s.gwrm.m.Delete(g) 3317 } 3318 } 3319 } 3320 l.Unlock() 3321 mapEmpty = false 3322 return true 3323 }) 3324 if mapEmpty && atomic.CompareAndSwapInt32(&s.gwrm.w, 1, 0) { 3325 ttl = 0 3326 t.Reset(time.Hour) 3327 } else { 3328 t.Reset(ttl) 3329 } 3330 case cttl := <-s.gwrm.ch: 3331 ttl = cttl 3332 if !t.Stop() { 3333 select { 3334 case <-t.C: 3335 default: 3336 } 3337 } 3338 t.Reset(ttl) 3339 case <-s.quitCh: 3340 return 3341 } 3342 } 3343 }) 3344 }