github.com/nats-io/nats-server/v2@v2.11.0-preview.2/server/leafnode_test.go (about) 1 // Copyright 2019-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 "bufio" 18 "bytes" 19 "context" 20 "crypto/tls" 21 "fmt" 22 "math/rand" 23 "net" 24 "net/http" 25 "net/http/httptest" 26 "net/url" 27 "reflect" 28 "strings" 29 "sync" 30 "sync/atomic" 31 "testing" 32 "time" 33 34 "github.com/nats-io/nkeys" 35 36 "github.com/klauspost/compress/s2" 37 jwt "github.com/nats-io/jwt/v2" 38 "github.com/nats-io/nats.go" 39 40 "github.com/nats-io/nats-server/v2/internal/testhelper" 41 ) 42 43 type captureLeafNodeRandomIPLogger struct { 44 DummyLogger 45 ch chan struct{} 46 ips [3]int 47 } 48 49 func (c *captureLeafNodeRandomIPLogger) Debugf(format string, v ...any) { 50 msg := fmt.Sprintf(format, v...) 51 if strings.Contains(msg, "hostname_to_resolve") { 52 ippos := strings.Index(msg, "127.0.0.") 53 if ippos != -1 { 54 n := int(msg[ippos+8] - '1') 55 c.ips[n]++ 56 for _, v := range c.ips { 57 if v < 2 { 58 return 59 } 60 } 61 // All IPs got at least some hit, we are done. 62 c.ch <- struct{}{} 63 } 64 } 65 } 66 67 func TestLeafNodeRandomIP(t *testing.T) { 68 u, err := url.Parse("nats://hostname_to_resolve:1234") 69 if err != nil { 70 t.Fatalf("Error parsing: %v", err) 71 } 72 73 resolver := &myDummyDNSResolver{ips: []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}} 74 75 o := DefaultOptions() 76 o.Host = "127.0.0.1" 77 o.Port = -1 78 o.LeafNode.Port = 0 79 o.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{u}}} 80 o.LeafNode.ReconnectInterval = 50 * time.Millisecond 81 o.LeafNode.resolver = resolver 82 o.LeafNode.dialTimeout = 15 * time.Millisecond 83 s := RunServer(o) 84 defer s.Shutdown() 85 86 l := &captureLeafNodeRandomIPLogger{ch: make(chan struct{})} 87 s.SetLogger(l, true, true) 88 89 select { 90 case <-l.ch: 91 case <-time.After(3 * time.Second): 92 t.Fatalf("Does not seem to have used random IPs") 93 } 94 } 95 96 func TestLeafNodeRandomRemotes(t *testing.T) { 97 // 16! possible permutations. 98 orderedURLs := make([]*url.URL, 0, 16) 99 for i := 0; i < cap(orderedURLs); i++ { 100 orderedURLs = append(orderedURLs, &url.URL{ 101 Scheme: "nats-leaf", 102 Host: fmt.Sprintf("host%d:7422", i), 103 }) 104 } 105 106 o := DefaultOptions() 107 o.LeafNode.Remotes = []*RemoteLeafOpts{ 108 {NoRandomize: true}, 109 {NoRandomize: false}, 110 } 111 o.LeafNode.Remotes[0].URLs = make([]*url.URL, cap(orderedURLs)) 112 copy(o.LeafNode.Remotes[0].URLs, orderedURLs) 113 o.LeafNode.Remotes[1].URLs = make([]*url.URL, cap(orderedURLs)) 114 copy(o.LeafNode.Remotes[1].URLs, orderedURLs) 115 116 s := RunServer(o) 117 defer s.Shutdown() 118 119 s.mu.Lock() 120 r1 := s.leafRemoteCfgs[0] 121 r2 := s.leafRemoteCfgs[1] 122 s.mu.Unlock() 123 124 r1.RLock() 125 gotOrdered := r1.urls 126 r1.RUnlock() 127 if got, want := len(gotOrdered), len(orderedURLs); got != want { 128 t.Fatalf("Unexpected rem0 len URLs, got %d, want %d", got, want) 129 } 130 131 // These should be IN order. 132 for i := range orderedURLs { 133 if got, want := gotOrdered[i].String(), orderedURLs[i].String(); got != want { 134 t.Fatalf("Unexpected ordered url, got %s, want %s", got, want) 135 } 136 } 137 138 r2.RLock() 139 gotRandom := r2.urls 140 r2.RUnlock() 141 if got, want := len(gotRandom), len(orderedURLs); got != want { 142 t.Fatalf("Unexpected rem1 len URLs, got %d, want %d", got, want) 143 } 144 145 // These should be OUT of order. 146 var random bool 147 for i := range orderedURLs { 148 if gotRandom[i].String() != orderedURLs[i].String() { 149 random = true 150 break 151 } 152 } 153 if !random { 154 t.Fatal("Expected urls to be random") 155 } 156 } 157 158 type testLoopbackResolver struct{} 159 160 func (r *testLoopbackResolver) LookupHost(ctx context.Context, host string) ([]string, error) { 161 return []string{"127.0.0.1"}, nil 162 } 163 164 func TestLeafNodeTLSWithCerts(t *testing.T) { 165 conf1 := createConfFile(t, []byte(` 166 port: -1 167 leaf { 168 listen: "127.0.0.1:-1" 169 tls { 170 ca_file: "../test/configs/certs/tlsauth/ca.pem" 171 cert_file: "../test/configs/certs/tlsauth/server.pem" 172 key_file: "../test/configs/certs/tlsauth/server-key.pem" 173 timeout: 2 174 } 175 } 176 `)) 177 s1, o1 := RunServerWithConfig(conf1) 178 defer s1.Shutdown() 179 180 u, err := url.Parse(fmt.Sprintf("nats://localhost:%d", o1.LeafNode.Port)) 181 if err != nil { 182 t.Fatalf("Error parsing url: %v", err) 183 } 184 conf2 := createConfFile(t, []byte(fmt.Sprintf(` 185 port: -1 186 leaf { 187 remotes [ 188 { 189 url: "%s" 190 tls { 191 ca_file: "../test/configs/certs/tlsauth/ca.pem" 192 cert_file: "../test/configs/certs/tlsauth/client.pem" 193 key_file: "../test/configs/certs/tlsauth/client-key.pem" 194 timeout: 2 195 } 196 } 197 ] 198 } 199 `, u.String()))) 200 o2, err := ProcessConfigFile(conf2) 201 if err != nil { 202 t.Fatalf("Error processing config file: %v", err) 203 } 204 o2.NoLog, o2.NoSigs = true, true 205 o2.LeafNode.resolver = &testLoopbackResolver{} 206 s2 := RunServer(o2) 207 defer s2.Shutdown() 208 209 checkFor(t, 3*time.Second, 10*time.Millisecond, func() error { 210 if nln := s1.NumLeafNodes(); nln != 1 { 211 return fmt.Errorf("Number of leaf nodes is %d", nln) 212 } 213 return nil 214 }) 215 } 216 217 func TestLeafNodeTLSRemoteWithNoCerts(t *testing.T) { 218 conf1 := createConfFile(t, []byte(` 219 port: -1 220 leaf { 221 listen: "127.0.0.1:-1" 222 tls { 223 ca_file: "../test/configs/certs/tlsauth/ca.pem" 224 cert_file: "../test/configs/certs/tlsauth/server.pem" 225 key_file: "../test/configs/certs/tlsauth/server-key.pem" 226 timeout: 2 227 } 228 } 229 `)) 230 s1, o1 := RunServerWithConfig(conf1) 231 defer s1.Shutdown() 232 233 u, err := url.Parse(fmt.Sprintf("nats://localhost:%d", o1.LeafNode.Port)) 234 if err != nil { 235 t.Fatalf("Error parsing url: %v", err) 236 } 237 conf2 := createConfFile(t, []byte(fmt.Sprintf(` 238 port: -1 239 leaf { 240 remotes [ 241 { 242 url: "%s" 243 tls { 244 ca_file: "../test/configs/certs/tlsauth/ca.pem" 245 timeout: 5 246 } 247 } 248 ] 249 } 250 `, u.String()))) 251 o2, err := ProcessConfigFile(conf2) 252 if err != nil { 253 t.Fatalf("Error processing config file: %v", err) 254 } 255 256 if len(o2.LeafNode.Remotes) == 0 { 257 t.Fatal("Expected at least a single leaf remote") 258 } 259 260 var ( 261 got float64 = o2.LeafNode.Remotes[0].TLSTimeout 262 expected float64 = 5 263 ) 264 if got != expected { 265 t.Fatalf("Expected %v, got: %v", expected, got) 266 } 267 o2.NoLog, o2.NoSigs = true, true 268 o2.LeafNode.resolver = &testLoopbackResolver{} 269 s2 := RunServer(o2) 270 defer s2.Shutdown() 271 272 checkFor(t, 3*time.Second, 10*time.Millisecond, func() error { 273 if nln := s1.NumLeafNodes(); nln != 1 { 274 return fmt.Errorf("Number of leaf nodes is %d", nln) 275 } 276 return nil 277 }) 278 279 // Here we only process options without starting the server 280 // and without a root CA for the remote. 281 conf3 := createConfFile(t, []byte(fmt.Sprintf(` 282 port: -1 283 leaf { 284 remotes [ 285 { 286 url: "%s" 287 tls { 288 timeout: 10 289 } 290 } 291 ] 292 } 293 `, u.String()))) 294 o3, err := ProcessConfigFile(conf3) 295 if err != nil { 296 t.Fatalf("Error processing config file: %v", err) 297 } 298 299 if len(o3.LeafNode.Remotes) == 0 { 300 t.Fatal("Expected at least a single leaf remote") 301 } 302 got = o3.LeafNode.Remotes[0].TLSTimeout 303 expected = 10 304 if got != expected { 305 t.Fatalf("Expected %v, got: %v", expected, got) 306 } 307 308 // Here we only process options without starting the server 309 // and check the default for leafnode remotes. 310 conf4 := createConfFile(t, []byte(fmt.Sprintf(` 311 port: -1 312 leaf { 313 remotes [ 314 { 315 url: "%s" 316 tls { 317 ca_file: "../test/configs/certs/tlsauth/ca.pem" 318 } 319 } 320 ] 321 } 322 `, u.String()))) 323 o4, err := ProcessConfigFile(conf4) 324 if err != nil { 325 t.Fatalf("Error processing config file: %v", err) 326 } 327 328 if len(o4.LeafNode.Remotes) == 0 { 329 t.Fatal("Expected at least a single leaf remote") 330 } 331 got = o4.LeafNode.Remotes[0].TLSTimeout 332 expected = float64(DEFAULT_LEAF_TLS_TIMEOUT) / float64(time.Second) 333 if int(got) != int(expected) { 334 t.Fatalf("Expected %v, got: %v", expected, got) 335 } 336 } 337 338 type captureErrorLogger struct { 339 DummyLogger 340 errCh chan string 341 } 342 343 func (l *captureErrorLogger) Errorf(format string, v ...any) { 344 select { 345 case l.errCh <- fmt.Sprintf(format, v...): 346 default: 347 } 348 } 349 350 func TestLeafNodeAccountNotFound(t *testing.T) { 351 ob := DefaultOptions() 352 ob.LeafNode.Host = "127.0.0.1" 353 ob.LeafNode.Port = -1 354 sb := RunServer(ob) 355 defer sb.Shutdown() 356 357 u, _ := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", ob.LeafNode.Port)) 358 359 oa := DefaultOptions() 360 oa.Cluster.Name = "xyz" 361 oa.LeafNode.ReconnectInterval = 10 * time.Millisecond 362 oa.LeafNode.Remotes = []*RemoteLeafOpts{ 363 { 364 LocalAccount: "foo", 365 URLs: []*url.URL{u}, 366 }, 367 } 368 // Expected to fail 369 if _, err := NewServer(oa); err == nil || !strings.Contains(err.Error(), "local account") { 370 t.Fatalf("Expected server to fail with error about no local account, got %v", err) 371 } 372 oa.Accounts = []*Account{NewAccount("foo")} 373 sa := RunServer(oa) 374 defer sa.Shutdown() 375 376 l := &captureErrorLogger{errCh: make(chan string, 1)} 377 sa.SetLogger(l, false, false) 378 379 checkLeafNodeConnected(t, sa) 380 381 // Now simulate account is removed with config reload, or it expires. 382 sa.accounts.Delete("foo") 383 384 // Restart B (with same Port) 385 sb.Shutdown() 386 sb = RunServer(ob) 387 defer sb.Shutdown() 388 389 // Wait for report of error 390 select { 391 case e := <-l.errCh: 392 if !strings.Contains(e, "Unable to lookup account") { 393 t.Fatalf("Expected error about no local account, got %s", e) 394 } 395 case <-time.After(2 * time.Second): 396 t.Fatalf("Did not get the error") 397 } 398 399 // TODO below test is bogus. Instead add the account, do a reload, and make sure the connection works. 400 401 // For now, sa would try to recreate the connection for ever. 402 // Check that lid is increasing... 403 checkFor(t, 2*time.Second, 100*time.Millisecond, func() error { 404 if lid := atomic.LoadUint64(&sa.gcid); lid < 3 { 405 return fmt.Errorf("Seems like connection was not retried, lid currently only at %d", lid) 406 } 407 return nil 408 }) 409 } 410 411 // This test ensures that we can connect using proper user/password 412 // to a LN URL that was discovered through the INFO protocol. 413 // We also check that the password doesn't leak to debug/trace logs. 414 func TestLeafNodeBasicAuthFailover(t *testing.T) { 415 // Something a little longer than "pwd" to prevent false positives amongst many log lines; 416 // don't make it complex enough to be subject to %-escaping, we want a simple needle search. 417 fatalPassword := "pwdfatal" 418 419 content := ` 420 listen: "127.0.0.1:-1" 421 cluster { 422 name: "abc" 423 listen: "127.0.0.1:-1" 424 %s 425 } 426 leafnodes { 427 listen: "127.0.0.1:-1" 428 authorization { 429 user: foo 430 password: %s 431 timeout: 1 432 } 433 } 434 ` 435 conf := createConfFile(t, []byte(fmt.Sprintf(content, "", fatalPassword))) 436 437 sb1, ob1 := RunServerWithConfig(conf) 438 defer sb1.Shutdown() 439 440 conf = createConfFile(t, []byte(fmt.Sprintf(content, fmt.Sprintf("routes: [nats://127.0.0.1:%d]", ob1.Cluster.Port), fatalPassword))) 441 442 sb2, _ := RunServerWithConfig(conf) 443 defer sb2.Shutdown() 444 445 checkClusterFormed(t, sb1, sb2) 446 447 content = ` 448 port: -1 449 accounts { 450 foo {} 451 } 452 leafnodes { 453 listen: "127.0.0.1:-1" 454 remotes [ 455 { 456 account: "foo" 457 url: "nats://foo:%s@127.0.0.1:%d" 458 } 459 ] 460 } 461 ` 462 conf = createConfFile(t, []byte(fmt.Sprintf(content, fatalPassword, ob1.LeafNode.Port))) 463 464 sa, _ := RunServerWithConfig(conf) 465 defer sa.Shutdown() 466 467 l := testhelper.NewDummyLogger(100) 468 sa.SetLogger(l, true, true) // we want debug & trace logs, to check for passwords in them 469 470 checkLeafNodeConnected(t, sa) 471 472 // Shutdown sb1, sa should reconnect to sb2 473 sb1.Shutdown() 474 475 // Wait a bit to make sure there was a disconnect and attempt to reconnect. 476 time.Sleep(250 * time.Millisecond) 477 478 // Should be able to reconnect 479 checkLeafNodeConnected(t, sa) 480 481 // Look at all our logs for the password; at time of writing it doesn't appear 482 // but we want to safe-guard against it. 483 l.CheckForProhibited(t, "fatal password", fatalPassword) 484 } 485 486 func TestLeafNodeRTT(t *testing.T) { 487 ob := DefaultOptions() 488 ob.PingInterval = 15 * time.Millisecond 489 ob.LeafNode.Host = "127.0.0.1" 490 ob.LeafNode.Port = -1 491 sb := RunServer(ob) 492 defer sb.Shutdown() 493 494 lnBURL, _ := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", ob.LeafNode.Port)) 495 oa := DefaultOptions() 496 oa.Cluster.Name = "xyz" 497 oa.PingInterval = 15 * time.Millisecond 498 oa.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{lnBURL}}} 499 sa := RunServer(oa) 500 defer sa.Shutdown() 501 502 checkLeafNodeConnected(t, sa) 503 504 checkRTT := func(t *testing.T, s *Server) time.Duration { 505 t.Helper() 506 var ln *client 507 s.mu.Lock() 508 for _, l := range s.leafs { 509 ln = l 510 break 511 } 512 s.mu.Unlock() 513 var rtt time.Duration 514 checkFor(t, 2*firstPingInterval, 15*time.Millisecond, func() error { 515 ln.mu.Lock() 516 rtt = ln.rtt 517 ln.mu.Unlock() 518 if rtt == 0 { 519 return fmt.Errorf("RTT not tracked") 520 } 521 return nil 522 }) 523 return rtt 524 } 525 526 prevA := checkRTT(t, sa) 527 prevB := checkRTT(t, sb) 528 529 // Wait to see if RTT is updated 530 checkUpdated := func(t *testing.T, s *Server, prev time.Duration) { 531 attempts := 0 532 timeout := time.Now().Add(2 * firstPingInterval) 533 for time.Now().Before(timeout) { 534 if rtt := checkRTT(t, s); rtt != prev { 535 return 536 } 537 attempts++ 538 if attempts == 5 { 539 s.mu.Lock() 540 for _, ln := range s.leafs { 541 ln.mu.Lock() 542 ln.rtt = 0 543 ln.mu.Unlock() 544 break 545 } 546 s.mu.Unlock() 547 } 548 time.Sleep(15 * time.Millisecond) 549 } 550 t.Fatalf("RTT probably not updated") 551 } 552 checkUpdated(t, sa, prevA) 553 checkUpdated(t, sb, prevB) 554 555 sa.Shutdown() 556 sb.Shutdown() 557 558 // Now check that initial RTT is computed prior to first PingInterval 559 // Get new options to avoid possible race changing the ping interval. 560 ob = DefaultOptions() 561 ob.Cluster.Name = "xyz" 562 ob.PingInterval = time.Minute 563 ob.LeafNode.Host = "127.0.0.1" 564 ob.LeafNode.Port = -1 565 sb = RunServer(ob) 566 defer sb.Shutdown() 567 568 lnBURL, _ = url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", ob.LeafNode.Port)) 569 oa = DefaultOptions() 570 oa.PingInterval = time.Minute 571 oa.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{lnBURL}}} 572 sa = RunServer(oa) 573 defer sa.Shutdown() 574 575 checkLeafNodeConnected(t, sa) 576 577 checkRTT(t, sa) 578 checkRTT(t, sb) 579 } 580 581 func TestLeafNodeValidateAuthOptions(t *testing.T) { 582 opts := DefaultOptions() 583 opts.LeafNode.Username = "user1" 584 opts.LeafNode.Password = "pwd" 585 opts.LeafNode.Users = []*User{{Username: "user", Password: "pwd"}} 586 if _, err := NewServer(opts); err == nil || !strings.Contains(err.Error(), 587 "can not have a single user/pass and a users array") { 588 t.Fatalf("Expected error about mixing single/multi users, got %v", err) 589 } 590 591 // Check duplicate user names 592 opts.LeafNode.Username = _EMPTY_ 593 opts.LeafNode.Password = _EMPTY_ 594 opts.LeafNode.Users = append(opts.LeafNode.Users, &User{Username: "user", Password: "pwd"}) 595 if _, err := NewServer(opts); err == nil || !strings.Contains(err.Error(), "duplicate user") { 596 t.Fatalf("Expected error about duplicate user, got %v", err) 597 } 598 } 599 600 func TestLeafNodeBasicAuthSingleton(t *testing.T) { 601 opts := DefaultOptions() 602 opts.LeafNode.Port = -1 603 opts.LeafNode.Account = "unknown" 604 if s, err := NewServer(opts); err == nil || !strings.Contains(err.Error(), "cannot find") { 605 if s != nil { 606 s.Shutdown() 607 } 608 t.Fatalf("Expected error about account not found, got %v", err) 609 } 610 611 template := ` 612 port: -1 613 accounts: { 614 ACC1: { users = [{user: "user1", password: "user1"}] } 615 ACC2: { users = [{user: "user2", password: "user2"}] } 616 } 617 leafnodes: { 618 port: -1 619 authorization { 620 %s 621 account: "ACC1" 622 } 623 } 624 ` 625 for iter, test := range []struct { 626 name string 627 userSpec string 628 lnURLCreds string 629 shouldFail bool 630 }{ 631 {"no user creds required and no user so binds to ACC1", "", "", false}, 632 {"no user creds required and pick user2 associated to ACC2", "", "user2:user2@", false}, 633 {"no user creds required and unknown user should fail", "", "unknown:user@", true}, 634 {"user creds required so binds to ACC1", "user: \"ln\"\npass: \"pwd\"", "ln:pwd@", false}, 635 } { 636 t.Run(test.name, func(t *testing.T) { 637 638 conf := createConfFile(t, []byte(fmt.Sprintf(template, test.userSpec))) 639 s1, o1 := RunServerWithConfig(conf) 640 defer s1.Shutdown() 641 642 // Create a sub on "foo" for account ACC1 (user user1), which is the one 643 // bound to the accepted LN connection. 644 ncACC1 := natsConnect(t, fmt.Sprintf("nats://user1:user1@%s:%d", o1.Host, o1.Port)) 645 defer ncACC1.Close() 646 sub1 := natsSubSync(t, ncACC1, "foo") 647 natsFlush(t, ncACC1) 648 649 // Create a sub on "foo" for account ACC2 (user user2). This one should 650 // not receive any message. 651 ncACC2 := natsConnect(t, fmt.Sprintf("nats://user2:user2@%s:%d", o1.Host, o1.Port)) 652 defer ncACC2.Close() 653 sub2 := natsSubSync(t, ncACC2, "foo") 654 natsFlush(t, ncACC2) 655 656 conf = createConfFile(t, []byte(fmt.Sprintf(` 657 port: -1 658 leafnodes: { 659 remotes = [ { url: "nats-leaf://%s%s:%d" } ] 660 } 661 `, test.lnURLCreds, o1.LeafNode.Host, o1.LeafNode.Port))) 662 s2, _ := RunServerWithConfig(conf) 663 defer s2.Shutdown() 664 665 if test.shouldFail { 666 // Wait a bit and ensure that there is no leaf node connection 667 time.Sleep(100 * time.Millisecond) 668 checkFor(t, time.Second, 15*time.Millisecond, func() error { 669 if n := s1.NumLeafNodes(); n != 0 { 670 return fmt.Errorf("Expected no leafnode connection, got %v", n) 671 } 672 return nil 673 }) 674 return 675 } 676 677 checkLeafNodeConnected(t, s2) 678 679 nc := natsConnect(t, s2.ClientURL()) 680 defer nc.Close() 681 natsPub(t, nc, "foo", []byte("hello")) 682 // If url contains known user, even when there is no credentials 683 // required, the connection will be bound to the user's account. 684 if iter == 1 { 685 // Should not receive on "ACC1", but should on "ACC2" 686 if _, err := sub1.NextMsg(100 * time.Millisecond); err != nats.ErrTimeout { 687 t.Fatalf("Expected timeout error, got %v", err) 688 } 689 natsNexMsg(t, sub2, time.Second) 690 } else { 691 // Should receive on "ACC1"... 692 natsNexMsg(t, sub1, time.Second) 693 // but not received on "ACC2" since leafnode bound to account "ACC1". 694 if _, err := sub2.NextMsg(100 * time.Millisecond); err != nats.ErrTimeout { 695 t.Fatalf("Expected timeout error, got %v", err) 696 } 697 } 698 }) 699 } 700 } 701 702 func TestLeafNodeBasicAuthMultiple(t *testing.T) { 703 conf := createConfFile(t, []byte(` 704 port: -1 705 accounts: { 706 S1ACC1: { users = [{user: "user1", password: "user1"}] } 707 S1ACC2: { users = [{user: "user2", password: "user2"}] } 708 } 709 leafnodes: { 710 port: -1 711 authorization { 712 users = [ 713 {user: "ln1", password: "ln1", account: "S1ACC1"} 714 {user: "ln2", password: "ln2", account: "S1ACC2"} 715 {user: "ln3", password: "ln3"} 716 ] 717 } 718 } 719 `)) 720 s1, o1 := RunServerWithConfig(conf) 721 defer s1.Shutdown() 722 723 // Make sure that we reject a LN connection if user does not match 724 conf = createConfFile(t, []byte(fmt.Sprintf(` 725 port: -1 726 leafnodes: { 727 remotes = [{url: "nats-leaf://wron:user@%s:%d"}] 728 } 729 `, o1.LeafNode.Host, o1.LeafNode.Port))) 730 s2, _ := RunServerWithConfig(conf) 731 defer s2.Shutdown() 732 // Give a chance for s2 to attempt to connect and make sure that s1 733 // did not register a LN connection. 734 time.Sleep(100 * time.Millisecond) 735 if n := s1.NumLeafNodes(); n != 0 { 736 t.Fatalf("Expected no leafnode connection, got %v", n) 737 } 738 s2.Shutdown() 739 740 ncACC1 := natsConnect(t, fmt.Sprintf("nats://user1:user1@%s:%d", o1.Host, o1.Port)) 741 defer ncACC1.Close() 742 sub1 := natsSubSync(t, ncACC1, "foo") 743 natsFlush(t, ncACC1) 744 745 ncACC2 := natsConnect(t, fmt.Sprintf("nats://user2:user2@%s:%d", o1.Host, o1.Port)) 746 defer ncACC2.Close() 747 sub2 := natsSubSync(t, ncACC2, "foo") 748 natsFlush(t, ncACC2) 749 750 // We will start s2 with 2 LN connections that should bind local account S2ACC1 751 // to account S1ACC1 and S2ACC2 to account S1ACC2 on s1. 752 conf = createConfFile(t, []byte(fmt.Sprintf(` 753 port: -1 754 accounts { 755 S2ACC1 { users = [{user: "user1", password: "user1"}] } 756 S2ACC2 { users = [{user: "user2", password: "user2"}] } 757 } 758 leafnodes: { 759 remotes = [ 760 { 761 url: "nats-leaf://ln1:ln1@%s:%d" 762 account: "S2ACC1" 763 } 764 { 765 url: "nats-leaf://ln2:ln2@%s:%d" 766 account: "S2ACC2" 767 } 768 ] 769 } 770 `, o1.LeafNode.Host, o1.LeafNode.Port, o1.LeafNode.Host, o1.LeafNode.Port))) 771 s2, o2 := RunServerWithConfig(conf) 772 defer s2.Shutdown() 773 774 checkFor(t, 5*time.Second, 100*time.Millisecond, func() error { 775 if nln := s2.NumLeafNodes(); nln != 2 { 776 return fmt.Errorf("Expected 2 connected leafnodes for server %q, got %d", s2.ID(), nln) 777 } 778 return nil 779 }) 780 781 // Create a user connection on s2 that binds to S2ACC1 (use user1). 782 nc1 := natsConnect(t, fmt.Sprintf("nats://user1:user1@%s:%d", o2.Host, o2.Port)) 783 defer nc1.Close() 784 785 // Create an user connection on s2 that binds to S2ACC2 (use user2). 786 nc2 := natsConnect(t, fmt.Sprintf("nats://user2:user2@%s:%d", o2.Host, o2.Port)) 787 defer nc2.Close() 788 789 // Now if a message is published from nc1, sub1 should receive it since 790 // their account are bound together. 791 natsPub(t, nc1, "foo", []byte("hello")) 792 natsNexMsg(t, sub1, time.Second) 793 // But sub2 should not receive it since different account. 794 if _, err := sub2.NextMsg(100 * time.Millisecond); err != nats.ErrTimeout { 795 t.Fatalf("Expected timeout error, got %v", err) 796 } 797 798 // Now use nc2 (S2ACC2) to publish 799 natsPub(t, nc2, "foo", []byte("hello")) 800 // Expect sub2 to receive and sub1 not to. 801 natsNexMsg(t, sub2, time.Second) 802 if _, err := sub1.NextMsg(100 * time.Millisecond); err != nats.ErrTimeout { 803 t.Fatalf("Expected timeout error, got %v", err) 804 } 805 806 // Now check that we don't panic if no account is specified for 807 // a given user. 808 conf = createConfFile(t, []byte(fmt.Sprintf(` 809 port: -1 810 leafnodes: { 811 remotes = [ 812 { url: "nats-leaf://ln3:ln3@%s:%d" } 813 ] 814 } 815 `, o1.LeafNode.Host, o1.LeafNode.Port))) 816 s3, _ := RunServerWithConfig(conf) 817 defer s3.Shutdown() 818 } 819 820 type loopDetectedLogger struct { 821 DummyLogger 822 ch chan string 823 } 824 825 func (l *loopDetectedLogger) Errorf(format string, v ...any) { 826 msg := fmt.Sprintf(format, v...) 827 if strings.Contains(msg, "Loop") { 828 select { 829 case l.ch <- msg: 830 default: 831 } 832 } 833 } 834 835 func TestLeafNodeLoop(t *testing.T) { 836 test := func(t *testing.T, cluster bool) { 837 // This test requires that we set the port to known value because 838 // we want A point to B and B to A. 839 oa := DefaultOptions() 840 oa.ServerName = "A" 841 if !cluster { 842 oa.Cluster.Port = 0 843 oa.Cluster.Name = _EMPTY_ 844 } 845 oa.LeafNode.ReconnectInterval = 10 * time.Millisecond 846 oa.LeafNode.Port = 1234 847 ub, _ := url.Parse("nats://127.0.0.1:5678") 848 oa.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{ub}}} 849 oa.LeafNode.connDelay = 50 * time.Millisecond 850 sa := RunServer(oa) 851 defer sa.Shutdown() 852 853 la := &loopDetectedLogger{ch: make(chan string, 1)} 854 sa.SetLogger(la, false, false) 855 856 ob := DefaultOptions() 857 ob.ServerName = "B" 858 if !cluster { 859 ob.Cluster.Port = 0 860 ob.Cluster.Name = _EMPTY_ 861 } else { 862 ob.Cluster.Name = "xyz" 863 } 864 ob.LeafNode.ReconnectInterval = 10 * time.Millisecond 865 ob.LeafNode.Port = 5678 866 ua, _ := url.Parse("nats://127.0.0.1:1234") 867 ob.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{ua}}} 868 ob.LeafNode.connDelay = 50 * time.Millisecond 869 sb := RunServer(ob) 870 defer sb.Shutdown() 871 872 lb := &loopDetectedLogger{ch: make(chan string, 1)} 873 sb.SetLogger(lb, false, false) 874 875 select { 876 case <-la.ch: 877 // OK! 878 case <-lb.ch: 879 // OK! 880 case <-time.After(5 * time.Second): 881 t.Fatalf("Did not get any error regarding loop") 882 } 883 884 sb.Shutdown() 885 ob.Port = -1 886 ob.Cluster.Port = -1 887 ob.LeafNode.Remotes = nil 888 sb = RunServer(ob) 889 defer sb.Shutdown() 890 891 checkLeafNodeConnected(t, sa) 892 } 893 t.Run("standalone", func(t *testing.T) { test(t, false) }) 894 t.Run("cluster", func(t *testing.T) { test(t, true) }) 895 } 896 897 func TestLeafNodeLoopFromDAG(t *testing.T) { 898 // We want B & C to point to A, A itself does not point to any other server. 899 // We need to cancel clustering since now this will suppress on its own. 900 oa := DefaultOptions() 901 oa.ServerName = "A" 902 oa.LeafNode.connDelay = 50 * time.Millisecond 903 oa.LeafNode.ReconnectInterval = 10 * time.Millisecond 904 oa.LeafNode.Port = -1 905 oa.Cluster = ClusterOpts{} 906 sa := RunServer(oa) 907 defer sa.Shutdown() 908 909 ua, _ := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", oa.LeafNode.Port)) 910 911 // B will point to A 912 ob := DefaultOptions() 913 ob.ServerName = "B" 914 ob.LeafNode.connDelay = 50 * time.Millisecond 915 ob.LeafNode.ReconnectInterval = 10 * time.Millisecond 916 ob.LeafNode.Port = -1 917 ob.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{ua}}} 918 ob.Cluster = ClusterOpts{} 919 sb := RunServer(ob) 920 defer sb.Shutdown() 921 922 ub, _ := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", ob.LeafNode.Port)) 923 924 checkLeafNodeConnected(t, sa) 925 checkLeafNodeConnected(t, sb) 926 927 // C will point to A and B 928 oc := DefaultOptions() 929 oc.ServerName = "C" 930 oc.LeafNode.connDelay = 50 * time.Millisecond 931 oc.LeafNode.ReconnectInterval = 10 * time.Millisecond 932 oc.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{ua}}, {URLs: []*url.URL{ub}}} 933 oc.LeafNode.connDelay = 100 * time.Millisecond // Allow logger to be attached before connecting. 934 oc.Cluster = ClusterOpts{} 935 sc := RunServer(oc) 936 937 lc := &loopDetectedLogger{ch: make(chan string, 1)} 938 sc.SetLogger(lc, false, false) 939 940 // We should get an error. 941 select { 942 case <-lc.ch: 943 // OK 944 case <-time.After(2 * time.Second): 945 t.Fatalf("Did not get any error regarding loop") 946 } 947 948 // C should not be connected to anything. 949 checkLeafNodeConnectedCount(t, sc, 0) 950 // A and B are connected to each other. 951 checkLeafNodeConnectedCount(t, sa, 1) 952 checkLeafNodeConnectedCount(t, sb, 1) 953 954 // Shutdown C and restart without the loop. 955 sc.Shutdown() 956 oc.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{ub}}} 957 958 sc = RunServer(oc) 959 defer sc.Shutdown() 960 961 checkLeafNodeConnectedCount(t, sa, 1) 962 checkLeafNodeConnectedCount(t, sb, 2) 963 checkLeafNodeConnectedCount(t, sc, 1) 964 } 965 966 func TestLeafNodeCloseTLSConnection(t *testing.T) { 967 opts := DefaultOptions() 968 opts.DisableShortFirstPing = true 969 opts.LeafNode.Host = "127.0.0.1" 970 opts.LeafNode.Port = -1 971 opts.LeafNode.TLSTimeout = 100 972 tc := &TLSConfigOpts{ 973 CertFile: "./configs/certs/server.pem", 974 KeyFile: "./configs/certs/key.pem", 975 Insecure: true, 976 } 977 tlsConf, err := GenTLSConfig(tc) 978 if err != nil { 979 t.Fatalf("Error generating tls config: %v", err) 980 } 981 opts.LeafNode.TLSConfig = tlsConf 982 opts.NoLog = true 983 opts.NoSigs = true 984 s := RunServer(opts) 985 defer s.Shutdown() 986 987 endpoint := fmt.Sprintf("%s:%d", opts.LeafNode.Host, opts.LeafNode.Port) 988 conn, err := net.DialTimeout("tcp", endpoint, 2*time.Second) 989 if err != nil { 990 t.Fatalf("Unexpected error on dial: %v", err) 991 } 992 defer conn.Close() 993 994 br := bufio.NewReaderSize(conn, 100) 995 if _, err := br.ReadString('\n'); err != nil { 996 t.Fatalf("Unexpected error reading INFO: %v", err) 997 } 998 999 tlsConn := tls.Client(conn, &tls.Config{InsecureSkipVerify: true}) 1000 defer tlsConn.Close() 1001 if err := tlsConn.Handshake(); err != nil { 1002 t.Fatalf("Unexpected error during handshake: %v", err) 1003 } 1004 connectOp := []byte("CONNECT {\"name\":\"leaf\",\"verbose\":false,\"pedantic\":false}\r\n") 1005 if _, err := tlsConn.Write(connectOp); err != nil { 1006 t.Fatalf("Unexpected error writing CONNECT: %v", err) 1007 } 1008 if _, err := tlsConn.Write([]byte("PING\r\n")); err != nil { 1009 t.Fatalf("Unexpected error writing PING: %v", err) 1010 } 1011 1012 checkLeafNodeConnected(t, s) 1013 1014 // Get leaf connection 1015 var leaf *client 1016 s.mu.Lock() 1017 for _, l := range s.leafs { 1018 leaf = l 1019 break 1020 } 1021 s.mu.Unlock() 1022 // Fill the buffer. We want to timeout on write so that nc.Close() 1023 // would block due to a write that cannot complete. 1024 buf := make([]byte, 64*1024) 1025 done := false 1026 for !done { 1027 leaf.nc.SetWriteDeadline(time.Now().Add(time.Second)) 1028 if _, err := leaf.nc.Write(buf); err != nil { 1029 done = true 1030 } 1031 leaf.nc.SetWriteDeadline(time.Time{}) 1032 } 1033 ch := make(chan bool) 1034 go func() { 1035 select { 1036 case <-ch: 1037 return 1038 case <-time.After(3 * time.Second): 1039 fmt.Println("!!!! closeConnection is blocked, test will hang !!!") 1040 return 1041 } 1042 }() 1043 // Close the route 1044 leaf.closeConnection(SlowConsumerWriteDeadline) 1045 ch <- true 1046 } 1047 1048 func TestLeafNodeTLSSaveName(t *testing.T) { 1049 opts := DefaultOptions() 1050 opts.LeafNode.Host = "127.0.0.1" 1051 opts.LeafNode.Port = -1 1052 tc := &TLSConfigOpts{ 1053 CertFile: "../test/configs/certs/server-noip.pem", 1054 KeyFile: "../test/configs/certs/server-key-noip.pem", 1055 Insecure: true, 1056 } 1057 tlsConf, err := GenTLSConfig(tc) 1058 if err != nil { 1059 t.Fatalf("Error generating tls config: %v", err) 1060 } 1061 opts.LeafNode.TLSConfig = tlsConf 1062 s := RunServer(opts) 1063 defer s.Shutdown() 1064 1065 lo := DefaultOptions() 1066 u, _ := url.Parse(fmt.Sprintf("nats://localhost:%d", opts.LeafNode.Port)) 1067 lo.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{u}}} 1068 lo.LeafNode.ReconnectInterval = 15 * time.Millisecond 1069 ln := RunServer(lo) 1070 defer ln.Shutdown() 1071 1072 // We know connection will fail, but it should not fail because of error such as: 1073 // "cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs" 1074 // This would mean that we are not saving the hostname to use during the TLS handshake. 1075 1076 le := &captureErrorLogger{errCh: make(chan string, 100)} 1077 ln.SetLogger(le, false, false) 1078 1079 tm := time.NewTimer(time.Second) 1080 var done bool 1081 for !done { 1082 select { 1083 case err := <-le.errCh: 1084 if strings.Contains(err, "doesn't contain any IP SANs") { 1085 t.Fatalf("Got this error: %q", err) 1086 } 1087 case <-tm.C: 1088 done = true 1089 } 1090 } 1091 } 1092 1093 func TestLeafNodeRemoteWrongPort(t *testing.T) { 1094 for _, test1 := range []struct { 1095 name string 1096 clusterAdvertise bool 1097 leafnodeAdvertise bool 1098 }{ 1099 {"advertise_on", false, false}, 1100 {"cluster_no_advertise", true, false}, 1101 {"leafnode_no_advertise", false, true}, 1102 } { 1103 t.Run(test1.name, func(t *testing.T) { 1104 oa := DefaultOptions() 1105 // Make sure we have all ports (client, route, gateway) and we will try 1106 // to create a leafnode to connection to each and make sure we get the error. 1107 oa.Cluster.NoAdvertise = test1.clusterAdvertise 1108 oa.Cluster.Name = "A" 1109 oa.Cluster.Host = "127.0.0.1" 1110 oa.Cluster.Port = -1 1111 oa.Gateway.Host = "127.0.0.1" 1112 oa.Gateway.Port = -1 1113 oa.Gateway.Name = "A" 1114 oa.LeafNode.Host = "127.0.0.1" 1115 oa.LeafNode.Port = -1 1116 oa.LeafNode.NoAdvertise = test1.leafnodeAdvertise 1117 oa.Accounts = []*Account{NewAccount("sys")} 1118 oa.SystemAccount = "sys" 1119 sa := RunServer(oa) 1120 defer sa.Shutdown() 1121 1122 ob := DefaultOptions() 1123 ob.Cluster.NoAdvertise = test1.clusterAdvertise 1124 ob.Cluster.Name = "A" 1125 ob.Cluster.Host = "127.0.0.1" 1126 ob.Cluster.Port = -1 1127 ob.Routes = RoutesFromStr(fmt.Sprintf("nats://%s:%d", oa.Cluster.Host, oa.Cluster.Port)) 1128 ob.Gateway.Host = "127.0.0.1" 1129 ob.Gateway.Port = -1 1130 ob.Gateway.Name = "A" 1131 ob.LeafNode.Host = "127.0.0.1" 1132 ob.LeafNode.Port = -1 1133 ob.LeafNode.NoAdvertise = test1.leafnodeAdvertise 1134 ob.Accounts = []*Account{NewAccount("sys")} 1135 ob.SystemAccount = "sys" 1136 sb := RunServer(ob) 1137 defer sb.Shutdown() 1138 1139 checkClusterFormed(t, sa, sb) 1140 1141 for _, test := range []struct { 1142 name string 1143 port int 1144 }{ 1145 {"client", oa.Port}, 1146 {"cluster", oa.Cluster.Port}, 1147 {"gateway", oa.Gateway.Port}, 1148 } { 1149 t.Run(test.name, func(t *testing.T) { 1150 oc := DefaultOptions() 1151 // Server with the wrong config against non leafnode port. 1152 leafURL, _ := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", test.port)) 1153 oc.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{leafURL}}} 1154 oc.LeafNode.ReconnectInterval = 5 * time.Millisecond 1155 sc := RunServer(oc) 1156 defer sc.Shutdown() 1157 l := &captureErrorLogger{errCh: make(chan string, 10)} 1158 sc.SetLogger(l, true, true) 1159 1160 select { 1161 case e := <-l.errCh: 1162 if strings.Contains(e, ErrConnectedToWrongPort.Error()) { 1163 return 1164 } 1165 case <-time.After(2 * time.Second): 1166 t.Fatalf("Did not get any error about connecting to wrong port for %q - %q", 1167 test1.name, test.name) 1168 } 1169 }) 1170 } 1171 }) 1172 } 1173 } 1174 1175 func TestLeafNodeRemoteIsHub(t *testing.T) { 1176 oa := testDefaultOptionsForGateway("A") 1177 oa.Accounts = []*Account{NewAccount("sys")} 1178 oa.SystemAccount = "sys" 1179 sa := RunServer(oa) 1180 defer sa.Shutdown() 1181 1182 lno := DefaultOptions() 1183 lno.LeafNode.Host = "127.0.0.1" 1184 lno.LeafNode.Port = -1 1185 ln := RunServer(lno) 1186 defer ln.Shutdown() 1187 1188 ob1 := testGatewayOptionsFromToWithServers(t, "B", "A", sa) 1189 ob1.Accounts = []*Account{NewAccount("sys")} 1190 ob1.SystemAccount = "sys" 1191 ob1.Cluster.Host = "127.0.0.1" 1192 ob1.Cluster.Port = -1 1193 ob1.LeafNode.Host = "127.0.0.1" 1194 ob1.LeafNode.Port = -1 1195 u, _ := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", lno.LeafNode.Port)) 1196 ob1.LeafNode.Remotes = []*RemoteLeafOpts{ 1197 { 1198 URLs: []*url.URL{u}, 1199 Hub: true, 1200 }, 1201 } 1202 sb1 := RunServer(ob1) 1203 defer sb1.Shutdown() 1204 1205 waitForOutboundGateways(t, sb1, 1, 2*time.Second) 1206 waitForInboundGateways(t, sb1, 1, 2*time.Second) 1207 waitForOutboundGateways(t, sa, 1, 2*time.Second) 1208 waitForInboundGateways(t, sa, 1, 2*time.Second) 1209 1210 checkLeafNodeConnected(t, sb1) 1211 1212 // For now, due to issue 977, let's restart the leafnode so that the 1213 // leafnode connect is propagated in the super-cluster. 1214 ln.Shutdown() 1215 ln = RunServer(lno) 1216 defer ln.Shutdown() 1217 checkLeafNodeConnected(t, sb1) 1218 1219 // Connect another server in cluster B 1220 ob2 := testGatewayOptionsFromToWithServers(t, "B", "A", sa) 1221 ob2.Accounts = []*Account{NewAccount("sys")} 1222 ob2.SystemAccount = "sys" 1223 ob2.Cluster.Host = "127.0.0.1" 1224 ob2.Cluster.Port = -1 1225 ob2.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", ob1.Cluster.Port)) 1226 sb2 := RunServer(ob2) 1227 defer sb2.Shutdown() 1228 1229 checkClusterFormed(t, sb1, sb2) 1230 waitForOutboundGateways(t, sb2, 1, 2*time.Second) 1231 1232 expectedSubs := ln.NumSubscriptions() + 2 1233 1234 // Create sub on "foo" connected to sa 1235 ncA := natsConnect(t, sa.ClientURL()) 1236 defer ncA.Close() 1237 subFoo := natsSubSync(t, ncA, "foo") 1238 1239 // Create sub on "bar" connected to sb2 1240 ncB2 := natsConnect(t, sb2.ClientURL()) 1241 defer ncB2.Close() 1242 subBar := natsSubSync(t, ncB2, "bar") 1243 1244 // Make sure subscriptions have propagated to the leafnode. 1245 checkFor(t, time.Second, 10*time.Millisecond, func() error { 1246 if subs := ln.NumSubscriptions(); subs < expectedSubs { 1247 return fmt.Errorf("Number of subs is %d", subs) 1248 } 1249 return nil 1250 }) 1251 1252 // Create pub connection on leafnode 1253 ncLN := natsConnect(t, ln.ClientURL()) 1254 defer ncLN.Close() 1255 1256 // Publish on foo and make sure it is received. 1257 natsPub(t, ncLN, "foo", []byte("msg")) 1258 natsNexMsg(t, subFoo, time.Second) 1259 1260 // Publish on foo and make sure it is received. 1261 natsPub(t, ncLN, "bar", []byte("msg")) 1262 natsNexMsg(t, subBar, time.Second) 1263 } 1264 1265 func TestLeafNodePermissions(t *testing.T) { 1266 lo1 := DefaultOptions() 1267 lo1.LeafNode.Host = "127.0.0.1" 1268 lo1.LeafNode.Port = -1 1269 ln1 := RunServer(lo1) 1270 defer ln1.Shutdown() 1271 1272 errLog := &captureErrorLogger{errCh: make(chan string, 1)} 1273 ln1.SetLogger(errLog, false, false) 1274 1275 u, _ := url.Parse(fmt.Sprintf("nats://%s:%d", lo1.LeafNode.Host, lo1.LeafNode.Port)) 1276 lo2 := DefaultOptions() 1277 lo2.Cluster.Name = "xyz" 1278 lo2.LeafNode.ReconnectInterval = 5 * time.Millisecond 1279 lo2.LeafNode.connDelay = 100 * time.Millisecond 1280 lo2.LeafNode.Remotes = []*RemoteLeafOpts{ 1281 { 1282 URLs: []*url.URL{u}, 1283 DenyExports: []string{"export.*", "export"}, 1284 DenyImports: []string{"import.*", "import"}, 1285 }, 1286 } 1287 ln2 := RunServer(lo2) 1288 defer ln2.Shutdown() 1289 1290 checkLeafNodeConnected(t, ln1) 1291 1292 // Create clients on ln1 and ln2 1293 nc1, err := nats.Connect(ln1.ClientURL()) 1294 if err != nil { 1295 t.Fatalf("Error creating client: %v", err) 1296 } 1297 defer nc1.Close() 1298 nc2, err := nats.Connect(ln2.ClientURL()) 1299 if err != nil { 1300 t.Fatalf("Error creating client: %v", err) 1301 } 1302 defer nc2.Close() 1303 1304 checkSubs := func(acc *Account, expected int) { 1305 t.Helper() 1306 checkFor(t, time.Second, 15*time.Millisecond, func() error { 1307 if n := acc.TotalSubs(); n != expected { 1308 return fmt.Errorf("Expected %d subs, got %v", expected, n) 1309 } 1310 return nil 1311 }) 1312 } 1313 1314 // Create a sub on ">" on LN1 1315 subAll := natsSubSync(t, nc1, ">") 1316 // this should be registered in LN2 (there is 1 sub for LN1 $LDS subject) + SYS IMPORTS 1317 checkSubs(ln2.globalAccount(), 12) 1318 1319 // Check deny export clause from messages published from LN2 1320 for _, test := range []struct { 1321 name string 1322 subject string 1323 received bool 1324 }{ 1325 {"do not send on export.bat", "export.bat", false}, 1326 {"do not send on export", "export", false}, 1327 {"send on foo", "foo", true}, 1328 {"send on export.this.one", "export.this.one", true}, 1329 } { 1330 t.Run(test.name, func(t *testing.T) { 1331 nc2.Publish(test.subject, []byte("msg")) 1332 if test.received { 1333 natsNexMsg(t, subAll, time.Second) 1334 } else { 1335 if _, err := subAll.NextMsg(50 * time.Millisecond); err == nil { 1336 t.Fatalf("Should not have received message on %q", test.subject) 1337 } 1338 } 1339 }) 1340 } 1341 1342 subAll.Unsubscribe() 1343 // Goes down by 1. 1344 checkSubs(ln2.globalAccount(), 11) 1345 1346 // We used to make sure we would not do subscriptions however that 1347 // was incorrect. We need to check publishes, not the subscriptions. 1348 // For instance if we can publish across a leafnode to foo, and the 1349 // other side has a subsxcription for '*' the message should cross 1350 // the leafnode. The old way would not allow this. 1351 1352 // Now check deny import clause. 1353 // As of now, we don't suppress forwarding of subscriptions on LN2 that 1354 // match the deny import clause to be forwarded to LN1. However, messages 1355 // should still not be able to come back to LN2. 1356 for _, test := range []struct { 1357 name string 1358 subSubject string 1359 pubSubject string 1360 ok bool 1361 }{ 1362 {"reject import on import.*", "import.*", "import.bad", false}, 1363 {"reject import on import", "import", "import", false}, 1364 {"accepts import on foo", "foo", "foo", true}, 1365 {"accepts import on import.this.one.ok", "import.*.>", "import.this.one.ok", true}, 1366 } { 1367 t.Run(test.name, func(t *testing.T) { 1368 sub := natsSubSync(t, nc2, test.subSubject) 1369 checkSubs(ln2.globalAccount(), 12) 1370 1371 if !test.ok { 1372 nc1.Publish(test.pubSubject, []byte("msg")) 1373 if _, err := sub.NextMsg(50 * time.Millisecond); err == nil { 1374 t.Fatalf("Did not expect to get the message") 1375 } 1376 } else { 1377 checkSubs(ln1.globalAccount(), 11) 1378 nc1.Publish(test.pubSubject, []byte("msg")) 1379 natsNexMsg(t, sub, time.Second) 1380 } 1381 sub.Unsubscribe() 1382 checkSubs(ln1.globalAccount(), 10) 1383 }) 1384 } 1385 } 1386 1387 func TestLeafNodePermissionsConcurrentAccess(t *testing.T) { 1388 lo1 := DefaultOptions() 1389 lo1.LeafNode.Host = "127.0.0.1" 1390 lo1.LeafNode.Port = -1 1391 ln1 := RunServer(lo1) 1392 defer ln1.Shutdown() 1393 1394 nc1 := natsConnect(t, ln1.ClientURL()) 1395 defer nc1.Close() 1396 1397 natsSub(t, nc1, "_INBOX.>", func(_ *nats.Msg) {}) 1398 natsFlush(t, nc1) 1399 1400 ch := make(chan struct{}, 1) 1401 wg := sync.WaitGroup{} 1402 wg.Add(2) 1403 1404 publish := func(nc *nats.Conn) { 1405 defer wg.Done() 1406 1407 for { 1408 select { 1409 case <-ch: 1410 return 1411 default: 1412 nc.Publish(nats.NewInbox(), []byte("hello")) 1413 } 1414 } 1415 } 1416 1417 go publish(nc1) 1418 1419 u, _ := url.Parse(fmt.Sprintf("nats://%s:%d", lo1.LeafNode.Host, lo1.LeafNode.Port)) 1420 lo2 := DefaultOptions() 1421 lo2.Cluster.Name = "xyz" 1422 lo2.LeafNode.ReconnectInterval = 5 * time.Millisecond 1423 lo2.LeafNode.connDelay = 500 * time.Millisecond 1424 lo2.LeafNode.Remotes = []*RemoteLeafOpts{ 1425 { 1426 URLs: []*url.URL{u}, 1427 DenyExports: []string{"foo"}, 1428 DenyImports: []string{"bar"}, 1429 }, 1430 } 1431 ln2 := RunServer(lo2) 1432 defer ln2.Shutdown() 1433 1434 nc2 := natsConnect(t, ln2.ClientURL()) 1435 defer nc2.Close() 1436 1437 natsSub(t, nc2, "_INBOX.>", func(_ *nats.Msg) {}) 1438 natsFlush(t, nc2) 1439 1440 go publish(nc2) 1441 1442 checkLeafNodeConnected(t, ln1) 1443 checkLeafNodeConnected(t, ln2) 1444 1445 time.Sleep(50 * time.Millisecond) 1446 close(ch) 1447 wg.Wait() 1448 } 1449 1450 func TestLeafNodePubAllowedPruning(t *testing.T) { 1451 c := &client{} 1452 c.setPermissions(&Permissions{Publish: &SubjectPermission{Allow: []string{"foo"}}}) 1453 1454 gr := 100 1455 wg := sync.WaitGroup{} 1456 wg.Add(gr) 1457 for i := 0; i < gr; i++ { 1458 go func() { 1459 defer wg.Done() 1460 for i := 0; i < 100; i++ { 1461 c.pubAllowed(nats.NewInbox()) 1462 } 1463 }() 1464 } 1465 1466 wg.Wait() 1467 if n := int(atomic.LoadInt32(&c.perms.pcsz)); n > maxPermCacheSize { 1468 t.Fatalf("Expected size to be less than %v, got %v", maxPermCacheSize, n) 1469 } 1470 if n := atomic.LoadInt32(&c.perms.prun); n != 0 { 1471 t.Fatalf("c.perms.prun should be 0, was %v", n) 1472 } 1473 } 1474 1475 func TestLeafNodeExportPermissionsNotForSpecialSubs(t *testing.T) { 1476 lo1 := DefaultOptions() 1477 lo1.Accounts = []*Account{NewAccount("SYS")} 1478 lo1.SystemAccount = "SYS" 1479 lo1.Cluster.Name = "A" 1480 lo1.Gateway.Name = "A" 1481 lo1.Gateway.Port = -1 1482 lo1.LeafNode.Host = "127.0.0.1" 1483 lo1.LeafNode.Port = -1 1484 ln1 := RunServer(lo1) 1485 defer ln1.Shutdown() 1486 1487 u, _ := url.Parse(fmt.Sprintf("nats://%s:%d", lo1.LeafNode.Host, lo1.LeafNode.Port)) 1488 lo2 := DefaultOptions() 1489 lo2.LeafNode.Remotes = []*RemoteLeafOpts{ 1490 { 1491 URLs: []*url.URL{u}, 1492 DenyExports: []string{">"}, 1493 }, 1494 } 1495 ln2 := RunServer(lo2) 1496 defer ln2.Shutdown() 1497 1498 checkLeafNodeConnected(t, ln1) 1499 1500 // The deny is totally restrictive, but make sure that we still accept the $LDS, $GR and _GR_ go from LN1. 1501 checkFor(t, time.Second, 15*time.Millisecond, func() error { 1502 // We should have registered the 3 subs from the accepting leafnode. 1503 if n := ln2.globalAccount().TotalSubs(); n != 9 { 1504 return fmt.Errorf("Expected %d subs, got %v", 9, n) 1505 } 1506 return nil 1507 }) 1508 } 1509 1510 // Make sure that if the node that detects the loop (and sends the error and 1511 // close the connection) is the accept side, the remote node (the one that solicits) 1512 // properly use the reconnect delay. 1513 func TestLeafNodeLoopDetectedOnAcceptSide(t *testing.T) { 1514 bo := DefaultOptions() 1515 bo.LeafNode.Host = "127.0.0.1" 1516 bo.LeafNode.Port = -1 1517 b := RunServer(bo) 1518 defer b.Shutdown() 1519 1520 l := &loopDetectedLogger{ch: make(chan string, 1)} 1521 b.SetLogger(l, false, false) 1522 1523 u, _ := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", bo.LeafNode.Port)) 1524 1525 ao := testDefaultOptionsForGateway("A") 1526 ao.Accounts = []*Account{NewAccount("SYS")} 1527 ao.SystemAccount = "SYS" 1528 ao.LeafNode.ReconnectInterval = 5 * time.Millisecond 1529 ao.LeafNode.Remotes = []*RemoteLeafOpts{ 1530 { 1531 URLs: []*url.URL{u}, 1532 Hub: true, 1533 }, 1534 } 1535 a := RunServer(ao) 1536 defer a.Shutdown() 1537 1538 co := testGatewayOptionsFromToWithServers(t, "C", "A", a) 1539 co.Accounts = []*Account{NewAccount("SYS")} 1540 co.SystemAccount = "SYS" 1541 co.LeafNode.ReconnectInterval = 5 * time.Millisecond 1542 co.LeafNode.Remotes = []*RemoteLeafOpts{ 1543 { 1544 URLs: []*url.URL{u}, 1545 Hub: true, 1546 }, 1547 } 1548 c := RunServer(co) 1549 defer c.Shutdown() 1550 1551 for i := 0; i < 2; i++ { 1552 select { 1553 case <-l.ch: 1554 // OK 1555 case <-time.After(200 * time.Millisecond): 1556 // We are likely to detect from each A and C servers, 1557 // but consider a failure if we did not receive any. 1558 if i == 0 { 1559 t.Fatalf("Should have detected loop") 1560 } 1561 } 1562 } 1563 1564 // The reconnect attempt is set to 5ms, but the default loop delay 1565 // is 30 seconds, so we should not get any new error for that long. 1566 // Check if we are getting more errors.. 1567 select { 1568 case e := <-l.ch: 1569 t.Fatalf("Should not have gotten another error, got %q", e) 1570 case <-time.After(50 * time.Millisecond): 1571 // OK! 1572 } 1573 } 1574 1575 func TestLeafNodeHubWithGateways(t *testing.T) { 1576 ao := DefaultOptions() 1577 ao.ServerName = "A" 1578 ao.LeafNode.Host = "127.0.0.1" 1579 ao.LeafNode.Port = -1 1580 a := RunServer(ao) 1581 defer a.Shutdown() 1582 1583 ua, _ := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", ao.LeafNode.Port)) 1584 1585 bo := testDefaultOptionsForGateway("B") 1586 bo.ServerName = "B" 1587 bo.Accounts = []*Account{NewAccount("SYS")} 1588 bo.SystemAccount = "SYS" 1589 bo.LeafNode.ReconnectInterval = 5 * time.Millisecond 1590 bo.LeafNode.Remotes = []*RemoteLeafOpts{ 1591 { 1592 URLs: []*url.URL{ua}, 1593 Hub: true, 1594 }, 1595 } 1596 b := RunServer(bo) 1597 defer b.Shutdown() 1598 1599 do := DefaultOptions() 1600 do.ServerName = "D" 1601 do.LeafNode.Host = "127.0.0.1" 1602 do.LeafNode.Port = -1 1603 d := RunServer(do) 1604 defer d.Shutdown() 1605 1606 ud, _ := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", do.LeafNode.Port)) 1607 1608 co := testGatewayOptionsFromToWithServers(t, "C", "B", b) 1609 co.ServerName = "C" 1610 co.Accounts = []*Account{NewAccount("SYS")} 1611 co.SystemAccount = "SYS" 1612 co.LeafNode.ReconnectInterval = 5 * time.Millisecond 1613 co.LeafNode.Remotes = []*RemoteLeafOpts{ 1614 { 1615 URLs: []*url.URL{ud}, 1616 Hub: true, 1617 }, 1618 } 1619 c := RunServer(co) 1620 defer c.Shutdown() 1621 1622 waitForInboundGateways(t, b, 1, 2*time.Second) 1623 waitForInboundGateways(t, c, 1, 2*time.Second) 1624 checkLeafNodeConnected(t, a) 1625 checkLeafNodeConnected(t, d) 1626 1627 // Create a responder on D 1628 ncD := natsConnect(t, d.ClientURL()) 1629 defer ncD.Close() 1630 1631 ncD.Subscribe("service", func(m *nats.Msg) { 1632 m.Respond([]byte("reply")) 1633 }) 1634 ncD.Flush() 1635 1636 checkFor(t, time.Second, 15*time.Millisecond, func() error { 1637 acc := a.globalAccount() 1638 if r := acc.sl.Match("service"); r != nil && len(r.psubs) == 1 { 1639 return nil 1640 } 1641 return fmt.Errorf("subscription still not registered") 1642 }) 1643 1644 // Create requestor on A and send the request, expect a reply. 1645 ncA := natsConnect(t, a.ClientURL()) 1646 defer ncA.Close() 1647 if msg, err := ncA.Request("service", []byte("request"), time.Second); err != nil { 1648 t.Fatalf("Failed to get reply: %v", err) 1649 } else if string(msg.Data) != "reply" { 1650 t.Fatalf("Unexpected reply: %q", msg.Data) 1651 } 1652 } 1653 1654 func TestLeafNodeTmpClients(t *testing.T) { 1655 ao := DefaultOptions() 1656 ao.LeafNode.Host = "127.0.0.1" 1657 ao.LeafNode.Port = -1 1658 a := RunServer(ao) 1659 defer a.Shutdown() 1660 1661 c, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", ao.LeafNode.Port)) 1662 if err != nil { 1663 t.Fatalf("Error connecting: %v", err) 1664 } 1665 defer c.Close() 1666 // Read info 1667 br := bufio.NewReader(c) 1668 br.ReadLine() 1669 1670 checkTmp := func(expected int) { 1671 t.Helper() 1672 checkFor(t, time.Second, 15*time.Millisecond, func() error { 1673 a.grMu.Lock() 1674 l := len(a.grTmpClients) 1675 a.grMu.Unlock() 1676 if l != expected { 1677 return fmt.Errorf("Expected tmp map to have %v entries, got %v", expected, l) 1678 } 1679 return nil 1680 }) 1681 } 1682 checkTmp(1) 1683 1684 // Close client and wait check that it is removed. 1685 c.Close() 1686 checkTmp(0) 1687 1688 // Check with normal leafnode connection that once connected, 1689 // the tmp map is also emptied. 1690 bo := DefaultOptions() 1691 bo.Cluster.Name = "xyz" 1692 bo.LeafNode.ReconnectInterval = 5 * time.Millisecond 1693 u, err := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", ao.LeafNode.Port)) 1694 if err != nil { 1695 t.Fatalf("Error creating url: %v", err) 1696 } 1697 bo.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{u}}} 1698 b := RunServer(bo) 1699 defer b.Shutdown() 1700 1701 checkLeafNodeConnected(t, b) 1702 checkTmp(0) 1703 } 1704 1705 func TestLeafNodeTLSVerifyAndMap(t *testing.T) { 1706 accName := "MyAccount" 1707 acc := NewAccount(accName) 1708 certUserName := "CN=example.com,OU=NATS.io" 1709 users := []*User{{Username: certUserName, Account: acc}} 1710 1711 for _, test := range []struct { 1712 name string 1713 leafUsers bool 1714 provideCert bool 1715 }{ 1716 {"no users override, provides cert", false, true}, 1717 {"no users override, does not provide cert", false, false}, 1718 {"users override, provides cert", true, true}, 1719 {"users override, does not provide cert", true, false}, 1720 } { 1721 t.Run(test.name, func(t *testing.T) { 1722 o := DefaultOptions() 1723 o.Accounts = []*Account{acc} 1724 o.LeafNode.Host = "127.0.0.1" 1725 o.LeafNode.Port = -1 1726 if test.leafUsers { 1727 o.LeafNode.Users = users 1728 } else { 1729 o.Users = users 1730 } 1731 tc := &TLSConfigOpts{ 1732 CertFile: "../test/configs/certs/tlsauth/server.pem", 1733 KeyFile: "../test/configs/certs/tlsauth/server-key.pem", 1734 CaFile: "../test/configs/certs/tlsauth/ca.pem", 1735 Verify: true, 1736 } 1737 tlsc, err := GenTLSConfig(tc) 1738 if err != nil { 1739 t.Fatalf("Error creating tls config: %v", err) 1740 } 1741 o.LeafNode.TLSConfig = tlsc 1742 o.LeafNode.TLSMap = true 1743 s := RunServer(o) 1744 defer s.Shutdown() 1745 1746 slo := DefaultOptions() 1747 slo.Cluster.Name = "xyz" 1748 1749 sltlsc := &tls.Config{} 1750 if test.provideCert { 1751 tc := &TLSConfigOpts{ 1752 CertFile: "../test/configs/certs/tlsauth/client.pem", 1753 KeyFile: "../test/configs/certs/tlsauth/client-key.pem", 1754 } 1755 var err error 1756 sltlsc, err = GenTLSConfig(tc) 1757 if err != nil { 1758 t.Fatalf("Error generating tls config: %v", err) 1759 } 1760 } 1761 sltlsc.InsecureSkipVerify = true 1762 u, _ := url.Parse(fmt.Sprintf("nats://%s:%d", o.LeafNode.Host, o.LeafNode.Port)) 1763 slo.LeafNode.Remotes = []*RemoteLeafOpts{ 1764 { 1765 TLSConfig: sltlsc, 1766 URLs: []*url.URL{u}, 1767 }, 1768 } 1769 sl := RunServer(slo) 1770 defer sl.Shutdown() 1771 1772 if !test.provideCert { 1773 // Wait a bit and make sure we are not connecting 1774 time.Sleep(100 * time.Millisecond) 1775 checkLeafNodeConnectedCount(t, s, 0) 1776 return 1777 } 1778 checkLeafNodeConnected(t, s) 1779 1780 var uname string 1781 var accname string 1782 s.mu.Lock() 1783 for _, c := range s.leafs { 1784 c.mu.Lock() 1785 uname = c.opts.Username 1786 if c.acc != nil { 1787 accname = c.acc.GetName() 1788 } 1789 c.mu.Unlock() 1790 } 1791 s.mu.Unlock() 1792 if uname != certUserName { 1793 t.Fatalf("Expected username %q, got %q", certUserName, uname) 1794 } 1795 if accname != accName { 1796 t.Fatalf("Expected account %q, got %v", accName, accname) 1797 } 1798 }) 1799 } 1800 } 1801 1802 type chanLogger struct { 1803 DummyLogger 1804 triggerChan chan string 1805 } 1806 1807 func (l *chanLogger) Warnf(format string, v ...any) { 1808 l.triggerChan <- fmt.Sprintf(format, v...) 1809 } 1810 1811 func (l *chanLogger) Errorf(format string, v ...any) { 1812 l.triggerChan <- fmt.Sprintf(format, v...) 1813 } 1814 1815 const ( 1816 testLeafNodeTLSVerifyAndMapSrvA = ` 1817 listen: 127.0.0.1:-1 1818 leaf { 1819 listen: "127.0.0.1:-1" 1820 tls { 1821 cert_file: "../test/configs/certs/server-cert.pem" 1822 key_file: "../test/configs/certs/server-key.pem" 1823 ca_file: "../test/configs/certs/ca.pem" 1824 timeout: 2 1825 verify_and_map: true 1826 } 1827 authorization { 1828 users [{ 1829 user: "%s" 1830 }] 1831 } 1832 } 1833 ` 1834 testLeafNodeTLSVerifyAndMapSrvB = ` 1835 listen: -1 1836 leaf { 1837 remotes [ 1838 { 1839 url: "tls://user-provided-in-url@localhost:%d" 1840 tls { 1841 cert_file: "../test/configs/certs/server-cert.pem" 1842 key_file: "../test/configs/certs/server-key.pem" 1843 ca_file: "../test/configs/certs/ca.pem" 1844 } 1845 } 1846 ] 1847 }` 1848 ) 1849 1850 func TestLeafNodeTLSVerifyAndMapCfgPass(t *testing.T) { 1851 l := &chanLogger{triggerChan: make(chan string, 100)} 1852 defer close(l.triggerChan) 1853 1854 confA := createConfFile(t, []byte(fmt.Sprintf(testLeafNodeTLSVerifyAndMapSrvA, "localhost"))) 1855 srvA, optsA := RunServerWithConfig(confA) 1856 defer srvA.Shutdown() 1857 srvA.SetLogger(l, true, true) 1858 1859 confB := createConfFile(t, []byte(fmt.Sprintf(testLeafNodeTLSVerifyAndMapSrvB, optsA.LeafNode.Port))) 1860 ob := LoadConfig(confB) 1861 ob.LeafNode.ReconnectInterval = 50 * time.Millisecond 1862 srvB := RunServer(ob) 1863 defer srvB.Shutdown() 1864 1865 // Now make sure that the leaf node connection is up and the correct account was picked 1866 checkFor(t, 10*time.Second, 10*time.Millisecond, func() error { 1867 for _, srv := range []*Server{srvA, srvB} { 1868 if nln := srv.NumLeafNodes(); nln != 1 { 1869 return fmt.Errorf("Number of leaf nodes is %d", nln) 1870 } 1871 if leafz, err := srv.Leafz(nil); err != nil { 1872 if len(leafz.Leafs) != 1 { 1873 return fmt.Errorf("Number of leaf nodes returned by LEAFZ is not one: %d", len(leafz.Leafs)) 1874 } else if leafz.Leafs[0].Account != DEFAULT_GLOBAL_ACCOUNT { 1875 return fmt.Errorf("Account used is not $G: %s", leafz.Leafs[0].Account) 1876 } 1877 } 1878 } 1879 return nil 1880 }) 1881 // Make sure that the user name in the url was ignored and a warning printed 1882 for { 1883 select { 1884 case w := <-l.triggerChan: 1885 if w == `User "user-provided-in-url" found in connect proto, but user required from cert` { 1886 return 1887 } 1888 case <-time.After(2 * time.Second): 1889 t.Fatal("Did not get expected warning") 1890 } 1891 } 1892 } 1893 1894 func TestLeafNodeTLSVerifyAndMapCfgFail(t *testing.T) { 1895 l := &chanLogger{triggerChan: make(chan string, 100)} 1896 defer close(l.triggerChan) 1897 1898 // use certificate with SAN localhost, but configure the server to not accept it 1899 // instead provide a name matching the user (to be matched by failed 1900 confA := createConfFile(t, []byte(fmt.Sprintf(testLeafNodeTLSVerifyAndMapSrvA, "user-provided-in-url"))) 1901 srvA, optsA := RunServerWithConfig(confA) 1902 defer srvA.Shutdown() 1903 srvA.SetLogger(l, true, true) 1904 1905 confB := createConfFile(t, []byte(fmt.Sprintf(testLeafNodeTLSVerifyAndMapSrvB, optsA.LeafNode.Port))) 1906 ob := LoadConfig(confB) 1907 ob.LeafNode.ReconnectInterval = 50 * time.Millisecond 1908 srvB := RunServer(ob) 1909 defer srvB.Shutdown() 1910 1911 // Now make sure that the leaf node connection is down 1912 checkFor(t, 10*time.Second, 10*time.Millisecond, func() error { 1913 for _, srv := range []*Server{srvA, srvB} { 1914 if nln := srv.NumLeafNodes(); nln != 0 { 1915 return fmt.Errorf("Number of leaf nodes is %d", nln) 1916 } 1917 } 1918 return nil 1919 }) 1920 // Make sure that the connection was closed for the right reason 1921 for { 1922 select { 1923 case w := <-l.triggerChan: 1924 if strings.Contains(w, ErrAuthentication.Error()) { 1925 return 1926 } 1927 case <-time.After(2 * time.Second): 1928 t.Fatal("Did not get expected warning") 1929 } 1930 } 1931 } 1932 1933 func TestLeafNodeOriginClusterInfo(t *testing.T) { 1934 hopts := DefaultOptions() 1935 hopts.ServerName = "hub" 1936 hopts.LeafNode.Port = -1 1937 1938 hub := RunServer(hopts) 1939 defer hub.Shutdown() 1940 1941 conf := createConfFile(t, []byte(fmt.Sprintf(` 1942 port: -1 1943 leaf { 1944 remotes [ { url: "nats://127.0.0.1:%d" } ] 1945 } 1946 `, hopts.LeafNode.Port))) 1947 1948 opts, err := ProcessConfigFile(conf) 1949 if err != nil { 1950 t.Fatalf("Error processing config file: %v", err) 1951 } 1952 opts.NoLog, opts.NoSigs = true, true 1953 1954 s := RunServer(opts) 1955 defer s.Shutdown() 1956 1957 checkLeafNodeConnected(t, s) 1958 1959 // Check the info on the leadnode client in the hub. 1960 grabLeaf := func() *client { 1961 var l *client 1962 hub.mu.Lock() 1963 for _, l = range hub.leafs { 1964 break 1965 } 1966 hub.mu.Unlock() 1967 return l 1968 } 1969 1970 l := grabLeaf() 1971 if rc := l.remoteCluster(); rc != "" { 1972 t.Fatalf("Expected an empty remote cluster, got %q", rc) 1973 } 1974 1975 s.Shutdown() 1976 1977 // Now make our leafnode part of a cluster. 1978 conf = createConfFile(t, []byte(fmt.Sprintf(` 1979 port: -1 1980 leaf { 1981 remotes [ { url: "nats://127.0.0.1:%d" } ] 1982 } 1983 cluster { 1984 name: "xyz" 1985 listen: "127.0.0.1:-1" 1986 } 1987 `, hopts.LeafNode.Port))) 1988 1989 opts, err = ProcessConfigFile(conf) 1990 if err != nil { 1991 t.Fatalf("Error processing config file: %v", err) 1992 } 1993 opts.NoLog, opts.NoSigs = true, true 1994 1995 s = RunServer(opts) 1996 defer s.Shutdown() 1997 1998 checkLeafNodeConnected(t, s) 1999 2000 l = grabLeaf() 2001 if rc := l.remoteCluster(); rc != "xyz" { 2002 t.Fatalf("Expected a remote cluster name of \"xyz\", got %q", rc) 2003 } 2004 pcid := l.cid 2005 2006 // Now make sure that if we update our cluster name, simulating the settling 2007 // of dynamic cluster names between competing servers. 2008 s.setClusterName("xyz") 2009 // Make sure we disconnect and reconnect. 2010 checkLeafNodeConnectedCount(t, s, 0) 2011 checkLeafNodeConnected(t, s) 2012 checkLeafNodeConnected(t, hub) 2013 2014 l = grabLeaf() 2015 if rc := l.remoteCluster(); rc != "xyz" { 2016 t.Fatalf("Expected a remote cluster name of \"xyz\", got %q", rc) 2017 } 2018 // Make sure we reconnected and have a new CID. 2019 if l.cid == pcid { 2020 t.Fatalf("Expected a different id, got the same") 2021 } 2022 } 2023 2024 type proxyAcceptDetectFailureLate struct { 2025 sync.Mutex 2026 wg sync.WaitGroup 2027 acceptPort int 2028 l net.Listener 2029 srvs []net.Conn 2030 leaf net.Conn 2031 startChan chan struct{} 2032 } 2033 2034 func (p *proxyAcceptDetectFailureLate) run(t *testing.T) int { 2035 return p.runEx(t, false) 2036 } 2037 2038 func (p *proxyAcceptDetectFailureLate) runEx(t *testing.T, needStart bool) int { 2039 l, err := natsListen("tcp", "127.0.0.1:0") 2040 if err != nil { 2041 t.Fatalf("Error on listen: %v", err) 2042 } 2043 p.Lock() 2044 var startChan chan struct{} 2045 if needStart { 2046 startChan = make(chan struct{}) 2047 p.startChan = startChan 2048 } 2049 p.l = l 2050 p.Unlock() 2051 port := l.Addr().(*net.TCPAddr).Port 2052 p.wg.Add(1) 2053 go func() { 2054 defer p.wg.Done() 2055 defer l.Close() 2056 defer func() { 2057 p.Lock() 2058 for _, c := range p.srvs { 2059 c.Close() 2060 } 2061 p.Unlock() 2062 }() 2063 if startChan != nil { 2064 <-startChan 2065 } 2066 for { 2067 c, err := l.Accept() 2068 if err != nil { 2069 return 2070 } 2071 srv, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", p.acceptPort)) 2072 if err != nil { 2073 return 2074 } 2075 p.Lock() 2076 p.leaf = c 2077 p.srvs = append(p.srvs, srv) 2078 p.Unlock() 2079 2080 transfer := func(c1, c2 net.Conn) { 2081 var buf [1024]byte 2082 for { 2083 n, err := c1.Read(buf[:]) 2084 if err != nil { 2085 return 2086 } 2087 if _, err := c2.Write(buf[:n]); err != nil { 2088 return 2089 } 2090 } 2091 } 2092 2093 go transfer(srv, c) 2094 go transfer(c, srv) 2095 } 2096 }() 2097 return port 2098 } 2099 2100 func (p *proxyAcceptDetectFailureLate) start() { 2101 p.Lock() 2102 if p.startChan != nil { 2103 close(p.startChan) 2104 p.startChan = nil 2105 } 2106 p.Unlock() 2107 } 2108 2109 func (p *proxyAcceptDetectFailureLate) close() { 2110 p.Lock() 2111 if p.startChan != nil { 2112 close(p.startChan) 2113 p.startChan = nil 2114 } 2115 p.l.Close() 2116 p.Unlock() 2117 2118 p.wg.Wait() 2119 } 2120 2121 type oldConnReplacedLogger struct { 2122 DummyLogger 2123 errCh chan string 2124 warnCh chan string 2125 } 2126 2127 func (l *oldConnReplacedLogger) Errorf(format string, v ...any) { 2128 select { 2129 case l.errCh <- fmt.Sprintf(format, v...): 2130 default: 2131 } 2132 } 2133 2134 func (l *oldConnReplacedLogger) Warnf(format string, v ...any) { 2135 select { 2136 case l.warnCh <- fmt.Sprintf(format, v...): 2137 default: 2138 } 2139 } 2140 2141 // This test will simulate that the accept side does not detect the connection 2142 // has been closed early enough. The soliciting side will attempt to reconnect 2143 // and we should not be getting the "loop detected" error. 2144 func TestLeafNodeLoopDetectedDueToReconnect(t *testing.T) { 2145 o := DefaultOptions() 2146 o.LeafNode.Host = "127.0.0.1" 2147 o.LeafNode.Port = -1 2148 s := RunServer(o) 2149 defer s.Shutdown() 2150 2151 l := &oldConnReplacedLogger{errCh: make(chan string, 10), warnCh: make(chan string, 10)} 2152 s.SetLogger(l, false, false) 2153 2154 p := &proxyAcceptDetectFailureLate{acceptPort: o.LeafNode.Port} 2155 defer p.close() 2156 port := p.run(t) 2157 2158 aurl, err := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", port)) 2159 if err != nil { 2160 t.Fatalf("Error parsing url: %v", err) 2161 } 2162 ol := DefaultOptions() 2163 ol.Cluster.Name = "cde" 2164 ol.LeafNode.ReconnectInterval = 50 * time.Millisecond 2165 ol.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{aurl}}} 2166 sl := RunServer(ol) 2167 defer sl.Shutdown() 2168 2169 checkLeafNodeConnected(t, s) 2170 checkLeafNodeConnected(t, sl) 2171 2172 // Cause disconnect client side... 2173 p.Lock() 2174 p.leaf.Close() 2175 p.Unlock() 2176 2177 // Make sure we did not get the loop detected error 2178 select { 2179 case e := <-l.errCh: 2180 if strings.Contains(e, "Loop detected") { 2181 t.Fatalf("Loop detected: %v", e) 2182 } 2183 case <-time.After(250 * time.Millisecond): 2184 // We are ok 2185 } 2186 2187 // Now make sure we got the warning 2188 select { 2189 case w := <-l.warnCh: 2190 if !strings.Contains(w, "Replacing connection from same server") { 2191 t.Fatalf("Unexpected warning: %v", w) 2192 } 2193 case <-time.After(time.Second): 2194 t.Fatal("Did not get expected warning") 2195 } 2196 2197 checkLeafNodeConnected(t, s) 2198 checkLeafNodeConnected(t, sl) 2199 } 2200 2201 func TestLeafNodeTwoRemotesBindToSameHubAccount(t *testing.T) { 2202 opts := DefaultOptions() 2203 opts.LeafNode.Host = "127.0.0.1" 2204 opts.LeafNode.Port = -1 2205 s := RunServer(opts) 2206 defer s.Shutdown() 2207 2208 for _, test := range []struct { 2209 name string 2210 account string 2211 fail bool 2212 }{ 2213 {"different local accounts", "b", false}, 2214 {"same local accounts", "a", true}, 2215 } { 2216 t.Run(test.name, func(t *testing.T) { 2217 conf := ` 2218 listen: 127.0.0.1:-1 2219 cluster { name: ln22, listen: 127.0.0.1:-1 } 2220 accounts { 2221 a { users [ {user: a, password: a} ]} 2222 b { users [ {user: b, password: b} ]} 2223 } 2224 leafnodes { 2225 remotes = [ 2226 { 2227 url: nats-leaf://127.0.0.1:%[1]d 2228 account: a 2229 } 2230 { 2231 url: nats-leaf://127.0.0.1:%[1]d 2232 account: %s 2233 } 2234 ] 2235 } 2236 ` 2237 lconf := createConfFile(t, []byte(fmt.Sprintf(conf, opts.LeafNode.Port, test.account))) 2238 2239 lopts, err := ProcessConfigFile(lconf) 2240 if err != nil { 2241 t.Fatalf("Error loading config file: %v", err) 2242 } 2243 lopts.NoLog = false 2244 ln, err := NewServer(lopts) 2245 if err != nil { 2246 t.Fatalf("Error creating server: %v", err) 2247 } 2248 defer ln.Shutdown() 2249 l := &captureErrorLogger{errCh: make(chan string, 10)} 2250 ln.SetLogger(l, false, false) 2251 2252 wg := sync.WaitGroup{} 2253 wg.Add(1) 2254 go func() { 2255 defer wg.Done() 2256 ln.Start() 2257 }() 2258 2259 select { 2260 case err := <-l.errCh: 2261 if test.fail && !strings.Contains(err, DuplicateRemoteLeafnodeConnection.String()) { 2262 t.Fatalf("Did not get expected duplicate connection error: %v", err) 2263 } else if !test.fail && strings.Contains(err, DuplicateRemoteLeafnodeConnection.String()) { 2264 t.Fatalf("Incorrectly detected a duplicate connection: %v", err) 2265 } 2266 case <-time.After(250 * time.Millisecond): 2267 if test.fail { 2268 t.Fatal("Did not get expected error") 2269 } 2270 } 2271 ln.Shutdown() 2272 wg.Wait() 2273 }) 2274 } 2275 } 2276 2277 func TestLeafNodeNoDuplicateWithinCluster(t *testing.T) { 2278 // This set the cluster name to "abc" 2279 oSrv1 := DefaultOptions() 2280 oSrv1.ServerName = "srv1" 2281 oSrv1.LeafNode.Host = "127.0.0.1" 2282 oSrv1.LeafNode.Port = -1 2283 srv1 := RunServer(oSrv1) 2284 defer srv1.Shutdown() 2285 2286 u, err := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", oSrv1.LeafNode.Port)) 2287 if err != nil { 2288 t.Fatalf("Error parsing url: %v", err) 2289 } 2290 2291 oLeaf1 := DefaultOptions() 2292 oLeaf1.ServerName = "leaf1" 2293 oLeaf1.Cluster.Name = "xyz" 2294 oLeaf1.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{u}}} 2295 leaf1 := RunServer(oLeaf1) 2296 defer leaf1.Shutdown() 2297 2298 leaf1ClusterURL := fmt.Sprintf("nats://127.0.0.1:%d", oLeaf1.Cluster.Port) 2299 2300 oLeaf2 := DefaultOptions() 2301 oLeaf2.ServerName = "leaf2" 2302 oLeaf2.Cluster.Name = "xyz" 2303 oLeaf2.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{u}}} 2304 oLeaf2.Routes = RoutesFromStr(leaf1ClusterURL) 2305 leaf2 := RunServer(oLeaf2) 2306 defer leaf2.Shutdown() 2307 2308 checkClusterFormed(t, leaf1, leaf2) 2309 2310 checkLeafNodeConnectedCount(t, srv1, 2) 2311 checkLeafNodeConnected(t, leaf1) 2312 checkLeafNodeConnected(t, leaf2) 2313 2314 ncSrv1 := natsConnect(t, srv1.ClientURL()) 2315 defer ncSrv1.Close() 2316 natsQueueSub(t, ncSrv1, "foo", "queue", func(m *nats.Msg) { 2317 m.Respond([]byte("from srv1")) 2318 }) 2319 2320 ncLeaf1 := natsConnect(t, leaf1.ClientURL()) 2321 defer ncLeaf1.Close() 2322 natsQueueSub(t, ncLeaf1, "foo", "queue", func(m *nats.Msg) { 2323 m.Respond([]byte("from leaf1")) 2324 }) 2325 2326 ncLeaf2 := natsConnect(t, leaf2.ClientURL()) 2327 defer ncLeaf2.Close() 2328 2329 // Check that "foo" interest is available everywhere. 2330 // For this test, we want to make sure that the 2 queue subs are 2331 // registered on all servers, so we don't use checkSubInterest 2332 // which would simply return "true" if there is any interest on "foo". 2333 servers := []*Server{srv1, leaf1, leaf2} 2334 checkFor(t, time.Second, 15*time.Millisecond, func() error { 2335 for _, s := range servers { 2336 acc, err := s.LookupAccount(globalAccountName) 2337 if err != nil { 2338 return err 2339 } 2340 acc.mu.RLock() 2341 r := acc.sl.Match("foo") 2342 ok := len(r.qsubs) == 1 && len(r.qsubs[0]) == 2 2343 acc.mu.RUnlock() 2344 if !ok { 2345 return fmt.Errorf("interest not propagated on %q", s.Name()) 2346 } 2347 } 2348 return nil 2349 }) 2350 2351 // Send requests (from leaf2). For this test to make sure that 2352 // there is no duplicate, we want to make sure that we check for 2353 // multiple replies and that the reply subject subscription has 2354 // been propagated everywhere. 2355 sub := natsSubSync(t, ncLeaf2, "reply_subj") 2356 natsFlush(t, ncLeaf2) 2357 2358 // Here we have a single sub on "reply_subj" so using checkSubInterest is ok. 2359 checkSubInterest(t, srv1, globalAccountName, "reply_subj", time.Second) 2360 checkSubInterest(t, leaf1, globalAccountName, "reply_subj", time.Second) 2361 checkSubInterest(t, leaf2, globalAccountName, "reply_subj", time.Second) 2362 2363 for i := 0; i < 5; i++ { 2364 // Now send the request 2365 natsPubReq(t, ncLeaf2, "foo", sub.Subject, []byte("req")) 2366 // Check that we get the reply 2367 replyMsg := natsNexMsg(t, sub, time.Second) 2368 // But make sure we received only 1! 2369 if otherReply, _ := sub.NextMsg(100 * time.Millisecond); otherReply != nil { 2370 t.Fatalf("Received duplicate reply, first was %q, followed by %q", 2371 replyMsg.Data, otherReply.Data) 2372 } 2373 // We also should have preferred the queue sub that is in the leaf cluster. 2374 if string(replyMsg.Data) != "from leaf1" { 2375 t.Fatalf("Expected reply from leaf1, got %q", replyMsg.Data) 2376 } 2377 } 2378 } 2379 2380 func TestLeafNodeLMsgSplit(t *testing.T) { 2381 // This set the cluster name to "abc" 2382 oSrv1 := DefaultOptions() 2383 oSrv1.LeafNode.Host = "127.0.0.1" 2384 oSrv1.LeafNode.Port = -1 2385 srv1 := RunServer(oSrv1) 2386 defer srv1.Shutdown() 2387 2388 oSrv2 := DefaultOptions() 2389 oSrv2.LeafNode.Host = "127.0.0.1" 2390 oSrv2.LeafNode.Port = -1 2391 oSrv2.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", oSrv1.Cluster.Port)) 2392 srv2 := RunServer(oSrv2) 2393 defer srv2.Shutdown() 2394 2395 checkClusterFormed(t, srv1, srv2) 2396 2397 u1, err := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", oSrv1.LeafNode.Port)) 2398 if err != nil { 2399 t.Fatalf("Error parsing url: %v", err) 2400 } 2401 u2, err := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", oSrv2.LeafNode.Port)) 2402 if err != nil { 2403 t.Fatalf("Error parsing url: %v", err) 2404 } 2405 2406 oLeaf1 := DefaultOptions() 2407 oLeaf1.Cluster.Name = "xyz" 2408 oLeaf1.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{u1, u2}}} 2409 leaf1 := RunServer(oLeaf1) 2410 defer leaf1.Shutdown() 2411 2412 oLeaf2 := DefaultOptions() 2413 oLeaf2.Cluster.Name = "xyz" 2414 oLeaf2.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{u1, u2}}} 2415 oLeaf2.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", oLeaf1.Cluster.Port)) 2416 leaf2 := RunServer(oLeaf2) 2417 defer leaf2.Shutdown() 2418 2419 checkClusterFormed(t, leaf1, leaf2) 2420 2421 checkLeafNodeConnected(t, leaf1) 2422 checkLeafNodeConnected(t, leaf2) 2423 2424 ncSrv2 := natsConnect(t, srv2.ClientURL()) 2425 defer ncSrv2.Close() 2426 natsQueueSub(t, ncSrv2, "foo", "queue", func(m *nats.Msg) { 2427 m.Respond([]byte("from srv2")) 2428 }) 2429 2430 // Check that "foo" interest is available everywhere. 2431 checkSubInterest(t, srv1, globalAccountName, "foo", time.Second) 2432 checkSubInterest(t, srv2, globalAccountName, "foo", time.Second) 2433 checkSubInterest(t, leaf1, globalAccountName, "foo", time.Second) 2434 checkSubInterest(t, leaf2, globalAccountName, "foo", time.Second) 2435 2436 // Not required, but have a request payload that is more than 100 bytes 2437 reqPayload := make([]byte, 150) 2438 for i := 0; i < len(reqPayload); i++ { 2439 reqPayload[i] = byte((i % 26)) + 'A' 2440 } 2441 2442 // Send repeated requests (from scratch) from leaf-2: 2443 sendReq := func() { 2444 t.Helper() 2445 2446 ncLeaf2 := natsConnect(t, leaf2.ClientURL()) 2447 defer ncLeaf2.Close() 2448 2449 if _, err := ncLeaf2.Request("foo", reqPayload, time.Second); err != nil { 2450 t.Fatalf("Did not receive reply: %v", err) 2451 } 2452 } 2453 for i := 0; i < 100; i++ { 2454 sendReq() 2455 } 2456 } 2457 2458 type parseRouteLSUnsubLogger struct { 2459 DummyLogger 2460 gotTrace chan struct{} 2461 gotErr chan error 2462 } 2463 2464 func (l *parseRouteLSUnsubLogger) Errorf(format string, v ...any) { 2465 err := fmt.Errorf(format, v...) 2466 select { 2467 case l.gotErr <- err: 2468 default: 2469 } 2470 } 2471 2472 func (l *parseRouteLSUnsubLogger) Tracef(format string, v ...any) { 2473 trace := fmt.Sprintf(format, v...) 2474 if strings.Contains(trace, "LS- $G foo bar") { 2475 l.gotTrace <- struct{}{} 2476 } 2477 } 2478 2479 func TestLeafNodeRouteParseLSUnsub(t *testing.T) { 2480 // This set the cluster name to "abc" 2481 oSrv1 := DefaultOptions() 2482 oSrv1.LeafNode.Host = "127.0.0.1" 2483 oSrv1.LeafNode.Port = -1 2484 srv1 := RunServer(oSrv1) 2485 defer srv1.Shutdown() 2486 2487 l := &parseRouteLSUnsubLogger{gotTrace: make(chan struct{}, 1), gotErr: make(chan error, 1)} 2488 srv1.SetLogger(l, true, true) 2489 2490 oSrv2 := DefaultOptions() 2491 oSrv2.LeafNode.Host = "127.0.0.1" 2492 oSrv2.LeafNode.Port = -1 2493 oSrv2.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", oSrv1.Cluster.Port)) 2494 srv2 := RunServer(oSrv2) 2495 defer srv2.Shutdown() 2496 2497 checkClusterFormed(t, srv1, srv2) 2498 2499 u2, err := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", oSrv2.LeafNode.Port)) 2500 if err != nil { 2501 t.Fatalf("Error parsing url: %v", err) 2502 } 2503 2504 oLeaf2 := DefaultOptions() 2505 oLeaf2.Cluster.Name = "xyz" 2506 oLeaf2.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{u2}}} 2507 leaf2 := RunServer(oLeaf2) 2508 defer leaf2.Shutdown() 2509 2510 checkLeafNodeConnected(t, srv2) 2511 checkLeafNodeConnected(t, leaf2) 2512 2513 ncLeaf2 := natsConnect(t, leaf2.ClientURL()) 2514 defer ncLeaf2.Close() 2515 2516 sub := natsQueueSubSync(t, ncLeaf2, "foo", "bar") 2517 // The issue was with the unsubscribe of this queue subscription 2518 natsUnsub(t, sub) 2519 2520 // We should get the trace 2521 select { 2522 case <-l.gotTrace: 2523 // OK! 2524 case <-time.After(100 * time.Millisecond): 2525 t.Fatalf("Did not get LS- trace") 2526 } 2527 // And no error... 2528 select { 2529 case e := <-l.gotErr: 2530 t.Fatalf("There was an error on server 1: %q", e.Error()) 2531 case <-time.After(100 * time.Millisecond): 2532 // OK! 2533 } 2534 } 2535 2536 func TestLeafNodeOperatorBadCfg(t *testing.T) { 2537 sysAcc, err := nkeys.CreateAccount() 2538 require_NoError(t, err) 2539 sysAccPk, err := sysAcc.PublicKey() 2540 require_NoError(t, err) 2541 tmpDir := t.TempDir() 2542 2543 configTmpl := ` 2544 port: -1 2545 operator: %s 2546 system_account: %s 2547 resolver: { 2548 type: cache 2549 dir: '%s' 2550 } 2551 leafnodes: { 2552 %s 2553 } 2554 ` 2555 2556 cases := []struct { 2557 name string 2558 errorText string 2559 cfg string 2560 }{ 2561 { 2562 name: "Operator with Leafnode", 2563 errorText: "operator mode does not allow specifying users in leafnode config", 2564 cfg: ` 2565 port: -1 2566 authorization { 2567 users = [{user: "u", password: "p"}] 2568 }`, 2569 }, 2570 { 2571 name: "Operator with NKey", 2572 errorText: "operator mode and non account nkeys are incompatible", 2573 cfg: ` 2574 port: -1 2575 authorization { 2576 account: notankey 2577 }`, 2578 }, 2579 { 2580 name: "Operator remote account NKeys", 2581 errorText: "operator mode requires account nkeys in remotes. " + 2582 "Please add an `account` key to each remote in your `leafnodes` section, to assign it to an account. " + 2583 "Each account value should be a 56 character public key, starting with the letter 'A'", 2584 cfg: `remotes: [{url: u}]`, 2585 }, 2586 } 2587 for _, c := range cases { 2588 t.Run(c.name, func(t *testing.T) { 2589 2590 conf := createConfFile(t, []byte(fmt.Sprintf(configTmpl, ojwt, sysAccPk, tmpDir, c.cfg))) 2591 opts := LoadConfig(conf) 2592 s, err := NewServer(opts) 2593 if err == nil { 2594 s.Shutdown() 2595 t.Fatal("Expected an error") 2596 } 2597 // Since the server cannot be stopped, since it did not start, 2598 // let's manually close the account resolver to avoid leaking go routines. 2599 opts.AccountResolver.Close() 2600 if err.Error() != c.errorText { 2601 t.Fatalf("Expected error %q but got %q", c.errorText, err) 2602 } 2603 }) 2604 } 2605 } 2606 2607 func TestLeafNodeTLSConfigReload(t *testing.T) { 2608 template := ` 2609 listen: 127.0.0.1:-1 2610 leaf { 2611 listen: "127.0.0.1:-1" 2612 tls { 2613 cert_file: "../test/configs/certs/server-cert.pem" 2614 key_file: "../test/configs/certs/server-key.pem" 2615 %s 2616 timeout: 2 2617 verify: true 2618 } 2619 } 2620 ` 2621 confA := createConfFile(t, []byte(fmt.Sprintf(template, ""))) 2622 2623 srvA, optsA := RunServerWithConfig(confA) 2624 defer srvA.Shutdown() 2625 2626 lg := &captureErrorLogger{errCh: make(chan string, 10)} 2627 srvA.SetLogger(lg, false, false) 2628 2629 confB := createConfFile(t, []byte(fmt.Sprintf(` 2630 listen: -1 2631 leaf { 2632 remotes [ 2633 { 2634 url: "tls://127.0.0.1:%d" 2635 tls { 2636 cert_file: "../test/configs/certs/server-cert.pem" 2637 key_file: "../test/configs/certs/server-key.pem" 2638 ca_file: "../test/configs/certs/ca.pem" 2639 } 2640 } 2641 ] 2642 } 2643 `, optsA.LeafNode.Port))) 2644 2645 optsB, err := ProcessConfigFile(confB) 2646 if err != nil { 2647 t.Fatalf("Error processing config file: %v", err) 2648 } 2649 optsB.LeafNode.ReconnectInterval = 50 * time.Millisecond 2650 optsB.NoLog, optsB.NoSigs = true, true 2651 2652 srvB := RunServer(optsB) 2653 defer srvB.Shutdown() 2654 2655 // Wait for the error 2656 select { 2657 case err := <-lg.errCh: 2658 // Since Go 1.18, we had to regenerate certs to not have to use GODEBUG="x509sha1=1" 2659 // But on macOS, with our test CA certs, no SCTs included, it will fail 2660 // for the reason "x509: “localhost” certificate is not standards compliant" 2661 // instead of "unknown authority". 2662 if !strings.Contains(err, "unknown") && !strings.Contains(err, "compliant") { 2663 t.Fatalf("Unexpected error: %v", err) 2664 } 2665 case <-time.After(2 * time.Second): 2666 t.Fatalf("Did not get TLS error") 2667 } 2668 2669 // Add the CA to srvA 2670 reloadUpdateConfig(t, srvA, confA, fmt.Sprintf(template, `ca_file: "../test/configs/certs/ca.pem"`)) 2671 2672 // Now make sure that srvB can create a LN connection. 2673 checkFor(t, 3*time.Second, 10*time.Millisecond, func() error { 2674 if nln := srvB.NumLeafNodes(); nln != 1 { 2675 return fmt.Errorf("Number of leaf nodes is %d", nln) 2676 } 2677 return nil 2678 }) 2679 } 2680 2681 func TestLeafNodeTLSConfigReloadForRemote(t *testing.T) { 2682 confA := createConfFile(t, []byte(` 2683 listen: 127.0.0.1:-1 2684 leaf { 2685 listen: "127.0.0.1:-1" 2686 tls { 2687 cert_file: "../test/configs/certs/server-cert.pem" 2688 key_file: "../test/configs/certs/server-key.pem" 2689 ca_file: "../test/configs/certs/ca.pem" 2690 timeout: 2 2691 verify: true 2692 } 2693 } 2694 `)) 2695 2696 srvA, optsA := RunServerWithConfig(confA) 2697 defer srvA.Shutdown() 2698 2699 lg := &captureErrorLogger{errCh: make(chan string, 10)} 2700 srvA.SetLogger(lg, false, false) 2701 2702 template := ` 2703 listen: -1 2704 leaf { 2705 remotes [ 2706 { 2707 url: "tls://127.0.0.1:%d" 2708 tls { 2709 cert_file: "../test/configs/certs/server-cert.pem" 2710 key_file: "../test/configs/certs/server-key.pem" 2711 %s 2712 } 2713 } 2714 ] 2715 } 2716 ` 2717 confB := createConfFile(t, []byte(fmt.Sprintf(template, optsA.LeafNode.Port, ""))) 2718 2719 srvB, _ := RunServerWithConfig(confB) 2720 defer srvB.Shutdown() 2721 2722 // Wait for the error 2723 select { 2724 case err := <-lg.errCh: 2725 if !strings.Contains(err, "bad certificate") { 2726 t.Fatalf("Unexpected error: %v", err) 2727 } 2728 case <-time.After(2 * time.Second): 2729 t.Fatalf("Did not get TLS error") 2730 } 2731 2732 // Add the CA to srvB 2733 reloadUpdateConfig(t, srvB, confB, fmt.Sprintf(template, optsA.LeafNode.Port, `ca_file: "../test/configs/certs/ca.pem"`)) 2734 2735 // Now make sure that srvB can create a LN connection. 2736 checkFor(t, 2*time.Second, 10*time.Millisecond, func() error { 2737 if nln := srvB.NumLeafNodes(); nln != 1 { 2738 return fmt.Errorf("Number of leaf nodes is %d", nln) 2739 } 2740 return nil 2741 }) 2742 } 2743 2744 func testDefaultLeafNodeWSOptions() *Options { 2745 o := DefaultOptions() 2746 o.Websocket.Host = "127.0.0.1" 2747 o.Websocket.Port = -1 2748 o.Websocket.NoTLS = true 2749 o.LeafNode.Host = "127.0.0.1" 2750 o.LeafNode.Port = -1 2751 return o 2752 } 2753 2754 func testDefaultRemoteLeafNodeWSOptions(t *testing.T, o *Options, tls bool) *Options { 2755 // Use some path in the URL.. we don't use that, but internally 2756 // the server will prefix the path with /leafnode so that the 2757 // WS webserver knows that it needs to create a LEAF connection. 2758 u, _ := url.Parse(fmt.Sprintf("ws://127.0.0.1:%d/some/path", o.Websocket.Port)) 2759 lo := DefaultOptions() 2760 lo.Cluster.Name = "LN" 2761 remote := &RemoteLeafOpts{URLs: []*url.URL{u}} 2762 if tls { 2763 tc := &TLSConfigOpts{ 2764 CertFile: "../test/configs/certs/server-cert.pem", 2765 KeyFile: "../test/configs/certs/server-key.pem", 2766 CaFile: "../test/configs/certs/ca.pem", 2767 } 2768 tlsConf, err := GenTLSConfig(tc) 2769 if err != nil { 2770 t.Fatalf("Error generating TLS config: %v", err) 2771 } 2772 // GenTLSConfig sets the CA in ClientCAs, but since here we act 2773 // as a client, set RootCAs... 2774 tlsConf.RootCAs = tlsConf.ClientCAs 2775 remote.TLSConfig = tlsConf 2776 } 2777 lo.LeafNode.Remotes = []*RemoteLeafOpts{remote} 2778 return lo 2779 } 2780 2781 func TestLeafNodeWSMixURLs(t *testing.T) { 2782 for _, test := range []struct { 2783 name string 2784 urls []string 2785 }{ 2786 {"mix 1", []string{"nats://127.0.0.1:1234", "ws://127.0.0.1:5678", "wss://127.0.0.1:9012"}}, 2787 {"mix 2", []string{"ws://127.0.0.1:1234", "nats://127.0.0.1:5678", "wss://127.0.0.1:9012"}}, 2788 {"mix 3", []string{"wss://127.0.0.1:1234", "ws://127.0.0.1:5678", "nats://127.0.0.1:9012"}}, 2789 {"mix 4", []string{"ws://127.0.0.1:1234", "nats://127.0.0.1:9012"}}, 2790 {"mix 5", []string{"nats://127.0.0.1:1234", "ws://127.0.0.1:9012"}}, 2791 {"mix 6", []string{"wss://127.0.0.1:1234", "nats://127.0.0.1:9012"}}, 2792 {"mix 7", []string{"nats://127.0.0.1:1234", "wss://127.0.0.1:9012"}}, 2793 } { 2794 t.Run(test.name, func(t *testing.T) { 2795 o := DefaultOptions() 2796 remote := &RemoteLeafOpts{} 2797 urls := make([]*url.URL, 0, 3) 2798 for _, ustr := range test.urls { 2799 u, err := url.Parse(ustr) 2800 if err != nil { 2801 t.Fatalf("Error parsing url: %v", err) 2802 } 2803 urls = append(urls, u) 2804 } 2805 remote.URLs = urls 2806 o.LeafNode.Remotes = []*RemoteLeafOpts{remote} 2807 s, err := NewServer(o) 2808 if err == nil || !strings.Contains(err.Error(), "mix") { 2809 if s != nil { 2810 s.Shutdown() 2811 } 2812 t.Fatalf("Unexpected error: %v", err) 2813 } 2814 }) 2815 } 2816 } 2817 2818 type testConnTrackSize struct { 2819 sync.Mutex 2820 net.Conn 2821 sz int 2822 } 2823 2824 func (c *testConnTrackSize) Write(p []byte) (int, error) { 2825 c.Lock() 2826 defer c.Unlock() 2827 n, err := c.Conn.Write(p) 2828 c.sz += n 2829 return n, err 2830 } 2831 2832 func TestLeafNodeWSBasic(t *testing.T) { 2833 for _, test := range []struct { 2834 name string 2835 masking bool 2836 tls bool 2837 acceptCompression bool 2838 remoteCompression bool 2839 }{ 2840 {"masking plain no compression", true, false, false, false}, 2841 {"masking plain compression", true, false, true, true}, 2842 {"masking plain compression disagree", true, false, false, true}, 2843 {"masking plain compression disagree 2", true, false, true, false}, 2844 {"masking tls no compression", true, true, false, false}, 2845 {"masking tls compression", true, true, true, true}, 2846 {"masking tls compression disagree", true, true, false, true}, 2847 {"masking tls compression disagree 2", true, true, true, false}, 2848 {"no masking plain no compression", false, false, false, false}, 2849 {"no masking plain compression", false, false, true, true}, 2850 {"no masking plain compression disagree", false, false, false, true}, 2851 {"no masking plain compression disagree 2", false, false, true, false}, 2852 {"no masking tls no compression", false, true, false, false}, 2853 {"no masking tls compression", false, true, true, true}, 2854 {"no masking tls compression disagree", false, true, false, true}, 2855 {"no masking tls compression disagree 2", false, true, true, false}, 2856 } { 2857 t.Run(test.name, func(t *testing.T) { 2858 o := testDefaultLeafNodeWSOptions() 2859 o.Websocket.NoTLS = !test.tls 2860 if test.tls { 2861 tc := &TLSConfigOpts{ 2862 CertFile: "../test/configs/certs/server-cert.pem", 2863 KeyFile: "../test/configs/certs/server-key.pem", 2864 CaFile: "../test/configs/certs/ca.pem", 2865 } 2866 tlsConf, err := GenTLSConfig(tc) 2867 if err != nil { 2868 t.Fatalf("Error generating TLS config: %v", err) 2869 } 2870 o.Websocket.TLSConfig = tlsConf 2871 } 2872 o.Websocket.Compression = test.acceptCompression 2873 s := RunServer(o) 2874 defer s.Shutdown() 2875 2876 lo := testDefaultRemoteLeafNodeWSOptions(t, o, test.tls) 2877 lo.LeafNode.Remotes[0].Websocket.Compression = test.remoteCompression 2878 lo.LeafNode.Remotes[0].Websocket.NoMasking = !test.masking 2879 ln := RunServer(lo) 2880 defer ln.Shutdown() 2881 2882 checkLeafNodeConnected(t, s) 2883 checkLeafNodeConnected(t, ln) 2884 2885 var trackSizeConn *testConnTrackSize 2886 if !test.tls { 2887 var cln *client 2888 ln.mu.Lock() 2889 for _, l := range ln.leafs { 2890 cln = l 2891 break 2892 } 2893 ln.mu.Unlock() 2894 cln.mu.Lock() 2895 trackSizeConn = &testConnTrackSize{Conn: cln.nc} 2896 cln.nc = trackSizeConn 2897 cln.mu.Unlock() 2898 } 2899 2900 nc1 := natsConnect(t, s.ClientURL()) 2901 defer nc1.Close() 2902 sub1 := natsSubSync(t, nc1, "foo") 2903 natsFlush(t, nc1) 2904 2905 checkSubInterest(t, ln, globalAccountName, "foo", time.Second) 2906 2907 nc2 := natsConnect(t, ln.ClientURL()) 2908 defer nc2.Close() 2909 msg1Payload := make([]byte, 2048) 2910 for i := 0; i < len(msg1Payload); i++ { 2911 msg1Payload[i] = 'A' 2912 } 2913 natsPub(t, nc2, "foo", msg1Payload) 2914 2915 msg := natsNexMsg(t, sub1, time.Second) 2916 if !bytes.Equal(msg.Data, msg1Payload) { 2917 t.Fatalf("Invalid message: %q", msg.Data) 2918 } 2919 2920 sub2 := natsSubSync(t, nc2, "bar") 2921 natsFlush(t, nc2) 2922 2923 checkSubInterest(t, s, globalAccountName, "bar", time.Second) 2924 2925 msg2Payload := make([]byte, 2048) 2926 for i := 0; i < len(msg2Payload); i++ { 2927 msg2Payload[i] = 'B' 2928 } 2929 natsPub(t, nc1, "bar", msg2Payload) 2930 2931 msg = natsNexMsg(t, sub2, time.Second) 2932 if !bytes.Equal(msg.Data, msg2Payload) { 2933 t.Fatalf("Invalid message: %q", msg.Data) 2934 } 2935 2936 if !test.tls { 2937 trackSizeConn.Lock() 2938 size := trackSizeConn.sz 2939 trackSizeConn.Unlock() 2940 2941 if test.acceptCompression && test.remoteCompression { 2942 if size >= 1024 { 2943 t.Fatalf("Seems that there was no compression: size=%v", size) 2944 } 2945 } else if size < 2048 { 2946 t.Fatalf("Seems compression was on while it should not: size=%v", size) 2947 } 2948 } 2949 }) 2950 } 2951 } 2952 2953 func TestLeafNodeWSRemoteCompressAndMaskingOptions(t *testing.T) { 2954 for _, test := range []struct { 2955 name string 2956 compress bool 2957 compStr string 2958 noMasking bool 2959 noMaskStr string 2960 }{ 2961 {"compression masking", true, "true", false, "false"}, 2962 {"compression no masking", true, "true", true, "true"}, 2963 {"no compression masking", false, "false", false, "false"}, 2964 {"no compression no masking", false, "false", true, "true"}, 2965 } { 2966 t.Run(test.name, func(t *testing.T) { 2967 conf := createConfFile(t, []byte(fmt.Sprintf(` 2968 port: -1 2969 leafnodes { 2970 remotes [ 2971 {url: "ws://127.0.0.1:1234", ws_compression: %s, ws_no_masking: %s} 2972 ] 2973 } 2974 `, test.compStr, test.noMaskStr))) 2975 o, err := ProcessConfigFile(conf) 2976 if err != nil { 2977 t.Fatalf("Error loading conf: %v", err) 2978 } 2979 if nr := len(o.LeafNode.Remotes); nr != 1 { 2980 t.Fatalf("Expected 1 remote, got %v", nr) 2981 } 2982 r := o.LeafNode.Remotes[0] 2983 if cur := r.Websocket.Compression; cur != test.compress { 2984 t.Fatalf("Expected compress to be %v, got %v", test.compress, cur) 2985 } 2986 if cur := r.Websocket.NoMasking; cur != test.noMasking { 2987 t.Fatalf("Expected ws_masking to be %v, got %v", test.noMasking, cur) 2988 } 2989 }) 2990 } 2991 } 2992 2993 func TestLeafNodeWSNoMaskingRejected(t *testing.T) { 2994 wsTestRejectNoMasking = true 2995 defer func() { wsTestRejectNoMasking = false }() 2996 2997 o := testDefaultLeafNodeWSOptions() 2998 s := RunServer(o) 2999 defer s.Shutdown() 3000 3001 lo := testDefaultRemoteLeafNodeWSOptions(t, o, false) 3002 lo.LeafNode.Remotes[0].Websocket.NoMasking = true 3003 ln := RunServer(lo) 3004 defer ln.Shutdown() 3005 3006 checkLeafNodeConnected(t, s) 3007 checkLeafNodeConnected(t, ln) 3008 3009 var cln *client 3010 ln.mu.Lock() 3011 for _, l := range ln.leafs { 3012 cln = l 3013 break 3014 } 3015 ln.mu.Unlock() 3016 3017 cln.mu.Lock() 3018 maskWrite := cln.ws.maskwrite 3019 cln.mu.Unlock() 3020 3021 if !maskWrite { 3022 t.Fatal("Leafnode remote connection should mask writes, it does not") 3023 } 3024 } 3025 3026 func TestLeafNodeWSSubPath(t *testing.T) { 3027 o := testDefaultLeafNodeWSOptions() 3028 s := RunServer(o) 3029 defer s.Shutdown() 3030 3031 lo := testDefaultRemoteLeafNodeWSOptions(t, o, false) 3032 ln := RunServer(lo) 3033 defer ln.Shutdown() 3034 3035 // Confirm that it can connect using the subpath. 3036 checkLeafNodeConnected(t, s) 3037 checkLeafNodeConnected(t, ln) 3038 3039 // Add another leafnode that tries to connect to the subpath 3040 // but intercept the attempt for the test. 3041 o2 := testDefaultLeafNodeWSOptions() 3042 lo2 := testDefaultRemoteLeafNodeWSOptions(t, o2, false) 3043 attempts := make(chan string, 2) 3044 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 3045 attempts <- r.URL.String() 3046 })) 3047 u, _ := url.Parse(fmt.Sprintf("%v/some/path", ts.URL)) 3048 u.Scheme = "ws" 3049 lo2.LeafNode.Remotes = []*RemoteLeafOpts{ 3050 { 3051 URLs: []*url.URL{u}, 3052 }, 3053 } 3054 ln2 := RunServer(lo2) 3055 defer ln2.Shutdown() 3056 3057 expected := "/some/path/leafnode" 3058 select { 3059 case got := <-attempts: 3060 if got != expected { 3061 t.Fatalf("Expected: %v, got: %v", expected, got) 3062 } 3063 case <-time.After(2 * time.Second): 3064 t.Fatal("Timed out waiting for leaf ws connect attempt") 3065 } 3066 } 3067 3068 func TestLeafNodeWSFailedConnection(t *testing.T) { 3069 o := testDefaultLeafNodeWSOptions() 3070 s := RunServer(o) 3071 defer s.Shutdown() 3072 3073 lo := testDefaultRemoteLeafNodeWSOptions(t, o, true) 3074 lo.LeafNode.ReconnectInterval = 100 * time.Millisecond 3075 ln := RunServer(lo) 3076 defer ln.Shutdown() 3077 3078 el := &captureErrorLogger{errCh: make(chan string, 100)} 3079 ln.SetLogger(el, false, false) 3080 3081 select { 3082 case err := <-el.errCh: 3083 if !strings.Contains(err, "handshake error") { 3084 t.Fatalf("Unexpected error: %v", err) 3085 } 3086 case <-time.After(time.Second): 3087 t.Fatal("No error reported!") 3088 } 3089 ln.Shutdown() 3090 s.Shutdown() 3091 3092 lst, err := natsListen("tcp", "127.0.0.1:0") 3093 if err != nil { 3094 t.Fatalf("Error starting listener: %v", err) 3095 } 3096 defer lst.Close() 3097 3098 wg := sync.WaitGroup{} 3099 wg.Add(2) 3100 3101 go func() { 3102 defer wg.Done() 3103 3104 for i := 0; i < 10; i++ { 3105 c, err := lst.Accept() 3106 if err != nil { 3107 return 3108 } 3109 time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 3110 if rand.Intn(2) == 1 { 3111 c.Write([]byte("something\r\n")) 3112 } 3113 c.Close() 3114 } 3115 }() 3116 3117 time.Sleep(100 * time.Millisecond) 3118 3119 port := lst.Addr().(*net.TCPAddr).Port 3120 u, _ := url.Parse(fmt.Sprintf("ws://127.0.0.1:%d", port)) 3121 lo = DefaultOptions() 3122 lo.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{u}}} 3123 lo.LeafNode.ReconnectInterval = 10 * time.Millisecond 3124 ln, _ = NewServer(lo) 3125 el = &captureErrorLogger{errCh: make(chan string, 100)} 3126 ln.SetLogger(el, false, false) 3127 3128 go func() { 3129 ln.Start() 3130 wg.Done() 3131 }() 3132 3133 timeout := time.NewTimer(time.Second) 3134 for i := 0; i < 10; i++ { 3135 select { 3136 case err := <-el.errCh: 3137 if !strings.Contains(err, "Error soliciting") { 3138 t.Fatalf("Unexpected error: %v", err) 3139 } 3140 case <-timeout.C: 3141 t.Fatal("No error reported!") 3142 } 3143 } 3144 ln.Shutdown() 3145 lst.Close() 3146 wg.Wait() 3147 } 3148 3149 func TestLeafNodeWSAuth(t *testing.T) { 3150 template := ` 3151 port: -1 3152 authorization { 3153 users [ 3154 {user: "user", pass: "puser", connection_types: ["%s"]} 3155 {user: "leaf", pass: "pleaf", connection_types: ["%s"%s]} 3156 ] 3157 } 3158 websocket { 3159 port: -1 3160 no_tls: true 3161 } 3162 leafnodes { 3163 port: -1 3164 } 3165 ` 3166 s, o, conf := runReloadServerWithContent(t, 3167 []byte(fmt.Sprintf(template, jwt.ConnectionTypeStandard, jwt.ConnectionTypeLeafnode, ""))) 3168 defer s.Shutdown() 3169 3170 l := &captureErrorLogger{errCh: make(chan string, 10)} 3171 s.SetLogger(l, false, false) 3172 3173 lo := testDefaultRemoteLeafNodeWSOptions(t, o, false) 3174 u, _ := url.Parse(fmt.Sprintf("ws://leaf:pleaf@127.0.0.1:%d", o.Websocket.Port)) 3175 remote := &RemoteLeafOpts{URLs: []*url.URL{u}} 3176 lo.LeafNode.Remotes = []*RemoteLeafOpts{remote} 3177 lo.LeafNode.ReconnectInterval = 50 * time.Millisecond 3178 ln := RunServer(lo) 3179 defer ln.Shutdown() 3180 3181 var lasterr string 3182 tm := time.NewTimer(2 * time.Second) 3183 for done := false; !done; { 3184 select { 3185 case lasterr = <-l.errCh: 3186 if strings.Contains(lasterr, "authentication") { 3187 done = true 3188 } 3189 case <-tm.C: 3190 t.Fatalf("Expected auth error, got %v", lasterr) 3191 } 3192 } 3193 3194 ws := fmt.Sprintf(`, "%s"`, jwt.ConnectionTypeLeafnodeWS) 3195 reloadUpdateConfig(t, s, conf, fmt.Sprintf(template, 3196 jwt.ConnectionTypeStandard, jwt.ConnectionTypeLeafnode, ws)) 3197 3198 checkLeafNodeConnected(t, s) 3199 checkLeafNodeConnected(t, ln) 3200 3201 nc1 := natsConnect(t, fmt.Sprintf("nats://user:puser@127.0.0.1:%d", o.Port)) 3202 defer nc1.Close() 3203 3204 sub := natsSubSync(t, nc1, "foo") 3205 natsFlush(t, nc1) 3206 3207 checkSubInterest(t, ln, globalAccountName, "foo", time.Second) 3208 3209 nc2 := natsConnect(t, ln.ClientURL()) 3210 defer nc2.Close() 3211 3212 natsPub(t, nc2, "foo", []byte("msg1")) 3213 msg := natsNexMsg(t, sub, time.Second) 3214 3215 if md := string(msg.Data); md != "msg1" { 3216 t.Fatalf("Invalid message: %q", md) 3217 } 3218 } 3219 3220 func TestLeafNodeWSGossip(t *testing.T) { 3221 o1 := testDefaultLeafNodeWSOptions() 3222 s1 := RunServer(o1) 3223 defer s1.Shutdown() 3224 3225 // Now connect from a server that knows only about s1 3226 lo := testDefaultRemoteLeafNodeWSOptions(t, o1, false) 3227 lo.LeafNode.ReconnectInterval = 15 * time.Millisecond 3228 ln := RunServer(lo) 3229 defer ln.Shutdown() 3230 3231 checkLeafNodeConnected(t, s1) 3232 checkLeafNodeConnected(t, ln) 3233 3234 // Now add a routed server to s1 3235 o2 := testDefaultLeafNodeWSOptions() 3236 o2.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", o1.Cluster.Port)) 3237 s2 := RunServer(o2) 3238 defer s2.Shutdown() 3239 3240 // Wait for cluster to form 3241 checkClusterFormed(t, s1, s2) 3242 3243 // Now shutdown s1 and check that ln is able to reconnect to s2. 3244 s1.Shutdown() 3245 3246 checkLeafNodeConnected(t, s2) 3247 checkLeafNodeConnected(t, ln) 3248 3249 // Make sure that the reconnection was as a WS connection, not simply to 3250 // the regular LN port. 3251 var s2lc *client 3252 s2.mu.Lock() 3253 for _, l := range s2.leafs { 3254 s2lc = l 3255 break 3256 } 3257 s2.mu.Unlock() 3258 3259 s2lc.mu.Lock() 3260 isWS := s2lc.isWebsocket() 3261 s2lc.mu.Unlock() 3262 3263 if !isWS { 3264 t.Fatal("Leafnode connection is not websocket!") 3265 } 3266 } 3267 3268 // This test was showing an issue if one set maxBufSize to very small value, 3269 // such as maxBufSize = 10. With such small value, we would get a corruption 3270 // in that LMSG would arrive with missing bytes. We are now always making 3271 // a copy when dealing with messages that are bigger than maxBufSize. 3272 func TestLeafNodeWSNoBufferCorruption(t *testing.T) { 3273 o := testDefaultLeafNodeWSOptions() 3274 s := RunServer(o) 3275 defer s.Shutdown() 3276 3277 lo1 := testDefaultRemoteLeafNodeWSOptions(t, o, false) 3278 lo1.LeafNode.ReconnectInterval = 15 * time.Millisecond 3279 ln1 := RunServer(lo1) 3280 defer ln1.Shutdown() 3281 3282 lo2 := DefaultOptions() 3283 lo2.Cluster.Name = "LN" 3284 lo2.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", lo1.Cluster.Port)) 3285 ln2 := RunServer(lo2) 3286 defer ln2.Shutdown() 3287 3288 checkClusterFormed(t, ln1, ln2) 3289 3290 checkLeafNodeConnected(t, s) 3291 checkLeafNodeConnected(t, ln1) 3292 3293 nc := natsConnect(t, s.ClientURL()) 3294 defer nc.Close() 3295 sub := natsSubSync(t, nc, "foo") 3296 3297 nc1 := natsConnect(t, ln1.ClientURL()) 3298 defer nc1.Close() 3299 3300 nc2 := natsConnect(t, ln2.ClientURL()) 3301 defer nc2.Close() 3302 sub2 := natsSubSync(t, nc2, "foo") 3303 3304 checkSubInterest(t, s, globalAccountName, "foo", time.Second) 3305 checkSubInterest(t, ln2, globalAccountName, "foo", time.Second) 3306 checkSubInterest(t, ln1, globalAccountName, "foo", time.Second) 3307 3308 payload := make([]byte, 100*1024) 3309 for i := 0; i < len(payload); i++ { 3310 payload[i] = 'A' 3311 } 3312 natsPub(t, nc1, "foo", payload) 3313 3314 checkMsgRcv := func(sub *nats.Subscription) { 3315 msg := natsNexMsg(t, sub, time.Second) 3316 if !bytes.Equal(msg.Data, payload) { 3317 t.Fatalf("Invalid message content: %q", msg.Data) 3318 } 3319 } 3320 checkMsgRcv(sub2) 3321 checkMsgRcv(sub) 3322 } 3323 3324 func TestLeafNodeWSRemoteNoTLSBlockWithWSSProto(t *testing.T) { 3325 o := testDefaultLeafNodeWSOptions() 3326 o.Websocket.NoTLS = false 3327 tc := &TLSConfigOpts{ 3328 CertFile: "../test/configs/certs/server-cert.pem", 3329 KeyFile: "../test/configs/certs/server-key.pem", 3330 CaFile: "../test/configs/certs/ca.pem", 3331 } 3332 tlsConf, err := GenTLSConfig(tc) 3333 if err != nil { 3334 t.Fatalf("Error generating TLS config: %v", err) 3335 } 3336 o.Websocket.TLSConfig = tlsConf 3337 s := RunServer(o) 3338 defer s.Shutdown() 3339 3340 // The test will make sure that if the protocol is "wss://", a TLS handshake must 3341 // be initiated, regardless of the presence of a TLS config block in config file 3342 // or here directly. 3343 // A bug was causing the absence of TLS config block to initiate a non TLS connection 3344 // even if "wss://" proto was specified, which would lead to "invalid websocket connection" 3345 // errors in the log. 3346 // With the fix, the connection will fail because the remote will fail to verify 3347 // the root CA, but at least, we will make sure that this is not an "invalid websocket connection" 3348 3349 u, _ := url.Parse(fmt.Sprintf("wss://127.0.0.1:%d/some/path", o.Websocket.Port)) 3350 lo := DefaultOptions() 3351 lo.Cluster.Name = "LN" 3352 remote := &RemoteLeafOpts{URLs: []*url.URL{u}} 3353 lo.LeafNode.Remotes = []*RemoteLeafOpts{remote} 3354 lo.LeafNode.ReconnectInterval = 100 * time.Millisecond 3355 ln := RunServer(lo) 3356 defer ln.Shutdown() 3357 3358 l := &captureErrorLogger{errCh: make(chan string, 10)} 3359 ln.SetLogger(l, false, false) 3360 3361 select { 3362 case e := <-l.errCh: 3363 if strings.Contains(e, "invalid websocket connection") { 3364 t.Fatalf("The remote did not try to create a TLS connection: %v", e) 3365 } 3366 // OK! 3367 return 3368 case <-time.After(2 * time.Second): 3369 t.Fatal("Connection should fail") 3370 } 3371 } 3372 3373 func TestLeafNodeWSNoAuthUser(t *testing.T) { 3374 conf := createConfFile(t, []byte(` 3375 port: -1 3376 accounts { 3377 A { users [ {user: a, password: a} ]} 3378 B { users [ {user: b, password: b} ]} 3379 } 3380 websocket { 3381 port: -1 3382 no_tls: true 3383 no_auth_user: a 3384 } 3385 leafnodes { 3386 port: -1 3387 } 3388 `)) 3389 s, o := RunServerWithConfig(conf) 3390 defer s.Shutdown() 3391 3392 nc1 := natsConnect(t, fmt.Sprintf("nats://a:a@127.0.0.1:%d", o.Port)) 3393 defer nc1.Close() 3394 3395 lconf := createConfFile(t, []byte(fmt.Sprintf(` 3396 port: -1 3397 accounts { 3398 A { users [ {user: a, password: a} ]} 3399 B { users [ {user: b, password: b} ]} 3400 } 3401 leafnodes { 3402 remotes [ 3403 { 3404 url: "ws://127.0.0.1:%d" 3405 account: A 3406 } 3407 ] 3408 } 3409 `, o.Websocket.Port))) 3410 3411 ln, lo := RunServerWithConfig(lconf) 3412 defer ln.Shutdown() 3413 3414 checkLeafNodeConnected(t, s) 3415 checkLeafNodeConnected(t, ln) 3416 3417 nc2 := natsConnect(t, fmt.Sprintf("nats://a:a@127.0.0.1:%d", lo.Port)) 3418 defer nc2.Close() 3419 3420 sub := natsSubSync(t, nc2, "foo") 3421 natsFlush(t, nc2) 3422 3423 checkSubInterest(t, s, "A", "foo", time.Second) 3424 3425 natsPub(t, nc1, "foo", []byte("msg1")) 3426 msg := natsNexMsg(t, sub, time.Second) 3427 3428 if md := string(msg.Data); md != "msg1" { 3429 t.Fatalf("Invalid message: %q", md) 3430 } 3431 } 3432 3433 func TestLeafNodeStreamImport(t *testing.T) { 3434 o1 := DefaultOptions() 3435 o1.LeafNode.Port = -1 3436 accA := NewAccount("A") 3437 o1.Accounts = []*Account{accA} 3438 o1.Users = []*User{{Username: "a", Password: "a", Account: accA}} 3439 o1.LeafNode.Account = "A" 3440 o1.NoAuthUser = "a" 3441 s1 := RunServer(o1) 3442 defer s1.Shutdown() 3443 3444 o2 := DefaultOptions() 3445 o2.LeafNode.Port = -1 3446 o2.Cluster.Name = "xyz" 3447 3448 accB := NewAccount("B") 3449 if err := accB.AddStreamExport(">", nil); err != nil { 3450 t.Fatalf("Error adding stream export: %v", err) 3451 } 3452 3453 accC := NewAccount("C") 3454 if err := accC.AddStreamImport(accB, ">", ""); err != nil { 3455 t.Fatalf("Error adding stream import: %v", err) 3456 } 3457 3458 o2.Accounts = []*Account{accB, accC} 3459 o2.Users = []*User{{Username: "b", Password: "b", Account: accB}, {Username: "c", Password: "c", Account: accC}} 3460 o2.NoAuthUser = "b" 3461 u, err := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", o1.LeafNode.Port)) 3462 if err != nil { 3463 t.Fatalf("Error parsing url: %v", err) 3464 } 3465 o2.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{u}, LocalAccount: "C"}} 3466 s2 := RunServer(o2) 3467 defer s2.Shutdown() 3468 3469 nc1 := natsConnect(t, s1.ClientURL()) 3470 defer nc1.Close() 3471 3472 sub := natsSubSync(t, nc1, "a") 3473 3474 checkSubInterest(t, s2, "C", "a", time.Second) 3475 3476 nc2 := natsConnect(t, s2.ClientURL()) 3477 defer nc2.Close() 3478 3479 natsPub(t, nc2, "a", []byte("hello?")) 3480 3481 natsNexMsg(t, sub, time.Second) 3482 } 3483 3484 func TestLeafNodeRouteSubWithOrigin(t *testing.T) { 3485 lo1 := DefaultOptions() 3486 lo1.LeafNode.Host = "127.0.0.1" 3487 lo1.LeafNode.Port = -1 3488 lo1.Cluster.Name = "local" 3489 lo1.Cluster.Host = "127.0.0.1" 3490 lo1.Cluster.Port = -1 3491 l1 := RunServer(lo1) 3492 defer l1.Shutdown() 3493 3494 lo2 := DefaultOptions() 3495 lo2.LeafNode.Host = "127.0.0.1" 3496 lo2.LeafNode.Port = -1 3497 lo2.Cluster.Name = "local" 3498 lo2.Cluster.Host = "127.0.0.1" 3499 lo2.Cluster.Port = -1 3500 lo2.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", lo1.Cluster.Port)) 3501 l2 := RunServer(lo2) 3502 defer l2.Shutdown() 3503 3504 checkClusterFormed(t, l1, l2) 3505 3506 u1, _ := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", lo1.LeafNode.Port)) 3507 urls := []*url.URL{u1} 3508 3509 ro1 := DefaultOptions() 3510 ro1.Cluster.Name = "remote" 3511 ro1.Cluster.Host = "127.0.0.1" 3512 ro1.Cluster.Port = -1 3513 ro1.LeafNode.ReconnectInterval = 50 * time.Millisecond 3514 ro1.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: urls}} 3515 r1 := RunServer(ro1) 3516 defer r1.Shutdown() 3517 3518 checkLeafNodeConnected(t, r1) 3519 3520 nc := natsConnect(t, r1.ClientURL(), nats.NoReconnect()) 3521 defer nc.Close() 3522 natsSubSync(t, nc, "foo") 3523 natsQueueSubSync(t, nc, "bar", "baz") 3524 checkSubInterest(t, l2, globalAccountName, "foo", time.Second) 3525 checkSubInterest(t, l2, globalAccountName, "bar", time.Second) 3526 3527 // Now shutdown the leafnode and check that any subscription for $G on l2 are gone. 3528 r1.Shutdown() 3529 checkFor(t, time.Second, 15*time.Millisecond, func() error { 3530 acc := l2.GlobalAccount() 3531 if n := acc.TotalSubs(); n != 5 { 3532 return fmt.Errorf("Account %q should have 5 subs, got %v", acc.GetName(), n) 3533 } 3534 return nil 3535 }) 3536 } 3537 3538 func TestLeafNodeLoopDetectionWithMultipleClusters(t *testing.T) { 3539 lo1 := DefaultOptions() 3540 lo1.LeafNode.Host = "127.0.0.1" 3541 lo1.LeafNode.Port = -1 3542 lo1.Cluster.Name = "local" 3543 lo1.Cluster.Host = "127.0.0.1" 3544 lo1.Cluster.Port = -1 3545 l1 := RunServer(lo1) 3546 defer l1.Shutdown() 3547 3548 lo2 := DefaultOptions() 3549 lo2.LeafNode.Host = "127.0.0.1" 3550 lo2.LeafNode.Port = -1 3551 lo2.Cluster.Name = "local" 3552 lo2.Cluster.Host = "127.0.0.1" 3553 lo2.Cluster.Port = -1 3554 lo2.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", lo1.Cluster.Port)) 3555 l2 := RunServer(lo2) 3556 defer l2.Shutdown() 3557 3558 checkClusterFormed(t, l1, l2) 3559 3560 ro1 := DefaultOptions() 3561 ro1.Cluster.Name = "remote" 3562 ro1.Cluster.Host = "127.0.0.1" 3563 ro1.Cluster.Port = -1 3564 ro1.LeafNode.ReconnectInterval = 50 * time.Millisecond 3565 ro1.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{ 3566 {Scheme: "nats", Host: fmt.Sprintf("127.0.0.1:%d", lo1.LeafNode.Port)}, 3567 {Scheme: "nats", Host: fmt.Sprintf("127.0.0.1:%d", lo2.LeafNode.Port)}, 3568 }}} 3569 r1 := RunServer(ro1) 3570 defer r1.Shutdown() 3571 3572 l := &captureErrorLogger{errCh: make(chan string, 100)} 3573 r1.SetLogger(l, false, false) 3574 3575 ro2 := DefaultOptions() 3576 ro2.Cluster.Name = "remote" 3577 ro2.Cluster.Host = "127.0.0.1" 3578 ro2.Cluster.Port = -1 3579 ro2.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", ro1.Cluster.Port)) 3580 ro2.LeafNode.ReconnectInterval = 50 * time.Millisecond 3581 ro2.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{ 3582 {Scheme: "nats", Host: fmt.Sprintf("127.0.0.1:%d", lo1.LeafNode.Port)}, 3583 {Scheme: "nats", Host: fmt.Sprintf("127.0.0.1:%d", lo2.LeafNode.Port)}, 3584 }}} 3585 r2 := RunServer(ro2) 3586 defer r2.Shutdown() 3587 3588 checkClusterFormed(t, r1, r2) 3589 checkLeafNodeConnected(t, r1) 3590 checkLeafNodeConnected(t, r2) 3591 3592 l1.Shutdown() 3593 3594 // Now wait for r1 and r2 to reconnect, they should not have a problem of loop detection. 3595 checkLeafNodeConnected(t, r1) 3596 checkLeafNodeConnected(t, r2) 3597 3598 // Wait and make sure we don't have a loop error 3599 timeout := time.NewTimer(500 * time.Millisecond) 3600 for { 3601 select { 3602 case err := <-l.errCh: 3603 if strings.Contains(err, "Loop detected") { 3604 t.Fatal(err) 3605 } 3606 case <-timeout.C: 3607 // OK, we are done. 3608 return 3609 } 3610 } 3611 } 3612 3613 func TestLeafNodeUnsubOnRouteDisconnect(t *testing.T) { 3614 lo1 := DefaultOptions() 3615 lo1.LeafNode.Host = "127.0.0.1" 3616 lo1.LeafNode.Port = -1 3617 lo1.Cluster.Name = "local" 3618 lo1.Cluster.Host = "127.0.0.1" 3619 lo1.Cluster.Port = -1 3620 l1 := RunServer(lo1) 3621 defer l1.Shutdown() 3622 3623 lo2 := DefaultOptions() 3624 lo2.LeafNode.Host = "127.0.0.1" 3625 lo2.LeafNode.Port = -1 3626 lo2.Cluster.Name = "local" 3627 lo2.Cluster.Host = "127.0.0.1" 3628 lo2.Cluster.Port = -1 3629 lo2.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", lo1.Cluster.Port)) 3630 l2 := RunServer(lo2) 3631 defer l2.Shutdown() 3632 3633 checkClusterFormed(t, l1, l2) 3634 3635 u1, _ := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", lo1.LeafNode.Port)) 3636 u2, _ := url.Parse(fmt.Sprintf("nats://127.0.0.1:%d", lo2.LeafNode.Port)) 3637 urls := []*url.URL{u1, u2} 3638 3639 ro1 := DefaultOptions() 3640 // DefaultOptions sets a cluster name, so make sure they are different. 3641 // Also, we don't have r1 and r2 clustered in this test, so set port to 0. 3642 ro1.Cluster.Name = _EMPTY_ 3643 ro1.Cluster.Port = 0 3644 ro1.LeafNode.ReconnectInterval = 50 * time.Millisecond 3645 ro1.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: urls}} 3646 r1 := RunServer(ro1) 3647 defer r1.Shutdown() 3648 3649 ro2 := DefaultOptions() 3650 ro1.Cluster.Name = _EMPTY_ 3651 ro2.Cluster.Port = 0 3652 ro2.LeafNode.ReconnectInterval = 50 * time.Millisecond 3653 // Have this one point only to l2 3654 ro2.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{u2}}} 3655 r2 := RunServer(ro2) 3656 defer r2.Shutdown() 3657 3658 checkLeafNodeConnected(t, r1) 3659 checkLeafNodeConnected(t, r2) 3660 3661 // Create a subscription on r1. 3662 nc := natsConnect(t, r1.ClientURL()) 3663 defer nc.Close() 3664 sub := natsSubSync(t, nc, "foo") 3665 natsFlush(t, nc) 3666 3667 checkSubInterest(t, l2, globalAccountName, "foo", time.Second) 3668 checkSubInterest(t, r2, globalAccountName, "foo", time.Second) 3669 3670 nc2 := natsConnect(t, r2.ClientURL()) 3671 defer nc2.Close() 3672 natsPub(t, nc, "foo", []byte("msg1")) 3673 3674 // Check message received 3675 natsNexMsg(t, sub, time.Second) 3676 3677 // Now shutdown l1, l2 should update subscription interest to r2. 3678 // When r1 reconnects to l2, subscription should be updated too. 3679 l1.Shutdown() 3680 3681 // Wait a bit (so that the check of interest is not OK just because 3682 // the route would not have been yet detected as broken), and check 3683 // interest still present on r2, l2. 3684 time.Sleep(100 * time.Millisecond) 3685 checkSubInterest(t, l2, globalAccountName, "foo", time.Second) 3686 checkSubInterest(t, r2, globalAccountName, "foo", time.Second) 3687 3688 // Check again that message received ok 3689 natsPub(t, nc, "foo", []byte("msg2")) 3690 natsNexMsg(t, sub, time.Second) 3691 3692 // Now close client. Interest should disappear on r2. Due to a bug, 3693 // it was not. 3694 nc.Close() 3695 3696 checkFor(t, time.Second, 15*time.Millisecond, func() error { 3697 acc := r2.GlobalAccount() 3698 if n := acc.Interest("foo"); n != 0 { 3699 return fmt.Errorf("Still interest on subject: %v", n) 3700 } 3701 return nil 3702 }) 3703 } 3704 3705 func TestLeafNodeNoPingBeforeConnect(t *testing.T) { 3706 o := DefaultOptions() 3707 o.LeafNode.Port = -1 3708 o.LeafNode.AuthTimeout = 0.5 3709 // For this test we need to disable compression, because we do use 3710 // the ping timer instead of the auth timer before the negotiation 3711 // is complete. 3712 o.LeafNode.Compression.Mode = CompressionOff 3713 s := RunServer(o) 3714 defer s.Shutdown() 3715 3716 addr := fmt.Sprintf("127.0.0.1:%d", o.LeafNode.Port) 3717 c, err := net.Dial("tcp", addr) 3718 if err != nil { 3719 t.Fatalf("Error on dial: %v", err) 3720 } 3721 defer c.Close() 3722 3723 // Read the info 3724 br := bufio.NewReader(c) 3725 c.SetReadDeadline(time.Now().Add(time.Second)) 3726 l, _, err := br.ReadLine() 3727 if err != nil { 3728 t.Fatalf("Error on read: %v", err) 3729 } 3730 if !strings.HasPrefix(string(l), "INFO") { 3731 t.Fatalf("Wrong proto: %q", l) 3732 } 3733 3734 var leaf *client 3735 checkFor(t, time.Second, 15*time.Millisecond, func() error { 3736 s.grMu.Lock() 3737 for _, l := range s.grTmpClients { 3738 leaf = l 3739 break 3740 } 3741 s.grMu.Unlock() 3742 if leaf == nil { 3743 return fmt.Errorf("No leaf connection found") 3744 } 3745 return nil 3746 }) 3747 3748 // Make sure that ping timer is not set 3749 leaf.mu.Lock() 3750 ptmrSet := leaf.ping.tmr != nil 3751 leaf.mu.Unlock() 3752 3753 if ptmrSet { 3754 t.Fatal("Ping timer was set before CONNECT was processed") 3755 } 3756 3757 // Send CONNECT 3758 if _, err := c.Write([]byte("CONNECT {}\r\n")); err != nil { 3759 t.Fatalf("Error writing connect: %v", err) 3760 } 3761 3762 // Check that we correctly set the timer now 3763 checkFor(t, time.Second, 15*time.Millisecond, func() error { 3764 leaf.mu.Lock() 3765 ptmrSet := leaf.ping.tmr != nil 3766 leaf.mu.Unlock() 3767 if !ptmrSet { 3768 return fmt.Errorf("Timer still not set") 3769 } 3770 return nil 3771 }) 3772 3773 // Reduce the first ping.. 3774 leaf.mu.Lock() 3775 leaf.ping.tmr.Reset(15 * time.Millisecond) 3776 leaf.mu.Unlock() 3777 3778 // Now consume that PING (we may get LS+, etc..) 3779 for { 3780 c.SetReadDeadline(time.Now().Add(time.Second)) 3781 l, _, err = br.ReadLine() 3782 if err != nil { 3783 t.Fatalf("Error on read: %v", err) 3784 } 3785 if strings.HasPrefix(string(l), "PING") { 3786 checkLeafNodeConnected(t, s) 3787 return 3788 } 3789 } 3790 } 3791 3792 func TestLeafNodeNoMsgLoop(t *testing.T) { 3793 hubConf := ` 3794 listen: "127.0.0.1:-1" 3795 accounts { 3796 FOO { 3797 users [ 3798 {username: leaf, password: pass} 3799 {username: user, password: pass} 3800 ] 3801 } 3802 } 3803 cluster { 3804 name: "hub" 3805 listen: "127.0.0.1:-1" 3806 %s 3807 } 3808 leafnodes { 3809 listen: "127.0.0.1:-1" 3810 authorization { 3811 account: FOO 3812 } 3813 } 3814 ` 3815 configS1 := createConfFile(t, []byte(fmt.Sprintf(hubConf, ""))) 3816 s1, o1 := RunServerWithConfig(configS1) 3817 defer s1.Shutdown() 3818 3819 configS2S3 := createConfFile(t, []byte(fmt.Sprintf(hubConf, fmt.Sprintf(`routes: ["nats://127.0.0.1:%d"]`, o1.Cluster.Port)))) 3820 s2, o2 := RunServerWithConfig(configS2S3) 3821 defer s2.Shutdown() 3822 3823 s3, _ := RunServerWithConfig(configS2S3) 3824 defer s3.Shutdown() 3825 3826 checkClusterFormed(t, s1, s2, s3) 3827 3828 contentLN := ` 3829 listen: "127.0.0.1:%d" 3830 accounts { 3831 FOO { 3832 users [ 3833 {username: leaf, password: pass} 3834 {username: user, password: pass} 3835 ] 3836 } 3837 } 3838 leafnodes { 3839 remotes = [ 3840 { 3841 url: "nats://leaf:pass@127.0.0.1:%d" 3842 account: FOO 3843 } 3844 ] 3845 } 3846 ` 3847 lnconf := createConfFile(t, []byte(fmt.Sprintf(contentLN, -1, o1.LeafNode.Port))) 3848 sl1, slo1 := RunServerWithConfig(lnconf) 3849 defer sl1.Shutdown() 3850 3851 sl2, slo2 := RunServerWithConfig(lnconf) 3852 defer sl2.Shutdown() 3853 3854 checkLeafNodeConnected(t, sl1) 3855 checkLeafNodeConnected(t, sl2) 3856 3857 // Create users on each leafnode 3858 nc1, err := nats.Connect(fmt.Sprintf("nats://user:pass@127.0.0.1:%d", slo1.Port)) 3859 if err != nil { 3860 t.Fatalf("Error on connect: %v", err) 3861 } 3862 defer nc1.Close() 3863 3864 rch := make(chan struct{}, 1) 3865 nc2, err := nats.Connect( 3866 fmt.Sprintf("nats://user:pass@127.0.0.1:%d", slo2.Port), 3867 nats.ReconnectWait(50*time.Millisecond), 3868 nats.ReconnectHandler(func(_ *nats.Conn) { 3869 rch <- struct{}{} 3870 }), 3871 ) 3872 if err != nil { 3873 t.Fatalf("Error on connect: %v", err) 3874 } 3875 defer nc2.Close() 3876 3877 // Create queue subs on sl2 3878 nc2.QueueSubscribe("foo", "bar", func(_ *nats.Msg) {}) 3879 nc2.QueueSubscribe("foo", "bar", func(_ *nats.Msg) {}) 3880 nc2.Flush() 3881 3882 // Wait for interest to propagate to sl1 3883 checkSubInterest(t, sl1, "FOO", "foo", 250*time.Millisecond) 3884 3885 // Create sub on sl1 3886 ch := make(chan *nats.Msg, 10) 3887 nc1.Subscribe("foo", func(m *nats.Msg) { 3888 select { 3889 case ch <- m: 3890 default: 3891 } 3892 }) 3893 nc1.Flush() 3894 3895 checkSubInterest(t, sl2, "FOO", "foo", 250*time.Millisecond) 3896 3897 // Produce from sl1 3898 nc1.Publish("foo", []byte("msg1")) 3899 3900 // Check message is received by plain sub 3901 select { 3902 case <-ch: 3903 case <-time.After(time.Second): 3904 t.Fatalf("Did not receive message") 3905 } 3906 3907 // Restart leaf node, this time make sure we connect to 2nd server. 3908 sl2.Shutdown() 3909 3910 // Use config file but this time reuse the client port and set the 2nd server for 3911 // the remote leaf node port. 3912 lnconf = createConfFile(t, []byte(fmt.Sprintf(contentLN, slo2.Port, o2.LeafNode.Port))) 3913 sl2, _ = RunServerWithConfig(lnconf) 3914 defer sl2.Shutdown() 3915 3916 checkLeafNodeConnected(t, sl2) 3917 3918 // Wait for client to reconnect 3919 select { 3920 case <-rch: 3921 case <-time.After(time.Second): 3922 t.Fatalf("Did not reconnect") 3923 } 3924 3925 // Produce a new messages 3926 for i := 0; i < 10; i++ { 3927 nc1.Publish("foo", []byte(fmt.Sprintf("msg%d", 2+i))) 3928 3929 // Check sub receives 1 message 3930 select { 3931 case <-ch: 3932 case <-time.After(time.Second): 3933 t.Fatalf("Did not receive message") 3934 } 3935 // Check that there is no more... 3936 select { 3937 case m := <-ch: 3938 t.Fatalf("Loop: received second message %s", m.Data) 3939 case <-time.After(50 * time.Millisecond): 3940 // OK 3941 } 3942 } 3943 } 3944 3945 func TestLeafNodeInterestPropagationDaisychain(t *testing.T) { 3946 aTmpl := ` 3947 port: %d 3948 leafnodes { 3949 port: %d 3950 } 3951 ` 3952 3953 confA := createConfFile(t, []byte(fmt.Sprintf(aTmpl, -1, -1))) 3954 sA, _ := RunServerWithConfig(confA) 3955 defer sA.Shutdown() 3956 3957 aPort := sA.opts.Port 3958 aLeafPort := sA.opts.LeafNode.Port 3959 3960 confB := createConfFile(t, []byte(fmt.Sprintf(` 3961 port: -1 3962 leafnodes { 3963 port: -1 3964 remotes = [{ 3965 url:"nats://127.0.0.1:%d" 3966 }] 3967 }`, aLeafPort))) 3968 sB, _ := RunServerWithConfig(confB) 3969 defer sB.Shutdown() 3970 3971 confC := createConfFile(t, []byte(fmt.Sprintf(` 3972 port: -1 3973 leafnodes { 3974 port: -1 3975 remotes = [{url:"nats://127.0.0.1:%d"}] 3976 }`, sB.opts.LeafNode.Port))) 3977 sC, _ := RunServerWithConfig(confC) 3978 defer sC.Shutdown() 3979 3980 checkLeafNodeConnectedCount(t, sC, 1) 3981 checkLeafNodeConnectedCount(t, sB, 2) 3982 checkLeafNodeConnectedCount(t, sA, 1) 3983 3984 ncC := natsConnect(t, sC.ClientURL()) 3985 defer ncC.Close() 3986 _, err := ncC.SubscribeSync("foo") 3987 require_NoError(t, err) 3988 require_NoError(t, ncC.Flush()) 3989 3990 checkSubInterest(t, sC, "$G", "foo", time.Second) 3991 checkSubInterest(t, sB, "$G", "foo", time.Second) 3992 checkSubInterest(t, sA, "$G", "foo", time.Second) 3993 3994 ncA := natsConnect(t, sA.ClientURL()) 3995 defer ncA.Close() 3996 3997 sA.Shutdown() 3998 sA.WaitForShutdown() 3999 4000 confAA := createConfFile(t, []byte(fmt.Sprintf(aTmpl, aPort, aLeafPort))) 4001 sAA, _ := RunServerWithConfig(confAA) 4002 defer sAA.Shutdown() 4003 4004 checkLeafNodeConnectedCount(t, sAA, 1) 4005 checkLeafNodeConnectedCount(t, sB, 2) 4006 checkLeafNodeConnectedCount(t, sC, 1) 4007 4008 checkSubInterest(t, sC, "$G", "foo", time.Second) 4009 checkSubInterest(t, sB, "$G", "foo", time.Second) 4010 checkSubInterest(t, sAA, "$G", "foo", time.Second) // failure issue 2448 4011 } 4012 4013 func TestLeafNodeQueueGroupWithLateLNJoin(t *testing.T) { 4014 /* 4015 4016 Topology: A cluster of leafnodes LN2 and LN3, connect 4017 to a cluster C1, C2. 4018 4019 sub(foo) sub(foo) 4020 \ / 4021 C1 <-> C2 4022 ^ ^ 4023 | | 4024 LN2 <-> LN3 4025 / \ 4026 sub(foo) sub(foo) 4027 4028 Once the above is set, start LN1 that connects to C1. 4029 4030 sub(foo) sub(foo) 4031 \ / 4032 LN1 -> C1 <-> C2 4033 ^ ^ 4034 | | 4035 LN2 <-> LN3 4036 / \ 4037 sub(foo) sub(foo) 4038 4039 Remove subs to LN3, C2 and C1. 4040 4041 LN1 -> C1 <-> C2 4042 ^ ^ 4043 | | 4044 LN2 <-> LN3 4045 / 4046 sub(foo) 4047 4048 Publish from LN1 and verify message is received by sub on LN2. 4049 4050 pub(foo) 4051 \ 4052 LN1 -> C1 <-> C2 4053 ^ ^ 4054 | | 4055 LN2 <-> LN3 4056 / 4057 sub(foo) 4058 */ 4059 co1 := DefaultOptions() 4060 co1.LeafNode.Host = "127.0.0.1" 4061 co1.LeafNode.Port = -1 4062 co1.Cluster.Name = "ngs" 4063 co1.Cluster.Host = "127.0.0.1" 4064 co1.Cluster.Port = -1 4065 c1 := RunServer(co1) 4066 defer c1.Shutdown() 4067 4068 co2 := DefaultOptions() 4069 co2.LeafNode.Host = "127.0.0.1" 4070 co2.LeafNode.Port = -1 4071 co2.Cluster.Name = "ngs" 4072 co2.Cluster.Host = "127.0.0.1" 4073 co2.Cluster.Port = -1 4074 co2.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", co1.Cluster.Port)) 4075 c2 := RunServer(co2) 4076 defer c2.Shutdown() 4077 4078 checkClusterFormed(t, c1, c2) 4079 4080 lo2 := DefaultOptions() 4081 lo2.Cluster.Name = "local" 4082 lo2.Cluster.Host = "127.0.0.1" 4083 lo2.Cluster.Port = -1 4084 lo2.LeafNode.ReconnectInterval = 50 * time.Millisecond 4085 lo2.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{{Scheme: "nats", Host: fmt.Sprintf("127.0.0.1:%d", co1.LeafNode.Port)}}}} 4086 ln2 := RunServer(lo2) 4087 defer ln2.Shutdown() 4088 4089 lo3 := DefaultOptions() 4090 lo3.Cluster.Name = "local" 4091 lo3.Cluster.Host = "127.0.0.1" 4092 lo3.Cluster.Port = -1 4093 lo3.Routes = RoutesFromStr(fmt.Sprintf("nats://127.0.0.1:%d", lo2.Cluster.Port)) 4094 lo3.LeafNode.ReconnectInterval = 50 * time.Millisecond 4095 lo3.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{{Scheme: "nats", Host: fmt.Sprintf("127.0.0.1:%d", co2.LeafNode.Port)}}}} 4096 ln3 := RunServer(lo3) 4097 defer ln3.Shutdown() 4098 4099 checkClusterFormed(t, ln2, ln3) 4100 checkLeafNodeConnected(t, ln2) 4101 checkLeafNodeConnected(t, ln3) 4102 4103 cln2 := natsConnect(t, ln2.ClientURL()) 4104 defer cln2.Close() 4105 sln2 := natsQueueSubSync(t, cln2, "foo", "qgroup") 4106 natsFlush(t, cln2) 4107 4108 cln3 := natsConnect(t, ln3.ClientURL()) 4109 defer cln3.Close() 4110 sln3 := natsQueueSubSync(t, cln3, "foo", "qgroup") 4111 natsFlush(t, cln3) 4112 4113 cc1 := natsConnect(t, c1.ClientURL()) 4114 defer cc1.Close() 4115 sc1 := natsQueueSubSync(t, cc1, "foo", "qgroup") 4116 natsFlush(t, cc1) 4117 4118 cc2 := natsConnect(t, c2.ClientURL()) 4119 defer cc2.Close() 4120 sc2 := natsQueueSubSync(t, cc2, "foo", "qgroup") 4121 natsFlush(t, cc2) 4122 4123 checkSubInterest(t, c1, globalAccountName, "foo", time.Second) 4124 checkSubInterest(t, c2, globalAccountName, "foo", time.Second) 4125 checkSubInterest(t, ln2, globalAccountName, "foo", time.Second) 4126 checkSubInterest(t, ln3, globalAccountName, "foo", time.Second) 4127 4128 lo1 := DefaultOptions() 4129 lo1.LeafNode.ReconnectInterval = 50 * time.Millisecond 4130 lo1.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{{Scheme: "nats", Host: fmt.Sprintf("127.0.0.1:%d", co1.LeafNode.Port)}}}} 4131 ln1 := RunServer(lo1) 4132 defer ln1.Shutdown() 4133 4134 checkLeafNodeConnected(t, ln1) 4135 checkSubInterest(t, ln1, globalAccountName, "foo", time.Second) 4136 4137 sln3.Unsubscribe() 4138 natsFlush(t, cln3) 4139 sc2.Unsubscribe() 4140 natsFlush(t, cc2) 4141 sc1.Unsubscribe() 4142 natsFlush(t, cc1) 4143 4144 cln1 := natsConnect(t, ln1.ClientURL()) 4145 defer cln1.Close() 4146 4147 natsPub(t, cln1, "foo", []byte("hello")) 4148 natsNexMsg(t, sln2, time.Second) 4149 } 4150 4151 func TestLeafNodeJetStreamDomainMapCrossTalk(t *testing.T) { 4152 accs := ` 4153 accounts :{ 4154 A:{ jetstream: enable, users:[ {user:a1,password:a1}]}, 4155 SYS:{ users:[ {user:s1,password:s1}]}, 4156 } 4157 system_account: SYS 4158 ` 4159 4160 sd1 := t.TempDir() 4161 confA := createConfFile(t, []byte(fmt.Sprintf(` 4162 listen: 127.0.0.1:-1 4163 %s 4164 jetstream: { domain: da, store_dir: '%s', max_mem: 50Mb, max_file: 50Mb } 4165 leafnodes: { 4166 listen: 127.0.0.1:-1 4167 no_advertise: true 4168 authorization: { 4169 timeout: 0.5 4170 } 4171 } 4172 `, accs, sd1))) 4173 sA, _ := RunServerWithConfig(confA) 4174 defer sA.Shutdown() 4175 4176 sd2 := t.TempDir() 4177 confL := createConfFile(t, []byte(fmt.Sprintf(` 4178 listen: 127.0.0.1:-1 4179 %s 4180 jetstream: { domain: dl, store_dir: '%s', max_mem: 50Mb, max_file: 50Mb } 4181 leafnodes:{ 4182 no_advertise: true 4183 remotes:[{url:nats://a1:a1@127.0.0.1:%d, account: A}, 4184 {url:nats://s1:s1@127.0.0.1:%d, account: SYS}] 4185 } 4186 `, accs, sd2, sA.opts.LeafNode.Port, sA.opts.LeafNode.Port))) 4187 sL, _ := RunServerWithConfig(confL) 4188 defer sL.Shutdown() 4189 4190 ncA := natsConnect(t, sA.ClientURL(), nats.UserInfo("a1", "a1")) 4191 defer ncA.Close() 4192 ncL := natsConnect(t, sL.ClientURL(), nats.UserInfo("a1", "a1")) 4193 defer ncL.Close() 4194 4195 test := func(jsA, jsL nats.JetStreamContext) { 4196 kvA, err := jsA.CreateKeyValue(&nats.KeyValueConfig{Bucket: "bucket"}) 4197 require_NoError(t, err) 4198 kvL, err := jsL.CreateKeyValue(&nats.KeyValueConfig{Bucket: "bucket"}) 4199 require_NoError(t, err) 4200 4201 _, err = kvA.Put("A", nil) 4202 require_NoError(t, err) 4203 _, err = kvL.Put("L", nil) 4204 require_NoError(t, err) 4205 4206 // check for unwanted cross talk 4207 _, err = kvA.Get("A") 4208 require_NoError(t, err) 4209 _, err = kvA.Get("l") 4210 require_Error(t, err) 4211 require_True(t, err == nats.ErrKeyNotFound) 4212 4213 _, err = kvL.Get("A") 4214 require_Error(t, err) 4215 require_True(t, err == nats.ErrKeyNotFound) 4216 _, err = kvL.Get("L") 4217 require_NoError(t, err) 4218 4219 err = jsA.DeleteKeyValue("bucket") 4220 require_NoError(t, err) 4221 err = jsL.DeleteKeyValue("bucket") 4222 require_NoError(t, err) 4223 } 4224 4225 jsA, err := ncA.JetStream() 4226 require_NoError(t, err) 4227 jsL, err := ncL.JetStream() 4228 require_NoError(t, err) 4229 test(jsA, jsL) 4230 4231 jsAL, err := ncA.JetStream(nats.Domain("dl")) 4232 require_NoError(t, err) 4233 jsLA, err := ncL.JetStream(nats.Domain("da")) 4234 require_NoError(t, err) 4235 test(jsAL, jsLA) 4236 4237 jsAA, err := ncA.JetStream(nats.Domain("da")) 4238 require_NoError(t, err) 4239 jsLL, err := ncL.JetStream(nats.Domain("dl")) 4240 require_NoError(t, err) 4241 test(jsAA, jsLL) 4242 } 4243 4244 type checkLeafMinVersionLogger struct { 4245 DummyLogger 4246 errCh chan string 4247 connCh chan string 4248 } 4249 4250 func (l *checkLeafMinVersionLogger) Errorf(format string, args ...any) { 4251 msg := fmt.Sprintf(format, args...) 4252 if strings.Contains(msg, "minimum version") { 4253 select { 4254 case l.errCh <- msg: 4255 default: 4256 } 4257 } 4258 } 4259 4260 func (l *checkLeafMinVersionLogger) Noticef(format string, args ...any) { 4261 msg := fmt.Sprintf(format, args...) 4262 if strings.Contains(msg, "Leafnode connection created") { 4263 select { 4264 case l.connCh <- msg: 4265 default: 4266 } 4267 } 4268 } 4269 4270 func TestLeafNodeMinVersion(t *testing.T) { 4271 conf := createConfFile(t, []byte(` 4272 port: -1 4273 leafnodes { 4274 port: -1 4275 min_version: 2.8.0 4276 } 4277 `)) 4278 s, o := RunServerWithConfig(conf) 4279 defer s.Shutdown() 4280 4281 rconf := createConfFile(t, []byte(fmt.Sprintf(` 4282 port: -1 4283 leafnodes { 4284 remotes [ 4285 {url: "nats://127.0.0.1:%d" } 4286 ] 4287 } 4288 `, o.LeafNode.Port))) 4289 ln, _ := RunServerWithConfig(rconf) 4290 defer ln.Shutdown() 4291 4292 checkLeafNodeConnected(t, s) 4293 checkLeafNodeConnected(t, ln) 4294 4295 ln.Shutdown() 4296 s.Shutdown() 4297 4298 // Now makes sure we validate options, not just config file. 4299 for _, test := range []struct { 4300 name string 4301 version string 4302 err string 4303 }{ 4304 {"invalid version", "abc", "semver"}, 4305 {"version too low", "2.7.9", "the minimum version should be at least 2.8.0"}, 4306 } { 4307 t.Run(test.name, func(t *testing.T) { 4308 o.Port = -1 4309 o.LeafNode.Port = -1 4310 o.LeafNode.MinVersion = test.version 4311 if s, err := NewServer(o); err == nil || !strings.Contains(err.Error(), test.err) { 4312 if s != nil { 4313 s.Shutdown() 4314 } 4315 t.Fatalf("Expected error to contain %q, got %v", test.err, err) 4316 } 4317 }) 4318 } 4319 4320 // Ok, so now to verify that a server rejects a leafnode connection 4321 // we will set the min_version above our current VERSION. So first 4322 // decompose the version: 4323 major, minor, _, err := versionComponents(VERSION) 4324 if err != nil { 4325 t.Fatalf("The current server version %q is not valid: %v", VERSION, err) 4326 } 4327 // Let's make our minimum server an minor version above 4328 mv := fmt.Sprintf("%d.%d.0", major, minor+1) 4329 conf = createConfFile(t, []byte(fmt.Sprintf(` 4330 port: -1 4331 leafnodes { 4332 port: -1 4333 min_version: "%s" 4334 } 4335 `, mv))) 4336 s, o = RunServerWithConfig(conf) 4337 defer s.Shutdown() 4338 4339 l := &checkLeafMinVersionLogger{errCh: make(chan string, 1), connCh: make(chan string, 1)} 4340 s.SetLogger(l, false, false) 4341 4342 rconf = createConfFile(t, []byte(fmt.Sprintf(` 4343 port: -1 4344 leafnodes { 4345 remotes [ 4346 {url: "nats://127.0.0.1:%d" } 4347 ] 4348 } 4349 `, o.LeafNode.Port))) 4350 lo := LoadConfig(rconf) 4351 lo.LeafNode.ReconnectInterval = 50 * time.Millisecond 4352 ln = RunServer(lo) 4353 defer ln.Shutdown() 4354 4355 select { 4356 case <-l.connCh: 4357 case <-time.After(time.Second): 4358 t.Fatal("Remote did not try to connect") 4359 } 4360 4361 select { 4362 case <-l.errCh: 4363 case <-time.After(time.Second): 4364 t.Fatal("Did not get the minimum version required error") 4365 } 4366 4367 // Since we have a very small reconnect interval, if the connection was 4368 // closed "right away", then we should have had a reconnect attempt with 4369 // another failure. This should not be the case because the server will 4370 // wait 5s before closing the connection. 4371 select { 4372 case <-l.connCh: 4373 t.Fatal("Should not have tried to reconnect") 4374 case <-time.After(250 * time.Millisecond): 4375 // OK 4376 } 4377 } 4378 4379 func TestLeafNodeStreamAndShadowSubs(t *testing.T) { 4380 hubConf := createConfFile(t, []byte(` 4381 port: -1 4382 leafnodes { 4383 port: -1 4384 authorization: { 4385 user: leaf 4386 password: leaf 4387 account: B 4388 } 4389 } 4390 accounts: { 4391 A: { 4392 users = [{user: usrA, password: usrA}] 4393 exports: [{stream: foo.*.>}] 4394 } 4395 B: { 4396 imports: [{stream: {account: A, subject: foo.*.>}}] 4397 } 4398 } 4399 `)) 4400 hub, hubo := RunServerWithConfig(hubConf) 4401 defer hub.Shutdown() 4402 4403 leafConfContet := fmt.Sprintf(` 4404 port: -1 4405 leafnodes { 4406 remotes = [ 4407 { 4408 url: "nats-leaf://leaf:leaf@127.0.0.1:%d" 4409 account: B 4410 } 4411 ] 4412 } 4413 accounts: { 4414 B: { 4415 exports: [{stream: foo.*.>}] 4416 } 4417 C: { 4418 users: [{user: usrC, password: usrC}] 4419 imports: [{stream: {account: B, subject: foo.bar.>}}] 4420 } 4421 } 4422 `, hubo.LeafNode.Port) 4423 leafConf := createConfFile(t, []byte(leafConfContet)) 4424 leafo := LoadConfig(leafConf) 4425 leafo.LeafNode.ReconnectInterval = 50 * time.Millisecond 4426 leaf := RunServer(leafo) 4427 defer leaf.Shutdown() 4428 4429 checkLeafNodeConnected(t, hub) 4430 checkLeafNodeConnected(t, leaf) 4431 4432 subPubAndCheck := func() { 4433 t.Helper() 4434 4435 ncl, err := nats.Connect(leaf.ClientURL(), nats.UserInfo("usrC", "usrC")) 4436 if err != nil { 4437 t.Fatalf("Error connecting: %v", err) 4438 } 4439 defer ncl.Close() 4440 4441 // This will send an LS+ to the "hub" server. 4442 sub, err := ncl.SubscribeSync("foo.*.baz") 4443 if err != nil { 4444 t.Fatalf("Error subscribing: %v", err) 4445 } 4446 ncl.Flush() 4447 4448 ncm, err := nats.Connect(hub.ClientURL(), nats.UserInfo("usrA", "usrA")) 4449 if err != nil { 4450 t.Fatalf("Error connecting: %v", err) 4451 } 4452 defer ncm.Close() 4453 4454 // Try a few times in case subject interest has not propagated yet 4455 for i := 0; i < 5; i++ { 4456 ncm.Publish("foo.bar.baz", []byte("msg")) 4457 if _, err := sub.NextMsg(time.Second); err == nil { 4458 // OK, done! 4459 return 4460 } 4461 } 4462 t.Fatal("Message was not received") 4463 } 4464 subPubAndCheck() 4465 4466 // Now cause a restart of the accepting side so that the leaf connection 4467 // is recreated. 4468 hub.Shutdown() 4469 hub = RunServer(hubo) 4470 defer hub.Shutdown() 4471 4472 checkLeafNodeConnected(t, hub) 4473 checkLeafNodeConnected(t, leaf) 4474 4475 subPubAndCheck() 4476 4477 // Issue a config reload even though we make no modification. There was 4478 // a defect that caused the interest propagation to break. 4479 // Set the ReconnectInterval to the default value so that reload does not complain. 4480 leaf.getOpts().LeafNode.ReconnectInterval = DEFAULT_LEAF_NODE_RECONNECT 4481 reloadUpdateConfig(t, leaf, leafConf, leafConfContet) 4482 4483 // Check again 4484 subPubAndCheck() 4485 } 4486 4487 func TestLeafNodeAuthConfigReload(t *testing.T) { 4488 template := ` 4489 listen: 127.0.0.1:-1 4490 accounts { test: {} } 4491 leaf { 4492 listen: "127.0.0.1:7422" 4493 tls { 4494 cert_file: "../test/configs/certs/server-cert.pem" 4495 key_file: "../test/configs/certs/server-key.pem" 4496 ca_file: "../test/configs/certs/ca.pem" 4497 } 4498 authorization { 4499 # These are only fields allowed atm. 4500 users = [ { user: test, password: "s3cret1", account: "test" } ] 4501 } 4502 } 4503 ` 4504 conf := createConfFile(t, []byte(template)) 4505 4506 s, _ := RunServerWithConfig(conf) 4507 defer s.Shutdown() 4508 4509 lg := &captureErrorLogger{errCh: make(chan string, 10)} 4510 s.SetLogger(lg, false, false) 4511 4512 // Reload here should work ok. 4513 reloadUpdateConfig(t, s, conf, template) 4514 } 4515 4516 func TestLeafNodeSignatureCB(t *testing.T) { 4517 content := ` 4518 port: -1 4519 server_name: OP 4520 operator = "../test/configs/nkeys/op.jwt" 4521 resolver = MEMORY 4522 listen: "127.0.0.1:-1" 4523 leafnodes { 4524 listen: "127.0.0.1:-1" 4525 } 4526 ` 4527 conf := createConfFile(t, []byte(content)) 4528 s, opts := RunServerWithConfig(conf) 4529 defer s.Shutdown() 4530 4531 _, akp := createAccount(s) 4532 kp, _ := nkeys.CreateUser() 4533 pub, _ := kp.PublicKey() 4534 nuc := jwt.NewUserClaims(pub) 4535 ujwt, err := nuc.Encode(akp) 4536 if err != nil { 4537 t.Fatalf("Error generating user JWT: %v", err) 4538 } 4539 4540 lopts := &DefaultTestOptions 4541 u, err := url.Parse(fmt.Sprintf("nats://%s:%d", opts.LeafNode.Host, opts.LeafNode.Port)) 4542 if err != nil { 4543 t.Fatalf("Error parsing url: %v", err) 4544 } 4545 remote := &RemoteLeafOpts{URLs: []*url.URL{u}} 4546 remote.SignatureCB = func(nonce []byte) (string, []byte, error) { 4547 return "", nil, fmt.Errorf("on purpose") 4548 } 4549 lopts.LeafNode.Remotes = []*RemoteLeafOpts{remote} 4550 lopts.LeafNode.ReconnectInterval = 100 * time.Millisecond 4551 sl := RunServer(lopts) 4552 defer sl.Shutdown() 4553 4554 slog := &captureErrorLogger{errCh: make(chan string, 10)} 4555 sl.SetLogger(slog, false, false) 4556 4557 // Now check that the leafnode got the error that the callback returned. 4558 select { 4559 case err := <-slog.errCh: 4560 if !strings.Contains(err, "on purpose") { 4561 t.Fatalf("Expected error from cb, got %v", err) 4562 } 4563 case <-time.After(time.Second): 4564 t.Fatal("Did not get expected error") 4565 } 4566 4567 sl.Shutdown() 4568 // Now check what happens if the connection is closed while in the callback. 4569 blockCh := make(chan struct{}) 4570 remote.SignatureCB = func(nonce []byte) (string, []byte, error) { 4571 <-blockCh 4572 sig, err := kp.Sign(nonce) 4573 return ujwt, sig, err 4574 } 4575 sl = RunServer(lopts) 4576 defer sl.Shutdown() 4577 4578 // Recreate the logger so that we are sure not to have possible previous errors 4579 slog = &captureErrorLogger{errCh: make(chan string, 10)} 4580 sl.SetLogger(slog, false, false) 4581 4582 // Get the leaf connection from the temp clients map and close it. 4583 checkFor(t, time.Second, 15*time.Millisecond, func() error { 4584 var c *client 4585 sl.grMu.Lock() 4586 for _, cli := range sl.grTmpClients { 4587 c = cli 4588 } 4589 sl.grMu.Unlock() 4590 if c == nil { 4591 return fmt.Errorf("Client still not found in temp map") 4592 } 4593 c.closeConnection(ClientClosed) 4594 return nil 4595 }) 4596 4597 // Release the callback, and check we get the appropriate error. 4598 close(blockCh) 4599 select { 4600 case err := <-slog.errCh: 4601 if !strings.Contains(err, ErrConnectionClosed.Error()) { 4602 t.Fatalf("Expected error that connection was closed, got %v", err) 4603 } 4604 case <-time.After(time.Second): 4605 t.Fatal("Did not get expected error") 4606 } 4607 4608 sl.Shutdown() 4609 // Change to a good CB and now it should work 4610 remote.SignatureCB = func(nonce []byte) (string, []byte, error) { 4611 sig, err := kp.Sign(nonce) 4612 return ujwt, sig, err 4613 } 4614 sl = RunServer(lopts) 4615 defer sl.Shutdown() 4616 checkLeafNodeConnected(t, sl) 4617 } 4618 4619 type testLeafTraceLogger struct { 4620 DummyLogger 4621 ch chan string 4622 } 4623 4624 func (l *testLeafTraceLogger) Tracef(format string, v ...any) { 4625 msg := fmt.Sprintf(format, v...) 4626 // We will sub to 'baz' and to 'bar', so filter on 'ba' prefix. 4627 if strings.Contains(msg, "[LS+ ba") { 4628 select { 4629 case l.ch <- msg: 4630 default: 4631 } 4632 } 4633 } 4634 4635 // Make sure permissioned denied subs do not make it to the leafnode even if existing. 4636 func TestLeafNodePermsSuppressSubs(t *testing.T) { 4637 conf := createConfFile(t, []byte(` 4638 listen: 127.0.0.1:-1 4639 authorization { 4640 PERMS = { 4641 publish = "foo" 4642 subscribe = ["_INBOX.>"] 4643 } 4644 users = [ 4645 {user: "user", password: "pass"} 4646 {user: "ln", password: "pass" , permissions: $PERMS } 4647 ] 4648 } 4649 no_auth_user: user 4650 4651 leafnodes { 4652 listen: 127.0.0.1:7422 4653 } 4654 `)) 4655 4656 lconf := createConfFile(t, []byte(` 4657 listen: 127.0.0.1:-1 4658 leafnodes { 4659 remotes = [ { url: "nats://ln:pass@127.0.0.1" } ] 4660 } 4661 trace = true 4662 `)) 4663 4664 s, _ := RunServerWithConfig(conf) 4665 defer s.Shutdown() 4666 4667 // Connect client to the hub. 4668 nc, err := nats.Connect(s.ClientURL()) 4669 require_NoError(t, err) 4670 4671 // This should not be seen on leafnode side since we only allow pub to "foo" 4672 _, err = nc.SubscribeSync("baz") 4673 require_NoError(t, err) 4674 4675 ln, _ := RunServerWithConfig(lconf) 4676 defer ln.Shutdown() 4677 4678 // Setup logger to capture trace events. 4679 l := &testLeafTraceLogger{ch: make(chan string, 10)} 4680 ln.SetLogger(l, true, true) 4681 4682 checkLeafNodeConnected(t, ln) 4683 4684 // Need to have ot reconnect to trigger since logger attaches too late. 4685 ln.mu.Lock() 4686 for _, c := range ln.leafs { 4687 c.mu.Lock() 4688 c.nc.Close() 4689 c.mu.Unlock() 4690 } 4691 ln.mu.Unlock() 4692 checkLeafNodeConnectedCount(t, ln, 0) 4693 checkLeafNodeConnectedCount(t, ln, 1) 4694 4695 select { 4696 case msg := <-l.ch: 4697 t.Fatalf("Unexpected LS+ seen on leafnode: %s", msg) 4698 case <-time.After(50 * time.Millisecond): 4699 // OK 4700 } 4701 4702 // Now double check that new subs also do not propagate. 4703 // This behavior was working already. 4704 _, err = nc.SubscribeSync("bar") 4705 require_NoError(t, err) 4706 4707 select { 4708 case msg := <-l.ch: 4709 t.Fatalf("Unexpected LS+ seen on leafnode: %s", msg) 4710 case <-time.After(50 * time.Millisecond): 4711 // OK 4712 } 4713 } 4714 4715 func TestLeafNodeDuplicateMsg(t *testing.T) { 4716 // This involves 2 clusters with leafnodes to each other with a different 4717 // account, and those accounts import/export a subject that caused 4718 // duplicate messages. This test requires static ports since we need to 4719 // have A->B and B->A. 4720 a1Conf := createConfFile(t, []byte(` 4721 cluster : { 4722 name : A 4723 port : -1 4724 } 4725 leafnodes : { 4726 port : 14333 4727 remotes : [{ 4728 account : A 4729 urls : [nats://leafa:pwd@127.0.0.1:24333] 4730 }] 4731 } 4732 port : -1 4733 server_name : A_1 4734 4735 accounts:{ 4736 A:{ 4737 users:[ 4738 {user: leafa, password: pwd}, 4739 {user: usera, password: usera, permissions: { 4740 publish:{ allow:["iot.b.topic"] } 4741 subscribe:{ allow:["iot.a.topic"] } 4742 }} 4743 ] 4744 imports:[ 4745 {stream:{account:"B", subject:"iot.a.topic"}} 4746 ] 4747 }, 4748 B:{ 4749 users:[ 4750 {user: leafb, password: pwd}, 4751 ] 4752 exports:[ 4753 {stream: "iot.a.topic", accounts: ["A"]} 4754 ] 4755 } 4756 } 4757 `)) 4758 a1, oa1 := RunServerWithConfig(a1Conf) 4759 defer a1.Shutdown() 4760 4761 a2Conf := createConfFile(t, []byte(fmt.Sprintf(` 4762 cluster : { 4763 name : A 4764 port : -1 4765 routes : [nats://127.0.0.1:%d] 4766 } 4767 leafnodes : { 4768 port : 14334 4769 remotes : [{ 4770 account : A 4771 urls : [nats://leafa:pwd@127.0.0.1:24334] 4772 }] 4773 } 4774 port : -1 4775 server_name : A_2 4776 4777 accounts:{ 4778 A:{ 4779 users:[ 4780 {user: leafa, password: pwd}, 4781 {user: usera, password: usera, permissions: { 4782 publish:{ allow:["iot.b.topic"] } 4783 subscribe:{ allow:["iot.a.topic"] } 4784 }} 4785 ] 4786 imports:[ 4787 {stream:{account:"B", subject:"iot.a.topic"}} 4788 ] 4789 }, 4790 B:{ 4791 users:[ 4792 {user: leafb, password: pwd}, 4793 ] 4794 exports:[ 4795 {stream: "iot.a.topic", accounts: ["A"]} 4796 ] 4797 } 4798 }`, oa1.Cluster.Port))) 4799 a2, _ := RunServerWithConfig(a2Conf) 4800 defer a2.Shutdown() 4801 4802 checkClusterFormed(t, a1, a2) 4803 4804 b1Conf := createConfFile(t, []byte(` 4805 cluster : { 4806 name : B 4807 port : -1 4808 } 4809 leafnodes : { 4810 port : 24333 4811 remotes : [{ 4812 account : B 4813 urls : [nats://leafb:pwd@127.0.0.1:14333] 4814 }] 4815 } 4816 port : -1 4817 server_name : B_1 4818 4819 accounts:{ 4820 A:{ 4821 users:[ 4822 {user: leafa, password: pwd}, 4823 ] 4824 exports:[ 4825 {stream: "iot.b.topic", accounts: ["B"]} 4826 ] 4827 }, 4828 B:{ 4829 users:[ 4830 {user: leafb, password: pwd}, 4831 {user: userb, password: userb, permissions: { 4832 publish:{ allow:["iot.a.topic"] }, 4833 subscribe:{ allow:["iot.b.topic"] } 4834 }} 4835 ] 4836 imports:[ 4837 {stream:{account:"A", subject:"iot.b.topic"}} 4838 ] 4839 } 4840 }`)) 4841 b1, ob1 := RunServerWithConfig(b1Conf) 4842 defer b1.Shutdown() 4843 4844 b2Conf := createConfFile(t, []byte(fmt.Sprintf(` 4845 cluster : { 4846 name : B 4847 port : -1 4848 routes : [nats://127.0.0.1:%d] 4849 } 4850 leafnodes : { 4851 port : 24334 4852 remotes : [{ 4853 account : B 4854 urls : [nats://leafb:pwd@127.0.0.1:14334] 4855 }] 4856 } 4857 port : -1 4858 server_name : B_2 4859 4860 accounts:{ 4861 A:{ 4862 users:[ 4863 {user: leafa, password: pwd}, 4864 ] 4865 exports:[ 4866 {stream: "iot.b.topic", accounts: ["B"]} 4867 ] 4868 }, 4869 B:{ 4870 users:[ 4871 {user: leafb, password: pwd}, 4872 {user: userb, password: userb, permissions: { 4873 publish:{ allow:["iot.a.topic"] }, 4874 subscribe:{ allow:["iot.b.topic"] } 4875 }} 4876 ] 4877 imports:[ 4878 {stream:{account:"A", subject:"iot.b.topic"}} 4879 ] 4880 } 4881 }`, ob1.Cluster.Port))) 4882 b2, _ := RunServerWithConfig(b2Conf) 4883 defer b2.Shutdown() 4884 4885 checkClusterFormed(t, b1, b2) 4886 4887 checkLeafNodeConnectedCount(t, a1, 2) 4888 checkLeafNodeConnectedCount(t, a2, 2) 4889 checkLeafNodeConnectedCount(t, b1, 2) 4890 checkLeafNodeConnectedCount(t, b2, 2) 4891 4892 check := func(t *testing.T, subSrv *Server, pubSrv *Server) { 4893 4894 sc := natsConnect(t, subSrv.ClientURL(), nats.UserInfo("userb", "userb")) 4895 defer sc.Close() 4896 4897 subject := "iot.b.topic" 4898 sub := natsSubSync(t, sc, subject) 4899 4900 // Wait for this to be available in A cluster 4901 checkSubInterest(t, a1, "A", subject, time.Second) 4902 checkSubInterest(t, a2, "A", subject, time.Second) 4903 4904 pb := natsConnect(t, pubSrv.ClientURL(), nats.UserInfo("usera", "usera")) 4905 defer pb.Close() 4906 4907 natsPub(t, pb, subject, []byte("msg")) 4908 natsNexMsg(t, sub, time.Second) 4909 // Should be only 1 4910 if msg, err := sub.NextMsg(100 * time.Millisecond); err == nil { 4911 t.Fatalf("Received duplicate on %q: %s", msg.Subject, msg.Data) 4912 } 4913 } 4914 t.Run("sub_b1_pub_a1", func(t *testing.T) { check(t, b1, a1) }) 4915 t.Run("sub_b1_pub_a2", func(t *testing.T) { check(t, b1, a2) }) 4916 t.Run("sub_b2_pub_a1", func(t *testing.T) { check(t, b2, a1) }) 4917 t.Run("sub_b2_pub_a2", func(t *testing.T) { check(t, b2, a2) }) 4918 } 4919 4920 func TestLeafNodeTLSHandshakeFirstVerifyNoInfoSent(t *testing.T) { 4921 confHub := createConfFile(t, []byte(` 4922 port : -1 4923 leafnodes : { 4924 port : -1 4925 tls { 4926 cert_file: "../test/configs/certs/server-cert.pem" 4927 key_file: "../test/configs/certs/server-key.pem" 4928 ca_file: "../test/configs/certs/ca.pem" 4929 timeout: 2 4930 handshake_first: true 4931 } 4932 } 4933 `)) 4934 s1, o1 := RunServerWithConfig(confHub) 4935 defer s1.Shutdown() 4936 4937 c, err := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", o1.LeafNode.Port), 2*time.Second) 4938 require_NoError(t, err) 4939 defer c.Close() 4940 4941 buf := make([]byte, 1024) 4942 // We will wait for up to 500ms to see if the server is sending (incorrectly) 4943 // the INFO. 4944 c.SetReadDeadline(time.Now().Add(500 * time.Millisecond)) 4945 n, err := c.Read(buf) 4946 c.SetReadDeadline(time.Time{}) 4947 // If we did not get an error, this is an issue... 4948 if err == nil { 4949 t.Fatalf("Should not have received anything, got n=%v buf=%s", n, buf[:n]) 4950 } 4951 // We expect a timeout error 4952 if ne, ok := err.(net.Error); !ok || !ne.Timeout() { 4953 t.Fatalf("Expected a timeout error, got %v", err) 4954 } 4955 } 4956 4957 func TestLeafNodeTLSHandshakeFirst(t *testing.T) { 4958 tmpl1 := ` 4959 port : -1 4960 leafnodes : { 4961 port : -1 4962 tls { 4963 cert_file: "../test/configs/certs/server-cert.pem" 4964 key_file: "../test/configs/certs/server-key.pem" 4965 ca_file: "../test/configs/certs/ca.pem" 4966 timeout: 2 4967 handshake_first: %s 4968 } 4969 } 4970 ` 4971 confHub := createConfFile(t, []byte(fmt.Sprintf(tmpl1, "true"))) 4972 s1, o1 := RunServerWithConfig(confHub) 4973 defer s1.Shutdown() 4974 4975 tmpl2 := ` 4976 port: -1 4977 leafnodes : { 4978 port : -1 4979 remotes : [ 4980 { 4981 urls : [tls://127.0.0.1:%d] 4982 tls { 4983 cert_file: "../test/configs/certs/client-cert.pem" 4984 key_file: "../test/configs/certs/client-key.pem" 4985 ca_file: "../test/configs/certs/ca.pem" 4986 timeout: 2 4987 first: %s 4988 } 4989 } 4990 ] 4991 } 4992 ` 4993 confSpoke := createConfFile(t, []byte(fmt.Sprintf(tmpl2, o1.LeafNode.Port, "true"))) 4994 s2, _ := RunServerWithConfig(confSpoke) 4995 defer s2.Shutdown() 4996 4997 checkLeafNodeConnected(t, s2) 4998 4999 s2.Shutdown() 5000 5001 // Now check that there will be a failure if the remote does not ask for 5002 // handshake first since the hub is configured that way. 5003 // Set a logger on s1 to capture errors 5004 l := &captureErrorLogger{errCh: make(chan string, 10)} 5005 s1.SetLogger(l, false, false) 5006 5007 confSpoke = createConfFile(t, []byte(fmt.Sprintf(tmpl2, o1.LeafNode.Port, "false"))) 5008 s2, _ = RunServerWithConfig(confSpoke) 5009 defer s2.Shutdown() 5010 5011 select { 5012 case err := <-l.errCh: 5013 if !strings.Contains(err, "handshake error") { 5014 t.Fatalf("Unexpected error: %v", err) 5015 } 5016 case <-time.After(2 * time.Second): 5017 t.Fatal("Did not get TLS handshake failure") 5018 } 5019 5020 // Check configuration reload for this remote 5021 reloadUpdateConfig(t, s2, confSpoke, fmt.Sprintf(tmpl2, o1.LeafNode.Port, "true")) 5022 checkLeafNodeConnected(t, s2) 5023 s2.Shutdown() 5024 5025 // Drain the logger error channel 5026 for done := false; !done; { 5027 select { 5028 case <-l.errCh: 5029 default: 5030 done = true 5031 } 5032 } 5033 5034 // Now change the config on the hub 5035 reloadUpdateConfig(t, s1, confHub, fmt.Sprintf(tmpl1, "false")) 5036 // Restart s2 5037 s2, _ = RunServerWithConfig(confSpoke) 5038 defer s2.Shutdown() 5039 5040 select { 5041 case err := <-l.errCh: 5042 if !strings.Contains(err, "handshake error") { 5043 t.Fatalf("Unexpected error: %v", err) 5044 } 5045 case <-time.After(2 * time.Second): 5046 t.Fatal("Did not get TLS handshake failure") 5047 } 5048 5049 // Reload again with "true" 5050 reloadUpdateConfig(t, s1, confHub, fmt.Sprintf(tmpl1, "true")) 5051 checkLeafNodeConnected(t, s2) 5052 } 5053 5054 func TestLeafNodeTLSHandshakeEvenForRemoteWithNoTLSBlock(t *testing.T) { 5055 confHub := createConfFile(t, []byte(` 5056 port : -1 5057 leafnodes : { 5058 port : -1 5059 tls { 5060 cert_file: "../test/configs/certs/server-cert.pem" 5061 key_file: "../test/configs/certs/server-key.pem" 5062 ca_file: "../test/configs/certs/ca.pem" 5063 timeout: 2 5064 } 5065 } 5066 `)) 5067 s1, o1 := RunServerWithConfig(confHub) 5068 defer s1.Shutdown() 5069 5070 tmpl2 := ` 5071 port: -1 5072 leafnodes : { 5073 port : -1 5074 remotes : [ 5075 { 5076 urls : [tls://127.0.0.1:%d] 5077 } 5078 ] 5079 } 5080 ` 5081 confSpoke := createConfFile(t, []byte(fmt.Sprintf(tmpl2, o1.LeafNode.Port))) 5082 s2, _ := RunServerWithConfig(confSpoke) 5083 defer s2.Shutdown() 5084 5085 l := &captureDebugLogger{dbgCh: make(chan string, 100)} 5086 s2.SetLogger(l, true, false) 5087 5088 tm := time.NewTimer(2 * time.Second) 5089 defer tm.Stop() 5090 for { 5091 select { 5092 case l := <-l.dbgCh: 5093 if strings.Contains(l, "Starting TLS") { 5094 // OK! 5095 return 5096 } 5097 case <-tm.C: 5098 t.Fatalf("Did not perform a TLS handshake") 5099 } 5100 } 5101 } 5102 5103 func TestLeafNodeCompressionOptions(t *testing.T) { 5104 org := testDefaultLeafNodeCompression 5105 testDefaultLeafNodeCompression = _EMPTY_ 5106 defer func() { testDefaultLeafNodeCompression = org }() 5107 5108 tmpl := ` 5109 port: -1 5110 leafnodes { 5111 port: -1 5112 compression: %s 5113 } 5114 ` 5115 for _, test := range []struct { 5116 name string 5117 mode string 5118 rttVals []int 5119 expected string 5120 rtts []time.Duration 5121 }{ 5122 {"boolean enabled", "true", nil, CompressionS2Auto, defaultCompressionS2AutoRTTThresholds}, 5123 {"string enabled", "enabled", nil, CompressionS2Auto, defaultCompressionS2AutoRTTThresholds}, 5124 {"string EnaBled", "EnaBled", nil, CompressionS2Auto, defaultCompressionS2AutoRTTThresholds}, 5125 {"string on", "on", nil, CompressionS2Auto, defaultCompressionS2AutoRTTThresholds}, 5126 {"string ON", "ON", nil, CompressionS2Auto, defaultCompressionS2AutoRTTThresholds}, 5127 {"string fast", "fast", nil, CompressionS2Fast, nil}, 5128 {"string Fast", "Fast", nil, CompressionS2Fast, nil}, 5129 {"string s2_fast", "s2_fast", nil, CompressionS2Fast, nil}, 5130 {"string s2_Fast", "s2_Fast", nil, CompressionS2Fast, nil}, 5131 {"boolean disabled", "false", nil, CompressionOff, nil}, 5132 {"string disabled", "disabled", nil, CompressionOff, nil}, 5133 {"string DisableD", "DisableD", nil, CompressionOff, nil}, 5134 {"string off", "off", nil, CompressionOff, nil}, 5135 {"string OFF", "OFF", nil, CompressionOff, nil}, 5136 {"better", "better", nil, CompressionS2Better, nil}, 5137 {"Better", "Better", nil, CompressionS2Better, nil}, 5138 {"s2_better", "s2_better", nil, CompressionS2Better, nil}, 5139 {"S2_BETTER", "S2_BETTER", nil, CompressionS2Better, nil}, 5140 {"best", "best", nil, CompressionS2Best, nil}, 5141 {"BEST", "BEST", nil, CompressionS2Best, nil}, 5142 {"s2_best", "s2_best", nil, CompressionS2Best, nil}, 5143 {"S2_BEST", "S2_BEST", nil, CompressionS2Best, nil}, 5144 {"auto no rtts", "auto", nil, CompressionS2Auto, defaultCompressionS2AutoRTTThresholds}, 5145 {"s2_auto no rtts", "s2_auto", nil, CompressionS2Auto, defaultCompressionS2AutoRTTThresholds}, 5146 {"auto", "{mode: auto, rtt_thresholds: [%s]}", []int{1}, CompressionS2Auto, []time.Duration{time.Millisecond}}, 5147 {"Auto", "{Mode: Auto, thresholds: [%s]}", []int{1, 2}, CompressionS2Auto, []time.Duration{time.Millisecond, 2 * time.Millisecond}}, 5148 {"s2_auto", "{mode: s2_auto, thresholds: [%s]}", []int{1, 2, 3}, CompressionS2Auto, []time.Duration{time.Millisecond, 2 * time.Millisecond, 3 * time.Millisecond}}, 5149 {"s2_AUTO", "{mode: s2_AUTO, thresholds: [%s]}", []int{1, 2, 3, 4}, CompressionS2Auto, []time.Duration{time.Millisecond, 2 * time.Millisecond, 3 * time.Millisecond, 4 * time.Millisecond}}, 5150 {"s2_auto:-10,5,10", "{mode: s2_auto, thresholds: [%s]}", []int{-10, 5, 10}, CompressionS2Auto, []time.Duration{0, 5 * time.Millisecond, 10 * time.Millisecond}}, 5151 {"s2_auto:5,10,15", "{mode: s2_auto, thresholds: [%s]}", []int{5, 10, 15}, CompressionS2Auto, []time.Duration{5 * time.Millisecond, 10 * time.Millisecond, 15 * time.Millisecond}}, 5152 {"s2_auto:0,5,10", "{mode: s2_auto, thresholds: [%s]}", []int{0, 5, 10}, CompressionS2Auto, []time.Duration{0, 5 * time.Millisecond, 10 * time.Millisecond}}, 5153 {"s2_auto:5,10,0,20", "{mode: s2_auto, thresholds: [%s]}", []int{5, 10, 0, 20}, CompressionS2Auto, []time.Duration{5 * time.Millisecond, 10 * time.Millisecond, 0, 20 * time.Millisecond}}, 5154 {"s2_auto:0,10,0,20", "{mode: s2_auto, thresholds: [%s]}", []int{0, 10, 0, 20}, CompressionS2Auto, []time.Duration{0, 10 * time.Millisecond, 0, 20 * time.Millisecond}}, 5155 {"s2_auto:0,0,0,20", "{mode: s2_auto, thresholds: [%s]}", []int{0, 0, 0, 20}, CompressionS2Auto, []time.Duration{0, 0, 0, 20 * time.Millisecond}}, 5156 {"s2_auto:0,10,0,0", "{mode: s2_auto, rtt_thresholds: [%s]}", []int{0, 10, 0, 0}, CompressionS2Auto, []time.Duration{0, 10 * time.Millisecond}}, 5157 } { 5158 t.Run(test.name, func(t *testing.T) { 5159 var val string 5160 if len(test.rttVals) > 0 { 5161 var rtts string 5162 for i, v := range test.rttVals { 5163 if i > 0 { 5164 rtts += ", " 5165 } 5166 rtts += fmt.Sprintf("%dms", v) 5167 } 5168 val = fmt.Sprintf(test.mode, rtts) 5169 } else { 5170 val = test.mode 5171 } 5172 conf := createConfFile(t, []byte(fmt.Sprintf(tmpl, val))) 5173 s, o := RunServerWithConfig(conf) 5174 defer s.Shutdown() 5175 5176 if cm := o.LeafNode.Compression.Mode; cm != test.expected { 5177 t.Fatalf("Expected compression value to be %q, got %q", test.expected, cm) 5178 } 5179 if !reflect.DeepEqual(test.rtts, o.LeafNode.Compression.RTTThresholds) { 5180 t.Fatalf("Expected RTT tresholds to be %+v, got %+v", test.rtts, o.LeafNode.Compression.RTTThresholds) 5181 } 5182 s.Shutdown() 5183 5184 o.LeafNode.Port = -1 5185 o.LeafNode.Compression.Mode = test.mode 5186 if len(test.rttVals) > 0 { 5187 o.LeafNode.Compression.Mode = CompressionS2Auto 5188 o.LeafNode.Compression.RTTThresholds = o.LeafNode.Compression.RTTThresholds[:0] 5189 for _, v := range test.rttVals { 5190 o.LeafNode.Compression.RTTThresholds = append(o.LeafNode.Compression.RTTThresholds, time.Duration(v)*time.Millisecond) 5191 } 5192 } 5193 s = RunServer(o) 5194 defer s.Shutdown() 5195 if cm := o.LeafNode.Compression.Mode; cm != test.expected { 5196 t.Fatalf("Expected compression value to be %q, got %q", test.expected, cm) 5197 } 5198 if !reflect.DeepEqual(test.rtts, o.LeafNode.Compression.RTTThresholds) { 5199 t.Fatalf("Expected RTT tresholds to be %+v, got %+v", test.rtts, o.LeafNode.Compression.RTTThresholds) 5200 } 5201 }) 5202 } 5203 5204 // Same, but with remotes 5205 tmpl = ` 5206 port: -1 5207 leafnodes { 5208 port: -1 5209 remotes [ 5210 { 5211 url: "nats://127.0.0.1:1234" 5212 compression: %s 5213 } 5214 ] 5215 } 5216 ` 5217 for _, test := range []struct { 5218 name string 5219 mode string 5220 rttVals []int 5221 expected string 5222 rtts []time.Duration 5223 }{ 5224 {"boolean enabled", "true", nil, CompressionS2Auto, defaultCompressionS2AutoRTTThresholds}, 5225 {"string enabled", "enabled", nil, CompressionS2Auto, defaultCompressionS2AutoRTTThresholds}, 5226 {"string EnaBled", "EnaBled", nil, CompressionS2Auto, defaultCompressionS2AutoRTTThresholds}, 5227 {"string on", "on", nil, CompressionS2Auto, defaultCompressionS2AutoRTTThresholds}, 5228 {"string ON", "ON", nil, CompressionS2Auto, defaultCompressionS2AutoRTTThresholds}, 5229 {"string fast", "fast", nil, CompressionS2Fast, nil}, 5230 {"string Fast", "Fast", nil, CompressionS2Fast, nil}, 5231 {"string s2_fast", "s2_fast", nil, CompressionS2Fast, nil}, 5232 {"string s2_Fast", "s2_Fast", nil, CompressionS2Fast, nil}, 5233 {"boolean disabled", "false", nil, CompressionOff, nil}, 5234 {"string disabled", "disabled", nil, CompressionOff, nil}, 5235 {"string DisableD", "DisableD", nil, CompressionOff, nil}, 5236 {"string off", "off", nil, CompressionOff, nil}, 5237 {"string OFF", "OFF", nil, CompressionOff, nil}, 5238 {"better", "better", nil, CompressionS2Better, nil}, 5239 {"Better", "Better", nil, CompressionS2Better, nil}, 5240 {"s2_better", "s2_better", nil, CompressionS2Better, nil}, 5241 {"S2_BETTER", "S2_BETTER", nil, CompressionS2Better, nil}, 5242 {"best", "best", nil, CompressionS2Best, nil}, 5243 {"BEST", "BEST", nil, CompressionS2Best, nil}, 5244 {"s2_best", "s2_best", nil, CompressionS2Best, nil}, 5245 {"S2_BEST", "S2_BEST", nil, CompressionS2Best, nil}, 5246 {"auto no rtts", "auto", nil, CompressionS2Auto, defaultCompressionS2AutoRTTThresholds}, 5247 {"s2_auto no rtts", "s2_auto", nil, CompressionS2Auto, defaultCompressionS2AutoRTTThresholds}, 5248 {"auto", "{mode: auto, rtt_thresholds: [%s]}", []int{1}, CompressionS2Auto, []time.Duration{time.Millisecond}}, 5249 {"Auto", "{Mode: Auto, thresholds: [%s]}", []int{1, 2}, CompressionS2Auto, []time.Duration{time.Millisecond, 2 * time.Millisecond}}, 5250 {"s2_auto", "{mode: s2_auto, thresholds: [%s]}", []int{1, 2, 3}, CompressionS2Auto, []time.Duration{time.Millisecond, 2 * time.Millisecond, 3 * time.Millisecond}}, 5251 {"s2_AUTO", "{mode: s2_AUTO, thresholds: [%s]}", []int{1, 2, 3, 4}, CompressionS2Auto, []time.Duration{time.Millisecond, 2 * time.Millisecond, 3 * time.Millisecond, 4 * time.Millisecond}}, 5252 {"s2_auto:-10,5,10", "{mode: s2_auto, thresholds: [%s]}", []int{-10, 5, 10}, CompressionS2Auto, []time.Duration{0, 5 * time.Millisecond, 10 * time.Millisecond}}, 5253 {"s2_auto:5,10,15", "{mode: s2_auto, thresholds: [%s]}", []int{5, 10, 15}, CompressionS2Auto, []time.Duration{5 * time.Millisecond, 10 * time.Millisecond, 15 * time.Millisecond}}, 5254 {"s2_auto:0,5,10", "{mode: s2_auto, thresholds: [%s]}", []int{0, 5, 10}, CompressionS2Auto, []time.Duration{0, 5 * time.Millisecond, 10 * time.Millisecond}}, 5255 {"s2_auto:5,10,0,20", "{mode: s2_auto, thresholds: [%s]}", []int{5, 10, 0, 20}, CompressionS2Auto, []time.Duration{5 * time.Millisecond, 10 * time.Millisecond, 0, 20 * time.Millisecond}}, 5256 {"s2_auto:0,10,0,20", "{mode: s2_auto, thresholds: [%s]}", []int{0, 10, 0, 20}, CompressionS2Auto, []time.Duration{0, 10 * time.Millisecond, 0, 20 * time.Millisecond}}, 5257 {"s2_auto:0,0,0,20", "{mode: s2_auto, thresholds: [%s]}", []int{0, 0, 0, 20}, CompressionS2Auto, []time.Duration{0, 0, 0, 20 * time.Millisecond}}, 5258 {"s2_auto:0,10,0,0", "{mode: s2_auto, rtt_thresholds: [%s]}", []int{0, 10, 0, 0}, CompressionS2Auto, []time.Duration{0, 10 * time.Millisecond}}, 5259 } { 5260 t.Run("remote leaf "+test.name, func(t *testing.T) { 5261 var val string 5262 if len(test.rttVals) > 0 { 5263 var rtts string 5264 for i, v := range test.rttVals { 5265 if i > 0 { 5266 rtts += ", " 5267 } 5268 rtts += fmt.Sprintf("%dms", v) 5269 } 5270 val = fmt.Sprintf(test.mode, rtts) 5271 } else { 5272 val = test.mode 5273 } 5274 conf := createConfFile(t, []byte(fmt.Sprintf(tmpl, val))) 5275 s, o := RunServerWithConfig(conf) 5276 defer s.Shutdown() 5277 5278 r := o.LeafNode.Remotes[0] 5279 5280 if cm := r.Compression.Mode; cm != test.expected { 5281 t.Fatalf("Expected compression value to be %q, got %q", test.expected, cm) 5282 } 5283 if !reflect.DeepEqual(test.rtts, r.Compression.RTTThresholds) { 5284 t.Fatalf("Expected RTT tresholds to be %+v, got %+v", test.rtts, r.Compression.RTTThresholds) 5285 } 5286 s.Shutdown() 5287 5288 o.LeafNode.Port = -1 5289 o.LeafNode.Remotes[0].Compression.Mode = test.mode 5290 if len(test.rttVals) > 0 { 5291 o.LeafNode.Remotes[0].Compression.Mode = CompressionS2Auto 5292 o.LeafNode.Remotes[0].Compression.RTTThresholds = o.LeafNode.Remotes[0].Compression.RTTThresholds[:0] 5293 for _, v := range test.rttVals { 5294 o.LeafNode.Remotes[0].Compression.RTTThresholds = append(o.LeafNode.Remotes[0].Compression.RTTThresholds, time.Duration(v)*time.Millisecond) 5295 } 5296 } 5297 s = RunServer(o) 5298 defer s.Shutdown() 5299 if cm := o.LeafNode.Remotes[0].Compression.Mode; cm != test.expected { 5300 t.Fatalf("Expected compression value to be %q, got %q", test.expected, cm) 5301 } 5302 if !reflect.DeepEqual(test.rtts, o.LeafNode.Remotes[0].Compression.RTTThresholds) { 5303 t.Fatalf("Expected RTT tresholds to be %+v, got %+v", test.rtts, o.LeafNode.Remotes[0].Compression.RTTThresholds) 5304 } 5305 }) 5306 } 5307 5308 // Test that with no compression specified, we default to "s2_auto" 5309 conf := createConfFile(t, []byte(` 5310 port: -1 5311 leafnodes { 5312 port: -1 5313 } 5314 `)) 5315 s, o := RunServerWithConfig(conf) 5316 defer s.Shutdown() 5317 if o.LeafNode.Compression.Mode != CompressionS2Auto { 5318 t.Fatalf("Expected compression value to be %q, got %q", CompressionAccept, o.LeafNode.Compression.Mode) 5319 } 5320 if !reflect.DeepEqual(defaultCompressionS2AutoRTTThresholds, o.LeafNode.Compression.RTTThresholds) { 5321 t.Fatalf("Expected RTT tresholds to be %+v, got %+v", defaultCompressionS2AutoRTTThresholds, o.LeafNode.Compression.RTTThresholds) 5322 } 5323 // Same for remotes 5324 conf = createConfFile(t, []byte(` 5325 port: -1 5326 leafnodes { 5327 port: -1 5328 remotes [ { url: "nats://127.0.0.1:1234" } ] 5329 } 5330 `)) 5331 s, o = RunServerWithConfig(conf) 5332 defer s.Shutdown() 5333 if cm := o.LeafNode.Remotes[0].Compression.Mode; cm != CompressionS2Auto { 5334 t.Fatalf("Expected compression value to be %q, got %q", CompressionAccept, cm) 5335 } 5336 if !reflect.DeepEqual(defaultCompressionS2AutoRTTThresholds, o.LeafNode.Remotes[0].Compression.RTTThresholds) { 5337 t.Fatalf("Expected RTT tresholds to be %+v, got %+v", defaultCompressionS2AutoRTTThresholds, o.LeafNode.Remotes[0].Compression.RTTThresholds) 5338 } 5339 for _, test := range []struct { 5340 name string 5341 mode string 5342 rtts []time.Duration 5343 err string 5344 }{ 5345 {"unsupported mode", "gzip", nil, "unsupported"}, 5346 {"not ascending order", "s2_auto", []time.Duration{ 5347 5 * time.Millisecond, 5348 10 * time.Millisecond, 5349 2 * time.Millisecond, 5350 }, "ascending"}, 5351 {"too many thresholds", "s2_auto", []time.Duration{ 5352 5 * time.Millisecond, 5353 10 * time.Millisecond, 5354 20 * time.Millisecond, 5355 40 * time.Millisecond, 5356 60 * time.Millisecond, 5357 }, "more than 4"}, 5358 {"all 0", "s2_auto", []time.Duration{0, 0, 0, 0}, "at least one"}, 5359 {"single 0", "s2_auto", []time.Duration{0}, "at least one"}, 5360 } { 5361 t.Run(test.name, func(t *testing.T) { 5362 o := DefaultOptions() 5363 o.LeafNode.Port = -1 5364 o.LeafNode.Compression = CompressionOpts{test.mode, test.rtts} 5365 if _, err := NewServer(o); err == nil || !strings.Contains(err.Error(), test.err) { 5366 t.Fatalf("Unexpected error: %v", err) 5367 } 5368 // Same with remotes 5369 o.LeafNode.Compression = CompressionOpts{} 5370 o.LeafNode.Remotes = []*RemoteLeafOpts{{Compression: CompressionOpts{test.mode, test.rtts}}} 5371 if _, err := NewServer(o); err == nil || !strings.Contains(err.Error(), test.err) { 5372 t.Fatalf("Unexpected error: %v", err) 5373 } 5374 }) 5375 } 5376 } 5377 5378 func TestLeafNodeCompression(t *testing.T) { 5379 conf1 := createConfFile(t, []byte(` 5380 port: -1 5381 server_name: "Hub" 5382 accounts { 5383 A { users: [{user: a, password: pwd}] } 5384 B { users: [{user: b, password: pwd}] } 5385 C { users: [{user: c, password: pwd}] } 5386 } 5387 leafnodes { 5388 port: -1 5389 compression: s2_fast 5390 } 5391 `)) 5392 s1, o1 := RunServerWithConfig(conf1) 5393 defer s1.Shutdown() 5394 5395 port := o1.LeafNode.Port 5396 conf2 := createConfFile(t, []byte(fmt.Sprintf(` 5397 port: -1 5398 server_name: "Spoke" 5399 accounts { 5400 A { users: [{user: a, password: pwd}] } 5401 B { users: [{user: b, password: pwd}] } 5402 C { users: [{user: c, password: pwd}] } 5403 } 5404 leafnodes { 5405 remotes [ 5406 { url: "nats://a:pwd@127.0.0.1:%d", account: "A", compression: s2_better } 5407 { url: "nats://b:pwd@127.0.0.1:%d", account: "B", compression: s2_best } 5408 { url: "nats://c:pwd@127.0.0.1:%d", account: "C", compression: off } 5409 ] 5410 } 5411 `, port, port, port))) 5412 s2, _ := RunServerWithConfig(conf2) 5413 defer s2.Shutdown() 5414 5415 checkLeafNodeConnectedCount(t, s1, 3) 5416 checkLeafNodeConnectedCount(t, s2, 3) 5417 5418 s1.mu.RLock() 5419 for _, l := range s1.leafs { 5420 l.mu.Lock() 5421 l.nc = &testConnSentBytes{Conn: l.nc} 5422 l.mu.Unlock() 5423 } 5424 s1.mu.RUnlock() 5425 5426 var payloads [][]byte 5427 totalPayloadSize := 0 5428 count := 26 5429 for i := 0; i < count; i++ { 5430 n := rand.Intn(2048) + 1 5431 p := make([]byte, n) 5432 for j := 0; j < n; j++ { 5433 p[j] = byte(i) + 'A' 5434 } 5435 totalPayloadSize += len(p) 5436 payloads = append(payloads, p) 5437 } 5438 5439 check := func(acc, user, subj string) { 5440 t.Helper() 5441 nc2 := natsConnect(t, s2.ClientURL(), nats.UserInfo(user, "pwd")) 5442 defer nc2.Close() 5443 sub := natsSubSync(t, nc2, subj) 5444 natsFlush(t, nc2) 5445 checkSubInterest(t, s1, acc, subj, time.Second) 5446 5447 nc1 := natsConnect(t, s1.ClientURL(), nats.UserInfo(user, "pwd")) 5448 defer nc1.Close() 5449 5450 for i := 0; i < count; i++ { 5451 natsPub(t, nc1, subj, payloads[i]) 5452 } 5453 for i := 0; i < count; i++ { 5454 m := natsNexMsg(t, sub, time.Second) 5455 if !bytes.Equal(m.Data, payloads[i]) { 5456 t.Fatalf("Expected payload %q - got %q", payloads[i], m.Data) 5457 } 5458 } 5459 5460 // Also check that the leafnode stats shows that compression likely occurred 5461 var out int 5462 s1.mu.RLock() 5463 for _, l := range s1.leafs { 5464 l.mu.Lock() 5465 if l.acc.Name == acc && l.nc != nil { 5466 nc := l.nc.(*testConnSentBytes) 5467 nc.Lock() 5468 out = nc.sent 5469 nc.sent = 0 5470 nc.Unlock() 5471 } 5472 l.mu.Unlock() 5473 } 5474 s1.mu.RUnlock() 5475 // Except for account "C", where compression should be off, 5476 // "out" should at least be smaller than totalPayloadSize, use 20%. 5477 if acc == "C" { 5478 if int(out) < totalPayloadSize { 5479 t.Fatalf("Expected s1's sent bytes to be at least payload size (%v), got %v", totalPayloadSize, out) 5480 } 5481 } else { 5482 limit := totalPayloadSize * 80 / 100 5483 if int(out) > limit { 5484 t.Fatalf("Expected s1's sent bytes to be less than %v, got %v (total payload was %v)", limit, out, totalPayloadSize) 5485 } 5486 } 5487 } 5488 check("A", "a", "foo") 5489 check("B", "b", "bar") 5490 check("C", "c", "baz") 5491 5492 // Check compression settings. S1 should always be s2_fast, except for account "C" 5493 // since "C" wanted compression "off" 5494 l, err := s1.Leafz(nil) 5495 require_NoError(t, err) 5496 for _, r := range l.Leafs { 5497 switch r.Account { 5498 case "C": 5499 if r.Compression != CompressionOff { 5500 t.Fatalf("Expected compression of remote for C account to be %q, got %q", CompressionOff, r.Compression) 5501 } 5502 default: 5503 if r.Compression != CompressionS2Fast { 5504 t.Fatalf("Expected compression of remote for %s account to be %q, got %q", r.Account, CompressionS2Fast, r.Compression) 5505 } 5506 } 5507 } 5508 5509 l, err = s2.Leafz(nil) 5510 require_NoError(t, err) 5511 for _, r := range l.Leafs { 5512 switch r.Account { 5513 case "A": 5514 if r.Compression != CompressionS2Better { 5515 t.Fatalf("Expected compression for A account to be %q, got %q", CompressionS2Better, r.Compression) 5516 } 5517 case "B": 5518 if r.Compression != CompressionS2Best { 5519 t.Fatalf("Expected compression for B account to be %q, got %q", CompressionS2Best, r.Compression) 5520 } 5521 case "C": 5522 if r.Compression != CompressionOff { 5523 t.Fatalf("Expected compression for C account to be %q, got %q", CompressionOff, r.Compression) 5524 } 5525 } 5526 } 5527 } 5528 5529 func BenchmarkLeafNodeCompression(b *testing.B) { 5530 conf1 := createConfFile(b, []byte(` 5531 port: -1 5532 server_name: "Hub" 5533 accounts { 5534 A { users: [{user: a, password: pwd}] } 5535 B { users: [{user: b, password: pwd}] } 5536 C { users: [{user: c, password: pwd}] } 5537 D { users: [{user: d, password: pwd}] } 5538 } 5539 leafnodes { 5540 port: -1 5541 } 5542 `)) 5543 s1, o1 := RunServerWithConfig(conf1) 5544 defer s1.Shutdown() 5545 5546 port := o1.LeafNode.Port 5547 conf2 := createConfFile(b, []byte(fmt.Sprintf(` 5548 port: -1 5549 server_name: "Spoke" 5550 accounts { 5551 A { users: [{user: a, password: pwd}] } 5552 B { users: [{user: b, password: pwd}] } 5553 C { users: [{user: c, password: pwd}] } 5554 D { users: [{user: d, password: pwd}] } 5555 } 5556 leafnodes { 5557 remotes [ 5558 { url: "nats://a:pwd@127.0.0.1:%d", account: "A", compression: s2_better } 5559 { url: "nats://b:pwd@127.0.0.1:%d", account: "B", compression: s2_best } 5560 { url: "nats://c:pwd@127.0.0.1:%d", account: "C", compression: s2_fast } 5561 { url: "nats://d:pwd@127.0.0.1:%d", account: "D", compression: off } 5562 ] 5563 } 5564 `, port, port, port, port))) 5565 s2, _ := RunServerWithConfig(conf2) 5566 defer s2.Shutdown() 5567 5568 checkLeafNodeConnectedCount(b, s1, 4) 5569 checkLeafNodeConnectedCount(b, s2, 4) 5570 5571 l, err := s2.Leafz(nil) 5572 require_NoError(b, err) 5573 for _, r := range l.Leafs { 5574 switch { 5575 case r.Account == "A" && r.Compression == CompressionS2Better: 5576 case r.Account == "B" && r.Compression == CompressionS2Best: 5577 case r.Account == "C" && r.Compression == CompressionS2Fast: 5578 case r.Account == "D" && r.Compression == CompressionOff: 5579 default: 5580 b.Fatalf("Account %q had incorrect compression mode %q on leaf connection", r.Account, r.Compression) 5581 } 5582 } 5583 5584 msg := make([]byte, 1024) 5585 for _, p := range []struct { 5586 algo string 5587 user string 5588 }{ 5589 {"Better", "a"}, 5590 {"Best", "b"}, 5591 {"Fast", "c"}, 5592 {"Off", "d"}, 5593 } { 5594 nc1 := natsConnect(b, s1.ClientURL(), nats.UserInfo(p.user, "pwd")) 5595 nc2 := natsConnect(b, s2.ClientURL(), nats.UserInfo(p.user, "pwd")) 5596 5597 sub, err := nc1.SubscribeSync("foo") 5598 require_NoError(b, err) 5599 5600 time.Sleep(time.Second) 5601 5602 b.Run(p.algo, func(b *testing.B) { 5603 start := time.Now() 5604 5605 for i := 0; i < b.N; i++ { 5606 err = nc2.Publish("foo", msg) 5607 require_NoError(b, err) 5608 5609 _, err = sub.NextMsg(time.Second) 5610 require_NoError(b, err) 5611 } 5612 5613 b.ReportMetric(float64(len(msg)*b.N)/1024/1024, "MB") 5614 b.ReportMetric(float64(len(msg)*b.N)/1024/1024/float64(time.Since(start).Seconds()), "MB/sec") 5615 }) 5616 5617 nc1.Close() 5618 nc2.Close() 5619 } 5620 } 5621 5622 func TestLeafNodeCompressionMatrixModes(t *testing.T) { 5623 for _, test := range []struct { 5624 name string 5625 s1 string 5626 s2 string 5627 s1Expected string 5628 s2Expected string 5629 }{ 5630 {"off off", "off", "off", CompressionOff, CompressionOff}, 5631 {"off accept", "off", "accept", CompressionOff, CompressionOff}, 5632 {"off on", "off", "on", CompressionOff, CompressionOff}, 5633 {"off better", "off", "better", CompressionOff, CompressionOff}, 5634 {"off best", "off", "best", CompressionOff, CompressionOff}, 5635 5636 {"accept off", "accept", "off", CompressionOff, CompressionOff}, 5637 {"accept accept", "accept", "accept", CompressionOff, CompressionOff}, 5638 // Note: "on", means s2_auto, which will mean uncompressed since RTT is low. 5639 {"accept on", "accept", "on", CompressionS2Fast, CompressionS2Uncompressed}, 5640 {"accept better", "accept", "better", CompressionS2Better, CompressionS2Better}, 5641 {"accept best", "accept", "best", CompressionS2Best, CompressionS2Best}, 5642 5643 {"on off", "on", "off", CompressionOff, CompressionOff}, 5644 {"on accept", "on", "accept", CompressionS2Uncompressed, CompressionS2Fast}, 5645 {"on on", "on", "on", CompressionS2Uncompressed, CompressionS2Uncompressed}, 5646 {"on better", "on", "better", CompressionS2Uncompressed, CompressionS2Better}, 5647 {"on best", "on", "best", CompressionS2Uncompressed, CompressionS2Best}, 5648 5649 {"better off", "better", "off", CompressionOff, CompressionOff}, 5650 {"better accept", "better", "accept", CompressionS2Better, CompressionS2Better}, 5651 {"better on", "better", "on", CompressionS2Better, CompressionS2Uncompressed}, 5652 {"better better", "better", "better", CompressionS2Better, CompressionS2Better}, 5653 {"better best", "better", "best", CompressionS2Better, CompressionS2Best}, 5654 5655 {"best off", "best", "off", CompressionOff, CompressionOff}, 5656 {"best accept", "best", "accept", CompressionS2Best, CompressionS2Best}, 5657 {"best on", "best", "on", CompressionS2Best, CompressionS2Uncompressed}, 5658 {"best better", "best", "better", CompressionS2Best, CompressionS2Better}, 5659 {"best best", "best", "best", CompressionS2Best, CompressionS2Best}, 5660 } { 5661 t.Run(test.name, func(t *testing.T) { 5662 conf1 := createConfFile(t, []byte(fmt.Sprintf(` 5663 port: -1 5664 server_name: "A" 5665 leafnodes { 5666 port: -1 5667 compression: %s 5668 } 5669 `, test.s1))) 5670 s1, o1 := RunServerWithConfig(conf1) 5671 defer s1.Shutdown() 5672 5673 conf2 := createConfFile(t, []byte(fmt.Sprintf(` 5674 port: -1 5675 server_name: "B" 5676 leafnodes { 5677 remotes: [ 5678 {url: "nats://127.0.0.1:%d", compression: %s} 5679 ] 5680 } 5681 `, o1.LeafNode.Port, test.s2))) 5682 s2, _ := RunServerWithConfig(conf2) 5683 defer s2.Shutdown() 5684 5685 checkLeafNodeConnected(t, s2) 5686 5687 nc1 := natsConnect(t, s1.ClientURL()) 5688 defer nc1.Close() 5689 5690 nc2 := natsConnect(t, s2.ClientURL()) 5691 defer nc2.Close() 5692 5693 payload := make([]byte, 128) 5694 check := func(ncp, ncs *nats.Conn, subj string, s *Server) { 5695 t.Helper() 5696 sub := natsSubSync(t, ncs, subj) 5697 checkSubInterest(t, s, globalAccountName, subj, time.Second) 5698 natsPub(t, ncp, subj, payload) 5699 natsNexMsg(t, sub, time.Second) 5700 5701 for _, srv := range []*Server{s1, s2} { 5702 lz, err := srv.Leafz(nil) 5703 require_NoError(t, err) 5704 var expected string 5705 if srv == s1 { 5706 expected = test.s1Expected 5707 } else { 5708 expected = test.s2Expected 5709 } 5710 if cm := lz.Leafs[0].Compression; cm != expected { 5711 t.Fatalf("Server %s - expected compression %q, got %q", srv, expected, cm) 5712 } 5713 } 5714 } 5715 check(nc1, nc2, "foo", s1) 5716 check(nc2, nc1, "bar", s2) 5717 }) 5718 } 5719 } 5720 5721 func TestLeafNodeCompressionWithOlderServer(t *testing.T) { 5722 tmpl1 := ` 5723 port: -1 5724 server_name: "A" 5725 leafnodes { 5726 port: -1 5727 compression: "%s" 5728 } 5729 ` 5730 conf1 := createConfFile(t, []byte(fmt.Sprintf(tmpl1, CompressionS2Fast))) 5731 s1, o1 := RunServerWithConfig(conf1) 5732 defer s1.Shutdown() 5733 5734 tmpl2 := ` 5735 port: -1 5736 server_name: "B" 5737 leafnodes { 5738 remotes [ 5739 {url: "nats://127.0.0.1:%d", compression: "%s"} 5740 ] 5741 } 5742 ` 5743 conf2 := createConfFile(t, []byte(fmt.Sprintf(tmpl2, o1.LeafNode.Port, CompressionNotSupported))) 5744 s2, _ := RunServerWithConfig(conf2) 5745 defer s2.Shutdown() 5746 5747 checkLeafNodeConnected(t, s2) 5748 5749 getLeafCompMode := func(s *Server) string { 5750 var cm string 5751 s.mu.RLock() 5752 defer s.mu.RUnlock() 5753 for _, l := range s1.leafs { 5754 l.mu.Lock() 5755 cm = l.leaf.compression 5756 l.mu.Unlock() 5757 return cm 5758 } 5759 return _EMPTY_ 5760 } 5761 for _, s := range []*Server{s1, s2} { 5762 if cm := getLeafCompMode(s); cm != CompressionNotSupported { 5763 t.Fatalf("Expected compression not supported, got %q", cm) 5764 } 5765 } 5766 5767 s2.Shutdown() 5768 s1.Shutdown() 5769 5770 conf1 = createConfFile(t, []byte(fmt.Sprintf(tmpl1, CompressionNotSupported))) 5771 s1, o1 = RunServerWithConfig(conf1) 5772 defer s1.Shutdown() 5773 5774 conf2 = createConfFile(t, []byte(fmt.Sprintf(tmpl2, o1.LeafNode.Port, CompressionS2Fast))) 5775 s2, _ = RunServerWithConfig(conf2) 5776 defer s2.Shutdown() 5777 5778 checkLeafNodeConnected(t, s2) 5779 for _, s := range []*Server{s1, s2} { 5780 if cm := getLeafCompMode(s); cm != CompressionNotSupported { 5781 t.Fatalf("Expected compression not supported, got %q", cm) 5782 } 5783 } 5784 } 5785 5786 func TestLeafNodeCompressionAuto(t *testing.T) { 5787 for _, test := range []struct { 5788 name string 5789 s1Ping string 5790 s1Compression string 5791 s2Ping string 5792 s2Compression string 5793 checkS1 bool 5794 }{ 5795 {"remote side", "10s", CompressionS2Fast, "100ms", "{mode: s2_auto, rtt_thresholds: [10ms, 20ms, 30ms]}", false}, 5796 {"accept side", "100ms", "{mode: s2_auto, rtt_thresholds: [10ms, 20ms, 30ms]}", "10s", CompressionS2Fast, true}, 5797 } { 5798 t.Run(test.name, func(t *testing.T) { 5799 conf1 := createConfFile(t, []byte(fmt.Sprintf(` 5800 port: -1 5801 server_name: "A" 5802 ping_interval: "%s" 5803 leafnodes { 5804 port: -1 5805 compression: %s 5806 } 5807 `, test.s1Ping, test.s1Compression))) 5808 s1, o1 := RunServerWithConfig(conf1) 5809 defer s1.Shutdown() 5810 5811 // Start with 0ms RTT 5812 np := createNetProxy(0, 1024*1024*1024, 1024*1024*1024, fmt.Sprintf("nats://127.0.0.1:%d", o1.LeafNode.Port), true) 5813 5814 conf2 := createConfFile(t, []byte(fmt.Sprintf(` 5815 port: -1 5816 server_name: "B" 5817 ping_interval: "%s" 5818 leafnodes { 5819 remotes [ 5820 {url: %s, compression %s} 5821 ] 5822 } 5823 `, test.s2Ping, np.routeURL(), test.s2Compression))) 5824 s2, _ := RunServerWithConfig(conf2) 5825 defer s2.Shutdown() 5826 defer np.stop() 5827 5828 checkLeafNodeConnected(t, s2) 5829 5830 checkComp := func(expected string) { 5831 t.Helper() 5832 var s *Server 5833 if test.checkS1 { 5834 s = s1 5835 } else { 5836 s = s2 5837 } 5838 checkFor(t, 2*time.Second, 15*time.Millisecond, func() error { 5839 s.mu.RLock() 5840 defer s.mu.RUnlock() 5841 for _, l := range s.leafs { 5842 l.mu.Lock() 5843 cm := l.leaf.compression 5844 l.mu.Unlock() 5845 if cm != expected { 5846 return fmt.Errorf("Leaf %v compression mode expected to be %q, got %q", l, expected, cm) 5847 } 5848 } 5849 return nil 5850 }) 5851 } 5852 checkComp(CompressionS2Uncompressed) 5853 5854 // Change the proxy RTT and we should get compression "fast" 5855 np.updateRTT(15 * time.Millisecond) 5856 checkComp(CompressionS2Fast) 5857 5858 // Now 25ms, and get "better" 5859 np.updateRTT(25 * time.Millisecond) 5860 checkComp(CompressionS2Better) 5861 5862 // Above 35 and we should get "best" 5863 np.updateRTT(35 * time.Millisecond) 5864 checkComp(CompressionS2Best) 5865 5866 // Down to 1ms and again should get "uncompressed" 5867 np.updateRTT(1 * time.Millisecond) 5868 checkComp(CompressionS2Uncompressed) 5869 }) 5870 } 5871 5872 // Make sure that if compression is off on one side, the update of RTT does 5873 // not trigger a compression change. 5874 conf1 := createConfFile(t, []byte(` 5875 port: -1 5876 server_name: "A" 5877 leafnodes { 5878 port: -1 5879 compression: off 5880 } 5881 `)) 5882 s1, o1 := RunServerWithConfig(conf1) 5883 defer s1.Shutdown() 5884 5885 // Start with 0ms RTT 5886 np := createNetProxy(0, 1024*1024*1024, 1024*1024*1024, fmt.Sprintf("nats://127.0.0.1:%d", o1.LeafNode.Port), true) 5887 5888 conf2 := createConfFile(t, []byte(fmt.Sprintf(` 5889 port: -1 5890 server_name: "B" 5891 ping_interval: "50ms" 5892 leafnodes { 5893 remotes [ 5894 {url: %s, compression s2_auto} 5895 ] 5896 } 5897 `, np.routeURL()))) 5898 s2, _ := RunServerWithConfig(conf2) 5899 defer s2.Shutdown() 5900 defer np.stop() 5901 5902 checkLeafNodeConnected(t, s2) 5903 5904 // Even with a bug of updating compression level while it should have been 5905 // off, the check done below would almost always pass because after 5906 // reconnecting, there could be a chance to get at first compression set 5907 // to "off". So we will double check that the leaf node CID did not change 5908 // at the end of the test. 5909 getCID := func() uint64 { 5910 s2.mu.RLock() 5911 defer s2.mu.RUnlock() 5912 for _, l := range s2.leafs { 5913 l.mu.Lock() 5914 cid := l.cid 5915 l.mu.Unlock() 5916 return cid 5917 } 5918 return 0 5919 } 5920 oldCID := getCID() 5921 5922 checkCompOff := func() { 5923 t.Helper() 5924 checkFor(t, 2*time.Second, 15*time.Millisecond, func() error { 5925 s2.mu.RLock() 5926 defer s2.mu.RUnlock() 5927 if len(s2.leafs) != 1 { 5928 return fmt.Errorf("Leaf not currently connected") 5929 } 5930 for _, l := range s2.leafs { 5931 l.mu.Lock() 5932 cm := l.leaf.compression 5933 l.mu.Unlock() 5934 if cm != CompressionOff { 5935 return fmt.Errorf("Leaf %v compression mode expected to be %q, got %q", l, CompressionOff, cm) 5936 } 5937 } 5938 return nil 5939 }) 5940 } 5941 checkCompOff() 5942 5943 // Now change RTT and again, make sure that it is still off 5944 np.updateRTT(20 * time.Millisecond) 5945 time.Sleep(100 * time.Millisecond) 5946 checkCompOff() 5947 if cid := getCID(); cid != oldCID { 5948 t.Fatalf("Leafnode has reconnected, cid was %v, now %v", oldCID, cid) 5949 } 5950 } 5951 5952 func TestLeafNodeCompressionWithWSCompression(t *testing.T) { 5953 conf1 := createConfFile(t, []byte(` 5954 port: -1 5955 server_name: "A" 5956 websocket { 5957 port: -1 5958 no_tls: true 5959 compression: true 5960 } 5961 leafnodes { 5962 port: -1 5963 compression: s2_fast 5964 } 5965 `)) 5966 s1, o1 := RunServerWithConfig(conf1) 5967 defer s1.Shutdown() 5968 5969 conf2 := createConfFile(t, []byte(fmt.Sprintf(` 5970 port: -1 5971 server_name: "B" 5972 leafnodes { 5973 remotes [ 5974 { 5975 url: "ws://127.0.0.1:%d" 5976 ws_compression: true 5977 compression: s2_fast 5978 } 5979 ] 5980 } 5981 `, o1.Websocket.Port))) 5982 s2, _ := RunServerWithConfig(conf2) 5983 defer s2.Shutdown() 5984 5985 checkLeafNodeConnected(t, s2) 5986 5987 nc1 := natsConnect(t, s1.ClientURL()) 5988 defer nc1.Close() 5989 5990 sub := natsSubSync(t, nc1, "foo") 5991 checkSubInterest(t, s2, globalAccountName, "foo", time.Second) 5992 5993 nc2 := natsConnect(t, s2.ClientURL()) 5994 defer nc2.Close() 5995 5996 payload := make([]byte, 1024) 5997 for i := 0; i < len(payload); i++ { 5998 payload[i] = 'A' 5999 } 6000 natsPub(t, nc2, "foo", payload) 6001 msg := natsNexMsg(t, sub, time.Second) 6002 require_True(t, len(msg.Data) == 1024) 6003 for i := 0; i < len(msg.Data); i++ { 6004 if msg.Data[i] != 'A' { 6005 t.Fatalf("Invalid msg: %s", msg.Data) 6006 } 6007 } 6008 } 6009 6010 func TestLeafNodeCompressionWithWSGetNeedsData(t *testing.T) { 6011 conf1 := createConfFile(t, []byte(` 6012 port: -1 6013 server_name: "A" 6014 websocket { 6015 port: -1 6016 no_tls: true 6017 } 6018 leafnodes { 6019 port: -1 6020 compression: s2_fast 6021 } 6022 `)) 6023 srv1, o1 := RunServerWithConfig(conf1) 6024 defer srv1.Shutdown() 6025 6026 conf2 := createConfFile(t, []byte(fmt.Sprintf(` 6027 port: -1 6028 server_name: "B" 6029 leafnodes { 6030 remotes [ 6031 { 6032 url: "ws://127.0.0.1:%d" 6033 ws_no_masking: true 6034 compression: s2_fast 6035 } 6036 ] 6037 } 6038 `, o1.Websocket.Port))) 6039 srv2, _ := RunServerWithConfig(conf2) 6040 defer srv2.Shutdown() 6041 6042 checkLeafNodeConnected(t, srv2) 6043 6044 nc1 := natsConnect(t, srv1.ClientURL()) 6045 defer nc1.Close() 6046 6047 sub := natsSubSync(t, nc1, "foo") 6048 checkSubInterest(t, srv2, globalAccountName, "foo", time.Second) 6049 6050 // We want to have the payload more than 126 bytes so that the websocket 6051 // code need to read 2 bytes for the length. See below. 6052 payload := "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ" 6053 sentBytes := []byte("LMSG foo 156\r\n" + payload + "\r\n") 6054 h, _ := wsCreateFrameHeader(false, false, wsBinaryMessage, len(sentBytes)) 6055 combined := &bytes.Buffer{} 6056 combined.Write(h) 6057 combined.Write(sentBytes) 6058 toSend := combined.Bytes() 6059 6060 // We will make a compressed block that cuts the websocket header that 6061 // makes the reader want to read bytes directly from the connection. 6062 // We want to make sure that we are not going to get compressed data 6063 // without going through the (de)compress library. So for that, compress 6064 // the first 3 bytes. 6065 b := &bytes.Buffer{} 6066 w := s2.NewWriter(b) 6067 w.Write(toSend[:3]) 6068 w.Close() 6069 6070 var nc net.Conn 6071 srv2.mu.RLock() 6072 for _, l := range srv2.leafs { 6073 l.mu.Lock() 6074 nc = l.nc 6075 l.mu.Unlock() 6076 } 6077 srv2.mu.RUnlock() 6078 6079 nc.Write(b.Bytes()) 6080 6081 // Pause to make sure other side just gets a partial of the whole WS frame. 6082 time.Sleep(100 * time.Millisecond) 6083 6084 b.Reset() 6085 w.Reset(b) 6086 w.Write(toSend[3:]) 6087 w.Close() 6088 6089 nc.Write(b.Bytes()) 6090 6091 msg := natsNexMsg(t, sub, time.Second) 6092 require_True(t, len(msg.Data) == 156) 6093 require_Equal(t, string(msg.Data), payload) 6094 } 6095 6096 func TestLeafNodeCompressionAuthTimeout(t *testing.T) { 6097 hconf := createConfFile(t, []byte(` 6098 port: -1 6099 server_name: "hub" 6100 leafnodes { 6101 port: -1 6102 authorization { 6103 timeout: 0.75 6104 } 6105 } 6106 `)) 6107 sh, oh := RunServerWithConfig(hconf) 6108 defer sh.Shutdown() 6109 6110 sconfTmpl := ` 6111 port: -1 6112 server_name: "%s" 6113 cluster { 6114 port: -1 6115 name: "spoke" 6116 %s 6117 } 6118 leafnodes { 6119 port: -1 6120 remotes [ 6121 { url: "nats://127.0.0.1:%d" } 6122 ] 6123 } 6124 ` 6125 s1conf := createConfFile(t, []byte(fmt.Sprintf(sconfTmpl, "SP1", _EMPTY_, oh.LeafNode.Port))) 6126 s1, o1 := RunServerWithConfig(s1conf) 6127 defer s1.Shutdown() 6128 6129 s2conf := createConfFile(t, []byte(fmt.Sprintf(sconfTmpl, "SP2", fmt.Sprintf("routes: [\"nats://127.0.0.1:%d\"]", o1.Cluster.Port), oh.LeafNode.Port))) 6130 s2, _ := RunServerWithConfig(s2conf) 6131 defer s2.Shutdown() 6132 6133 checkClusterFormed(t, s1, s2) 6134 6135 checkLeafNodeConnected(t, s1) 6136 checkLeafNodeConnected(t, s2) 6137 6138 getCID := func(s *Server) uint64 { 6139 s.mu.RLock() 6140 defer s.mu.RUnlock() 6141 var cid uint64 6142 for _, l := range s.leafs { 6143 l.mu.Lock() 6144 cid = l.cid 6145 l.mu.Unlock() 6146 } 6147 return cid 6148 } 6149 leaf1 := getCID(s1) 6150 leaf2 := getCID(s2) 6151 6152 // Wait for more than auth timeout 6153 time.Sleep(time.Second) 6154 6155 checkLeafNodeConnected(t, s1) 6156 checkLeafNodeConnected(t, s2) 6157 if l1 := getCID(s1); l1 != leaf1 { 6158 t.Fatalf("Leaf connection first connection had CID %v, now %v", leaf1, l1) 6159 } 6160 if l2 := getCID(s2); l2 != leaf2 { 6161 t.Fatalf("Leaf connection first connection had CID %v, now %v", leaf2, l2) 6162 } 6163 } 6164 6165 func TestLeafNodeWithWeightedDQRequestsToSuperClusterWithSeparateAccounts(t *testing.T) { 6166 sc := createJetStreamSuperClusterWithTemplate(t, jsClusterAccountsTempl, 3, 2) 6167 defer sc.shutdown() 6168 6169 // Now create a leafnode cluster that has 2 LNs, one to each cluster but on separate accounts, ONE and TWO. 6170 var lnTmpl = ` 6171 listen: 127.0.0.1:-1 6172 server_name: %s 6173 jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'} 6174 6175 {{leaf}} 6176 6177 cluster { 6178 name: %s 6179 listen: 127.0.0.1:%d 6180 routes = [%s] 6181 } 6182 6183 accounts { $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] }} 6184 ` 6185 6186 var leafFrag = ` 6187 leaf { 6188 listen: 127.0.0.1:-1 6189 remotes [ 6190 { urls: [ %s ] } 6191 { urls: [ %s ] } 6192 ] 6193 }` 6194 6195 // We want to have two leaf node connections that join to the same local account on the leafnode servers, 6196 // but connect to different accounts in different clusters. 6197 c1 := sc.clusters[0] // Will connect to account ONE 6198 c2 := sc.clusters[1] // Will connect to account TWO 6199 6200 genLeafTmpl := func(tmpl string) string { 6201 t.Helper() 6202 6203 var ln1, ln2 []string 6204 for _, s := range c1.servers { 6205 if s.ClusterName() != c1.name { 6206 continue 6207 } 6208 ln := s.getOpts().LeafNode 6209 ln1 = append(ln1, fmt.Sprintf("nats://one:p@%s:%d", ln.Host, ln.Port)) 6210 } 6211 6212 for _, s := range c2.servers { 6213 if s.ClusterName() != c2.name { 6214 continue 6215 } 6216 ln := s.getOpts().LeafNode 6217 ln2 = append(ln2, fmt.Sprintf("nats://two:p@%s:%d", ln.Host, ln.Port)) 6218 } 6219 return strings.Replace(tmpl, "{{leaf}}", fmt.Sprintf(leafFrag, strings.Join(ln1, ", "), strings.Join(ln2, ", ")), 1) 6220 } 6221 6222 tmpl := strings.Replace(lnTmpl, "store_dir:", fmt.Sprintf(`domain: "%s", store_dir:`, "SA"), 1) 6223 tmpl = genLeafTmpl(tmpl) 6224 6225 ln := createJetStreamCluster(t, tmpl, "SA", "SA-", 3, 22280, false) 6226 ln.waitOnClusterReady() 6227 defer ln.shutdown() 6228 6229 for _, s := range ln.servers { 6230 checkLeafNodeConnectedCount(t, s, 2) 6231 } 6232 6233 // Now connect DQ subscribers to each cluster and they separate accounts, and make sure we get the right behavior, balanced between 6234 // them when requests originate from the leaf cluster. 6235 6236 // Create 5 clients for each cluster / account 6237 var c1c, c2c []*nats.Conn 6238 for i := 0; i < 5; i++ { 6239 nc1, _ := jsClientConnect(t, c1.randomServer(), nats.UserInfo("one", "p")) 6240 defer nc1.Close() 6241 c1c = append(c1c, nc1) 6242 nc2, _ := jsClientConnect(t, c2.randomServer(), nats.UserInfo("two", "p")) 6243 defer nc2.Close() 6244 c2c = append(c2c, nc2) 6245 } 6246 6247 createSubs := func(num int, conns []*nats.Conn) (subs []*nats.Subscription) { 6248 for i := 0; i < num; i++ { 6249 nc := conns[rand.Intn(len(conns))] 6250 sub, err := nc.QueueSubscribeSync("REQUEST", "MC") 6251 require_NoError(t, err) 6252 subs = append(subs, sub) 6253 nc.Flush() 6254 } 6255 // Let subs propagate. 6256 time.Sleep(100 * time.Millisecond) 6257 return subs 6258 } 6259 closeSubs := func(subs []*nats.Subscription) { 6260 for _, sub := range subs { 6261 sub.Unsubscribe() 6262 } 6263 } 6264 6265 // Simple test first. 6266 subs1 := createSubs(1, c1c) 6267 defer closeSubs(subs1) 6268 subs2 := createSubs(1, c2c) 6269 defer closeSubs(subs2) 6270 6271 sendRequests := func(num int) { 6272 t.Helper() 6273 // Now connect to the leaf cluster and send some requests. 6274 nc, _ := jsClientConnect(t, ln.randomServer()) 6275 defer nc.Close() 6276 6277 for i := 0; i < num; i++ { 6278 require_NoError(t, nc.Publish("REQUEST", []byte("HELP"))) 6279 } 6280 nc.Flush() 6281 } 6282 6283 pending := func(subs []*nats.Subscription) (total int) { 6284 t.Helper() 6285 for _, sub := range subs { 6286 n, _, err := sub.Pending() 6287 require_NoError(t, err) 6288 total += n 6289 } 6290 return total 6291 } 6292 6293 num := 1000 6294 checkAllReceived := func() error { 6295 total := pending(subs1) + pending(subs2) 6296 if total == num { 6297 return nil 6298 } 6299 return fmt.Errorf("Not all received: %d vs %d", total, num) 6300 } 6301 6302 checkBalanced := func(total, pc1, pc2 int) { 6303 t.Helper() 6304 tf := float64(total) 6305 e1 := tf * (float64(pc1) / 100.00) 6306 e2 := tf * (float64(pc2) / 100.00) 6307 delta := tf / 10 6308 p1 := float64(pending(subs1)) 6309 if p1 < e1-delta || p1 > e1+delta { 6310 t.Fatalf("Value out of range for subs1, expected %v got %v", e1, p1) 6311 } 6312 p2 := float64(pending(subs2)) 6313 if p2 < e2-delta || p2 > e2+delta { 6314 t.Fatalf("Value out of range for subs2, expected %v got %v", e2, p2) 6315 } 6316 } 6317 6318 // Now connect to the leaf cluster and send some requests. 6319 6320 // Simple 50/50 6321 sendRequests(num) 6322 checkFor(t, time.Second, 200*time.Millisecond, checkAllReceived) 6323 checkBalanced(num, 50, 50) 6324 6325 closeSubs(subs1) 6326 closeSubs(subs2) 6327 6328 // Now test unbalanced. 10/90 6329 subs1 = createSubs(1, c1c) 6330 defer closeSubs(subs1) 6331 subs2 = createSubs(9, c2c) 6332 defer closeSubs(subs2) 6333 6334 sendRequests(num) 6335 checkFor(t, time.Second, 200*time.Millisecond, checkAllReceived) 6336 checkBalanced(num, 10, 90) 6337 6338 // Now test draining the subs as we are sending from an initial balanced situation simulating a draining of a cluster. 6339 6340 closeSubs(subs1) 6341 closeSubs(subs2) 6342 subs1, subs2 = nil, nil 6343 6344 // These subs slightly different. 6345 var r1, r2 atomic.Uint64 6346 for i := 0; i < 20; i++ { 6347 nc := c1c[rand.Intn(len(c1c))] 6348 sub, err := nc.QueueSubscribe("REQUEST", "MC", func(m *nats.Msg) { r1.Add(1) }) 6349 require_NoError(t, err) 6350 subs1 = append(subs1, sub) 6351 nc.Flush() 6352 6353 nc = c2c[rand.Intn(len(c2c))] 6354 sub, err = nc.QueueSubscribe("REQUEST", "MC", func(m *nats.Msg) { r2.Add(1) }) 6355 require_NoError(t, err) 6356 subs2 = append(subs2, sub) 6357 nc.Flush() 6358 } 6359 defer closeSubs(subs1) 6360 defer closeSubs(subs2) 6361 6362 nc, _ := jsClientConnect(t, ln.randomServer()) 6363 defer nc.Close() 6364 6365 for i, dindex := 0, 1; i < num; i++ { 6366 require_NoError(t, nc.Publish("REQUEST", []byte("HELP"))) 6367 // Check if we have more to simulate draining. 6368 // Will drain within first ~100 requests using 20% rand test below. 6369 // Will leave 1 behind. 6370 if dindex < len(subs1)-1 && rand.Intn(6) > 4 { 6371 sub := subs1[dindex] 6372 dindex++ 6373 sub.Drain() 6374 } 6375 } 6376 nc.Flush() 6377 6378 checkFor(t, time.Second, 200*time.Millisecond, func() error { 6379 total := int(r1.Load() + r2.Load()) 6380 if total == num { 6381 return nil 6382 } 6383 return fmt.Errorf("Not all received: %d vs %d", total, num) 6384 }) 6385 require_True(t, r2.Load() > r1.Load()) 6386 } 6387 6388 func TestLeafNodeWithWeightedDQRequestsToSuperClusterWithStreamImportAccounts(t *testing.T) { 6389 var tmpl = ` 6390 listen: 127.0.0.1:-1 6391 6392 server_name: %s 6393 jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'} 6394 6395 leaf { listen: 127.0.0.1:-1 } 6396 6397 cluster { 6398 name: %s 6399 listen: 127.0.0.1:%d 6400 routes = [%s] 6401 } 6402 6403 accounts { 6404 EFG { 6405 users = [ { user: "efg", pass: "p" } ] 6406 jetstream: enabled 6407 imports [ 6408 { stream: { account: STL, subject: "REQUEST"} } 6409 { stream: { account: KSC, subject: "REQUEST"} } 6410 ] 6411 exports [ { stream: "RESPONSE" } ] 6412 } 6413 STL { 6414 users = [ { user: "stl", pass: "p" } ] 6415 exports [ { stream: "REQUEST" } ] 6416 imports [ { stream: { account: EFG, subject: "RESPONSE"} } ] 6417 } 6418 KSC { 6419 users = [ { user: "ksc", pass: "p" } ] 6420 exports [ { stream: "REQUEST" } ] 6421 imports [ { stream: { account: EFG, subject: "RESPONSE"} } ] 6422 } 6423 $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] } 6424 }` 6425 6426 sc := createJetStreamSuperClusterWithTemplate(t, tmpl, 5, 2) 6427 defer sc.shutdown() 6428 6429 // Now create a leafnode cluster that has 2 LNs, one to each cluster but on separate accounts, STL and KSC. 6430 var lnTmpl = ` 6431 listen: 127.0.0.1:-1 6432 server_name: %s 6433 jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'} 6434 6435 {{leaf}} 6436 6437 cluster { 6438 name: %s 6439 listen: 127.0.0.1:%d 6440 routes = [%s] 6441 } 6442 6443 accounts { $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] }} 6444 ` 6445 6446 var leafFrag = ` 6447 leaf { 6448 listen: 127.0.0.1:-1 6449 remotes [ 6450 { urls: [ %s ] } 6451 { urls: [ %s ] } 6452 { urls: [ %s ] ; deny_export: [REQUEST, RESPONSE], deny_import: RESPONSE } 6453 ] 6454 }` 6455 6456 // We want to have two leaf node connections that join to the same local account on the leafnode servers, 6457 // but connect to different accounts in different clusters. 6458 c1 := sc.clusters[0] // Will connect to account KSC 6459 c2 := sc.clusters[1] // Will connect to account STL 6460 6461 genLeafTmpl := func(tmpl string) string { 6462 t.Helper() 6463 6464 var ln1, ln2, ln3 []string 6465 for _, s := range c1.servers { 6466 if s.ClusterName() != c1.name { 6467 continue 6468 } 6469 ln := s.getOpts().LeafNode 6470 ln1 = append(ln1, fmt.Sprintf("nats://ksc:p@%s:%d", ln.Host, ln.Port)) 6471 } 6472 6473 for _, s := range c2.servers { 6474 if s.ClusterName() != c2.name { 6475 continue 6476 } 6477 ln := s.getOpts().LeafNode 6478 ln2 = append(ln2, fmt.Sprintf("nats://stl:p@%s:%d", ln.Host, ln.Port)) 6479 ln3 = append(ln3, fmt.Sprintf("nats://efg:p@%s:%d", ln.Host, ln.Port)) 6480 } 6481 return strings.Replace(tmpl, "{{leaf}}", fmt.Sprintf(leafFrag, strings.Join(ln1, ", "), strings.Join(ln2, ", "), strings.Join(ln3, ", ")), 1) 6482 } 6483 6484 tmpl = strings.Replace(lnTmpl, "store_dir:", fmt.Sprintf(`domain: "%s", store_dir:`, "SA"), 1) 6485 tmpl = genLeafTmpl(tmpl) 6486 6487 ln := createJetStreamCluster(t, tmpl, "SA", "SA-", 3, 22280, false) 6488 ln.waitOnClusterReady() 6489 defer ln.shutdown() 6490 6491 for _, s := range ln.servers { 6492 checkLeafNodeConnectedCount(t, s, 3) 6493 } 6494 6495 // Now connect DQ subscribers to each cluster but to the global account. 6496 6497 // Create 5 clients for each cluster / account 6498 var c1c, c2c []*nats.Conn 6499 for i := 0; i < 5; i++ { 6500 nc1, _ := jsClientConnect(t, c1.randomServer(), nats.UserInfo("efg", "p")) 6501 defer nc1.Close() 6502 c1c = append(c1c, nc1) 6503 nc2, _ := jsClientConnect(t, c2.randomServer(), nats.UserInfo("efg", "p")) 6504 defer nc2.Close() 6505 c2c = append(c2c, nc2) 6506 } 6507 6508 createSubs := func(num int, conns []*nats.Conn) (subs []*nats.Subscription) { 6509 for i := 0; i < num; i++ { 6510 nc := conns[rand.Intn(len(conns))] 6511 sub, err := nc.QueueSubscribeSync("REQUEST", "MC") 6512 require_NoError(t, err) 6513 subs = append(subs, sub) 6514 nc.Flush() 6515 } 6516 // Let subs propagate. 6517 time.Sleep(100 * time.Millisecond) 6518 return subs 6519 } 6520 closeSubs := func(subs []*nats.Subscription) { 6521 for _, sub := range subs { 6522 sub.Unsubscribe() 6523 } 6524 } 6525 6526 // Simple test first. 6527 subs1 := createSubs(1, c1c) 6528 defer closeSubs(subs1) 6529 subs2 := createSubs(1, c2c) 6530 defer closeSubs(subs2) 6531 6532 sendRequests := func(num int) { 6533 t.Helper() 6534 // Now connect to the leaf cluster and send some requests. 6535 nc, _ := jsClientConnect(t, ln.randomServer()) 6536 defer nc.Close() 6537 6538 for i := 0; i < num; i++ { 6539 require_NoError(t, nc.Publish("REQUEST", []byte("HELP"))) 6540 } 6541 nc.Flush() 6542 } 6543 6544 pending := func(subs []*nats.Subscription) (total int) { 6545 t.Helper() 6546 for _, sub := range subs { 6547 n, _, err := sub.Pending() 6548 require_NoError(t, err) 6549 total += n 6550 } 6551 return total 6552 } 6553 6554 num := 1000 6555 checkAllReceived := func() error { 6556 total := pending(subs1) + pending(subs2) 6557 if total == num { 6558 return nil 6559 } 6560 return fmt.Errorf("Not all received: %d vs %d", total, num) 6561 } 6562 6563 checkBalanced := func(total, pc1, pc2 int) { 6564 t.Helper() 6565 tf := float64(total) 6566 e1 := tf * (float64(pc1) / 100.00) 6567 e2 := tf * (float64(pc2) / 100.00) 6568 delta := tf / 10 6569 p1 := float64(pending(subs1)) 6570 if p1 < e1-delta || p1 > e1+delta { 6571 t.Fatalf("Value out of range for subs1, expected %v got %v", e1, p1) 6572 } 6573 p2 := float64(pending(subs2)) 6574 if p2 < e2-delta || p2 > e2+delta { 6575 t.Fatalf("Value out of range for subs2, expected %v got %v", e2, p2) 6576 } 6577 } 6578 6579 // Now connect to the leaf cluster and send some requests. 6580 6581 // Simple 50/50 6582 sendRequests(num) 6583 checkFor(t, time.Second, 200*time.Millisecond, checkAllReceived) 6584 checkBalanced(num, 50, 50) 6585 6586 closeSubs(subs1) 6587 closeSubs(subs2) 6588 6589 // Now test unbalanced. 10/90 6590 subs1 = createSubs(1, c1c) 6591 defer closeSubs(subs1) 6592 subs2 = createSubs(9, c2c) 6593 defer closeSubs(subs2) 6594 6595 sendRequests(num) 6596 checkFor(t, time.Second, 200*time.Millisecond, checkAllReceived) 6597 checkBalanced(num, 10, 90) 6598 6599 closeSubs(subs1) 6600 closeSubs(subs2) 6601 6602 // Now test unbalanced. 80/20 6603 subs1 = createSubs(80, c1c) 6604 defer closeSubs(subs1) 6605 subs2 = createSubs(20, c2c) 6606 defer closeSubs(subs2) 6607 6608 sendRequests(num) 6609 checkFor(t, time.Second, 200*time.Millisecond, checkAllReceived) 6610 checkBalanced(num, 80, 20) 6611 6612 // Now test draining the subs as we are sending from an initial balanced situation simulating a draining of a cluster. 6613 6614 closeSubs(subs1) 6615 closeSubs(subs2) 6616 subs1, subs2 = nil, nil 6617 6618 // These subs slightly different. 6619 var r1, r2 atomic.Uint64 6620 for i := 0; i < 20; i++ { 6621 nc := c1c[rand.Intn(len(c1c))] 6622 sub, err := nc.QueueSubscribe("REQUEST", "MC", func(m *nats.Msg) { r1.Add(1) }) 6623 require_NoError(t, err) 6624 subs1 = append(subs1, sub) 6625 nc.Flush() 6626 6627 nc = c2c[rand.Intn(len(c2c))] 6628 sub, err = nc.QueueSubscribe("REQUEST", "MC", func(m *nats.Msg) { r2.Add(1) }) 6629 require_NoError(t, err) 6630 subs2 = append(subs2, sub) 6631 nc.Flush() 6632 } 6633 defer closeSubs(subs1) 6634 defer closeSubs(subs2) 6635 6636 nc, _ := jsClientConnect(t, ln.randomServer()) 6637 defer nc.Close() 6638 6639 for i, dindex := 0, 1; i < num; i++ { 6640 require_NoError(t, nc.Publish("REQUEST", []byte("HELP"))) 6641 // Check if we have more to simulate draining. 6642 // Will drain within first ~100 requests using 20% rand test below. 6643 // Will leave 1 behind. 6644 if dindex < len(subs1)-1 && rand.Intn(6) > 4 { 6645 sub := subs1[dindex] 6646 dindex++ 6647 sub.Drain() 6648 } 6649 } 6650 nc.Flush() 6651 6652 checkFor(t, time.Second, 200*time.Millisecond, func() error { 6653 total := int(r1.Load() + r2.Load()) 6654 if total == num { 6655 return nil 6656 } 6657 return fmt.Errorf("Not all received: %d vs %d", total, num) 6658 }) 6659 require_True(t, r2.Load() > r1.Load()) 6660 6661 // Now check opposite flow for responses. 6662 6663 // Create 10 subscribers. 6664 var rsubs []*nats.Subscription 6665 6666 for i := 0; i < 10; i++ { 6667 nc, _ := jsClientConnect(t, ln.randomServer()) 6668 defer nc.Close() 6669 sub, err := nc.QueueSubscribeSync("RESPONSE", "SA") 6670 require_NoError(t, err) 6671 nc.Flush() 6672 rsubs = append(rsubs, sub) 6673 } 6674 6675 nc, _ = jsClientConnect(t, ln.randomServer()) 6676 defer nc.Close() 6677 _, err := nc.SubscribeSync("RESPONSE") 6678 require_NoError(t, err) 6679 nc.Flush() 6680 6681 // Now connect and send responses from EFG in cloud. 6682 nc, _ = jsClientConnect(t, sc.randomServer(), nats.UserInfo("efg", "p")) 6683 6684 for i := 0; i < 100; i++ { 6685 require_NoError(t, nc.Publish("RESPONSE", []byte("OK"))) 6686 } 6687 nc.Flush() 6688 6689 checkAllRespReceived := func() error { 6690 p := pending(rsubs) 6691 if p == 100 { 6692 return nil 6693 } 6694 return fmt.Errorf("Not all responses received: %d vs %d", p, 100) 6695 } 6696 6697 checkFor(t, time.Second, 200*time.Millisecond, checkAllRespReceived) 6698 } 6699 6700 func TestLeafNodeWithWeightedDQResponsesWithStreamImportAccountsWithUnsub(t *testing.T) { 6701 var tmpl = ` 6702 listen: 127.0.0.1:-1 6703 6704 server_name: %s 6705 jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'} 6706 6707 leaf { listen: 127.0.0.1:-1 } 6708 6709 cluster { 6710 name: %s 6711 listen: 127.0.0.1:%d 6712 routes = [%s] 6713 } 6714 6715 accounts { 6716 EFG { 6717 users = [ { user: "efg", pass: "p" } ] 6718 jetstream: enabled 6719 exports [ { stream: "RESPONSE" } ] 6720 } 6721 STL { 6722 users = [ { user: "stl", pass: "p" } ] 6723 imports [ { stream: { account: EFG, subject: "RESPONSE"} } ] 6724 } 6725 KSC { 6726 users = [ { user: "ksc", pass: "p" } ] 6727 imports [ { stream: { account: EFG, subject: "RESPONSE"} } ] 6728 } 6729 $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] } 6730 }` 6731 6732 c := createJetStreamClusterWithTemplate(t, tmpl, "US-CENTRAL", 3) 6733 defer c.shutdown() 6734 6735 // Now create a leafnode cluster that has 2 LNs, one to each cluster but on separate accounts, STL and KSC. 6736 var lnTmpl = ` 6737 listen: 127.0.0.1:-1 6738 server_name: %s 6739 jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '%s'} 6740 6741 {{leaf}} 6742 6743 cluster { 6744 name: %s 6745 listen: 127.0.0.1:%d 6746 routes = [%s] 6747 } 6748 6749 accounts { $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] }} 6750 ` 6751 6752 var leafFrag = ` 6753 leaf { 6754 listen: 127.0.0.1:-1 6755 remotes [ { urls: [ %s ] } ] 6756 }` 6757 6758 genLeafTmpl := func(tmpl string) string { 6759 t.Helper() 6760 6761 var ln []string 6762 for _, s := range c.servers { 6763 lno := s.getOpts().LeafNode 6764 ln = append(ln, fmt.Sprintf("nats://ksc:p@%s:%d", lno.Host, lno.Port)) 6765 } 6766 return strings.Replace(tmpl, "{{leaf}}", fmt.Sprintf(leafFrag, strings.Join(ln, ", ")), 1) 6767 } 6768 6769 tmpl = strings.Replace(lnTmpl, "store_dir:", fmt.Sprintf(`domain: "%s", store_dir:`, "SA"), 1) 6770 tmpl = genLeafTmpl(tmpl) 6771 6772 ln := createJetStreamCluster(t, tmpl, "SA", "SA-", 3, 22280, false) 6773 ln.waitOnClusterReady() 6774 defer ln.shutdown() 6775 6776 for _, s := range ln.servers { 6777 checkLeafNodeConnectedCount(t, s, 1) 6778 } 6779 6780 // Create 10 subscribers. 6781 var rsubs []*nats.Subscription 6782 6783 closeSubs := func(subs []*nats.Subscription) { 6784 for _, sub := range subs { 6785 sub.Unsubscribe() 6786 } 6787 } 6788 6789 checkAllRespReceived := func() error { 6790 t.Helper() 6791 var total int 6792 for _, sub := range rsubs { 6793 n, _, err := sub.Pending() 6794 require_NoError(t, err) 6795 total += n 6796 } 6797 if total == 100 { 6798 return nil 6799 } 6800 return fmt.Errorf("Not all responses received: %d vs %d", total, 100) 6801 } 6802 6803 s := ln.randomServer() 6804 for i := 0; i < 4; i++ { 6805 nc, _ := jsClientConnect(t, s) 6806 defer nc.Close() 6807 sub, err := nc.QueueSubscribeSync("RESPONSE", "SA") 6808 require_NoError(t, err) 6809 nc.Flush() 6810 rsubs = append(rsubs, sub) 6811 } 6812 6813 // Now connect and send responses from EFG in cloud. 6814 nc, _ := jsClientConnect(t, c.randomServer(), nats.UserInfo("efg", "p")) 6815 for i := 0; i < 100; i++ { 6816 require_NoError(t, nc.Publish("RESPONSE", []byte("OK"))) 6817 } 6818 nc.Flush() 6819 6820 // Make sure all received. 6821 checkFor(t, time.Second, 200*time.Millisecond, checkAllRespReceived) 6822 6823 checkAccountInterest := func(s *Server, accName string) *SublistResult { 6824 t.Helper() 6825 acc, err := s.LookupAccount(accName) 6826 require_NoError(t, err) 6827 acc.mu.RLock() 6828 r := acc.sl.Match("RESPONSE") 6829 acc.mu.RUnlock() 6830 return r 6831 } 6832 6833 checkInterest := func() error { 6834 t.Helper() 6835 for _, s := range c.servers { 6836 if r := checkAccountInterest(s, "KSC"); len(r.psubs)+len(r.qsubs) > 0 { 6837 return fmt.Errorf("Subs still present for %q: %+v", "KSC", r) 6838 } 6839 if r := checkAccountInterest(s, "EFG"); len(r.psubs)+len(r.qsubs) > 0 { 6840 return fmt.Errorf("Subs still present for %q: %+v", "EFG", r) 6841 } 6842 } 6843 return nil 6844 } 6845 6846 // Now unsub them and create new ones on a different server. 6847 closeSubs(rsubs) 6848 rsubs = rsubs[:0] 6849 6850 // Also restart the server that we had all the rsubs on. 6851 s.Shutdown() 6852 s.WaitForShutdown() 6853 s = ln.restartServer(s) 6854 ln.waitOnClusterReady() 6855 ln.waitOnServerCurrent(s) 6856 6857 checkFor(t, time.Second, 200*time.Millisecond, checkInterest) 6858 6859 for i := 0; i < 4; i++ { 6860 nc, _ := jsClientConnect(t, s) 6861 defer nc.Close() 6862 sub, err := nc.QueueSubscribeSync("RESPONSE", "SA") 6863 require_NoError(t, err) 6864 nc.Flush() 6865 rsubs = append(rsubs, sub) 6866 } 6867 6868 for i := 0; i < 100; i++ { 6869 require_NoError(t, nc.Publish("RESPONSE", []byte("OK"))) 6870 } 6871 nc.Flush() 6872 6873 // Make sure all received. 6874 checkFor(t, time.Second, 200*time.Millisecond, checkAllRespReceived) 6875 6876 closeSubs(rsubs) 6877 checkFor(t, time.Second, 200*time.Millisecond, checkInterest) 6878 } 6879 6880 func TestLeafNodeTwoRemotesToSameHubAccount(t *testing.T) { 6881 conf1 := createConfFile(t, []byte(` 6882 port: -1 6883 server_name: "hub" 6884 accounts { 6885 HA { users: [{user: ha, password: pwd}] } 6886 } 6887 leafnodes { 6888 port: -1 6889 } 6890 `)) 6891 s1, o1 := RunServerWithConfig(conf1) 6892 defer s1.Shutdown() 6893 6894 conf2 := createConfFile(t, []byte(fmt.Sprintf(` 6895 port: -1 6896 server_name: "spoke" 6897 accounts { 6898 A { users: [{user: A, password: pwd}] } 6899 B { users: [{user: B, password: pwd}] } 6900 C { users: [{user: C, password: pwd}] } 6901 } 6902 leafnodes { 6903 remotes [ 6904 { 6905 url: "nats://ha:pwd@127.0.0.1:%d" 6906 local: "A" 6907 } 6908 { 6909 url: "nats://ha:pwd@127.0.0.1:%d" 6910 local: "C" 6911 } 6912 ] 6913 } 6914 `, o1.LeafNode.Port, o1.LeafNode.Port))) 6915 s2, _ := RunServerWithConfig(conf2) 6916 defer s2.Shutdown() 6917 6918 l := &captureErrorLogger{errCh: make(chan string, 10)} 6919 s2.SetLogger(l, false, false) 6920 6921 checkLeafNodeConnectedCount(t, s2, 2) 6922 6923 // Make sure we don't get duplicate leafnode connection errors 6924 deadline := time.NewTimer(1500 * time.Millisecond) 6925 for done := false; !done; { 6926 select { 6927 case err := <-l.errCh: 6928 if strings.Contains(err, DuplicateRemoteLeafnodeConnection.String()) { 6929 t.Fatalf("Got error: %v", err) 6930 } 6931 case <-deadline.C: 6932 done = true 6933 } 6934 } 6935 6936 nca := natsConnect(t, s2.ClientURL(), nats.UserInfo("A", "pwd")) 6937 defer nca.Close() 6938 suba := natsSubSync(t, nca, "A") 6939 ncb := natsConnect(t, s2.ClientURL(), nats.UserInfo("B", "pwd")) 6940 defer ncb.Close() 6941 subb := natsSubSync(t, ncb, "B") 6942 ncc := natsConnect(t, s2.ClientURL(), nats.UserInfo("C", "pwd")) 6943 defer ncc.Close() 6944 subc := natsSubSync(t, ncc, "C") 6945 subs := map[string]*nats.Subscription{"A": suba, "B": subb, "C": subc} 6946 6947 for _, subj := range []string{"A", "C"} { 6948 checkSubInterest(t, s1, "HA", subj, time.Second) 6949 } 6950 6951 nc := natsConnect(t, s1.ClientURL(), nats.UserInfo("ha", "pwd")) 6952 defer nc.Close() 6953 6954 for _, subj := range []string{"A", "B", "C"} { 6955 natsPub(t, nc, subj, []byte("hello")) 6956 } 6957 6958 for _, subj := range []string{"A", "B", "C"} { 6959 var expected bool 6960 if subj != "B" { 6961 expected = true 6962 } 6963 sub := subs[subj] 6964 if expected { 6965 natsNexMsg(t, sub, time.Second) 6966 } else { 6967 if _, err := sub.NextMsg(50 * time.Millisecond); err != nats.ErrTimeout { 6968 t.Fatalf("Expected timeout error, got %v", err) 6969 } 6970 } 6971 } 6972 } 6973 6974 func TestLeafNodeTwoRemotesToSameHubAccountWithClusters(t *testing.T) { 6975 hubTmpl := ` 6976 port: -1 6977 server_name: "%s" 6978 accounts { 6979 HA { users: [{user: HA, password: pwd}] } 6980 } 6981 cluster { 6982 name: "hub" 6983 port: -1 6984 %s 6985 } 6986 leafnodes { 6987 port: -1 6988 } 6989 ` 6990 confH1 := createConfFile(t, []byte(fmt.Sprintf(hubTmpl, "H1", _EMPTY_))) 6991 sh1, oh1 := RunServerWithConfig(confH1) 6992 defer sh1.Shutdown() 6993 6994 confH2 := createConfFile(t, []byte(fmt.Sprintf(hubTmpl, "H2", fmt.Sprintf("routes: [\"nats://127.0.0.1:%d\"]", oh1.Cluster.Port)))) 6995 sh2, oh2 := RunServerWithConfig(confH2) 6996 defer sh2.Shutdown() 6997 6998 checkClusterFormed(t, sh1, sh2) 6999 7000 spokeTmpl := ` 7001 port: -1 7002 server_name: "%s" 7003 accounts { 7004 A { users: [{user: A, password: pwd}] } 7005 B { users: [{user: B, password: pwd}] } 7006 } 7007 cluster { 7008 name: "spoke" 7009 port: -1 7010 %s 7011 } 7012 leafnodes { 7013 remotes [ 7014 { 7015 url: "nats://HA:pwd@127.0.0.1:%d" 7016 local: "A" 7017 } 7018 { 7019 url: "nats://HA:pwd@127.0.0.1:%d" 7020 local: "B" 7021 } 7022 ] 7023 } 7024 ` 7025 for _, test := range []struct { 7026 name string 7027 sp2Leafport int 7028 }{ 7029 {"connect to different hub servers", oh2.LeafNode.Port}, 7030 {"connect to same hub server", oh1.LeafNode.Port}, 7031 } { 7032 t.Run(test.name, func(t *testing.T) { 7033 confSP1 := createConfFile(t, []byte(fmt.Sprintf(spokeTmpl, "SP1", _EMPTY_, oh1.LeafNode.Port, oh1.LeafNode.Port))) 7034 sp1, osp1 := RunServerWithConfig(confSP1) 7035 defer sp1.Shutdown() 7036 7037 confSP2 := createConfFile(t, []byte(fmt.Sprintf(spokeTmpl, "SP2", 7038 fmt.Sprintf("routes: [\"nats://127.0.0.1:%d\"]", osp1.Cluster.Port), test.sp2Leafport, test.sp2Leafport))) 7039 sp2, _ := RunServerWithConfig(confSP2) 7040 defer sp2.Shutdown() 7041 7042 checkClusterFormed(t, sp1, sp2) 7043 checkLeafNodeConnectedCount(t, sp1, 2) 7044 checkLeafNodeConnectedCount(t, sp2, 2) 7045 7046 var conns []*nats.Conn 7047 createConn := func(s *Server, user string) { 7048 t.Helper() 7049 nc := natsConnect(t, s.ClientURL(), nats.UserInfo(user, "pwd")) 7050 conns = append(conns, nc) 7051 } 7052 for _, nc := range conns { 7053 defer nc.Close() 7054 } 7055 createConn(sh1, "HA") 7056 createConn(sh2, "HA") 7057 createConn(sp1, "A") 7058 createConn(sp2, "A") 7059 createConn(sp1, "B") 7060 createConn(sp2, "B") 7061 7062 check := func(subConn *nats.Conn, subj string, checkA, checkB bool) { 7063 t.Helper() 7064 sub := natsSubSync(t, subConn, subj) 7065 defer sub.Unsubscribe() 7066 7067 checkSubInterest(t, sh1, "HA", subj, time.Second) 7068 checkSubInterest(t, sh2, "HA", subj, time.Second) 7069 if checkA { 7070 checkSubInterest(t, sp1, "A", subj, time.Second) 7071 checkSubInterest(t, sp2, "A", subj, time.Second) 7072 } 7073 if checkB { 7074 checkSubInterest(t, sp1, "B", subj, time.Second) 7075 checkSubInterest(t, sp2, "B", subj, time.Second) 7076 } 7077 7078 for i, ncp := range conns { 7079 // Don't publish from account "A" connections if we are 7080 // dealing with account "B", and vice-versa. 7081 if !checkA && i >= 2 && i <= 3 { 7082 continue 7083 } 7084 if !checkB && i >= 4 { 7085 continue 7086 } 7087 natsPub(t, ncp, subj, []byte("hello")) 7088 natsNexMsg(t, sub, time.Second) 7089 // Make sure we don't get a duplicate 7090 if msg, err := sub.NextMsg(50 * time.Millisecond); err != nats.ErrTimeout { 7091 t.Fatalf("Unexpected message or error: msg=%v - err=%v", msg, err) 7092 } 7093 } 7094 } 7095 check(conns[0], "HA.1", true, true) 7096 check(conns[1], "HA.2", true, true) 7097 check(conns[2], "SPA.1", true, false) 7098 check(conns[3], "SPA.2", true, false) 7099 check(conns[4], "SPB.1", false, true) 7100 check(conns[5], "SPB.2", false, true) 7101 }) 7102 } 7103 } 7104 7105 func TestLeafNodeSameLocalAccountToMultipleHubs(t *testing.T) { 7106 hub1Conf := createConfFile(t, []byte(` 7107 port: -1 7108 server_name: hub1 7109 accounts { 7110 hub1 { users: [{user: hub1, password: pwd}] } 7111 } 7112 leafnodes { 7113 port: -1 7114 } 7115 `)) 7116 sh1, oh1 := RunServerWithConfig(hub1Conf) 7117 defer sh1.Shutdown() 7118 7119 hub2Conf := createConfFile(t, []byte(` 7120 port: -1 7121 server_name: hub2 7122 accounts { 7123 hub2 { users: [{user: hub2, password: pwd}] } 7124 } 7125 leafnodes { 7126 port: -1 7127 } 7128 `)) 7129 sh2, oh2 := RunServerWithConfig(hub2Conf) 7130 defer sh2.Shutdown() 7131 7132 lconf := createConfFile(t, []byte(fmt.Sprintf(` 7133 port: -1 7134 server_name: leaf 7135 accounts { 7136 A { users: [{user: A, password: pwd}] } 7137 B { users: [{user: B, password: pwd}] } 7138 C { users: [{user: C, password: pwd}] } 7139 } 7140 leafnodes { 7141 port: -1 7142 remotes [ 7143 { 7144 url: nats://hub1:pwd@127.0.0.1:%[1]d 7145 local: "A" 7146 } 7147 { 7148 url: nats://hub1:pwd@127.0.0.1:%[1]d 7149 local: "C" 7150 } 7151 { 7152 url: nats://hub2:pwd@127.0.0.1:%[2]d 7153 local: "A" 7154 } 7155 { 7156 url: nats://hub2:pwd@127.0.0.1:%[2]d 7157 local: "B" 7158 } 7159 ] 7160 } 7161 `, oh1.LeafNode.Port, oh2.LeafNode.Port))) 7162 s, _ := RunServerWithConfig(lconf) 7163 defer s.Shutdown() 7164 7165 // The leafnode to hub1 should have 2 connections (A and C) 7166 // while the one to hub2 should have 2 connections (A and B) 7167 checkLeafNodeConnectedCount(t, sh1, 2) 7168 checkLeafNodeConnectedCount(t, sh2, 2) 7169 checkLeafNodeConnectedCount(t, s, 4) 7170 7171 nca := natsConnect(t, s.ClientURL(), nats.UserInfo("A", "pwd")) 7172 defer nca.Close() 7173 suba := natsSubSync(t, nca, "A") 7174 ncb := natsConnect(t, s.ClientURL(), nats.UserInfo("B", "pwd")) 7175 defer ncb.Close() 7176 subb := natsSubSync(t, ncb, "B") 7177 ncc := natsConnect(t, s.ClientURL(), nats.UserInfo("C", "pwd")) 7178 defer ncc.Close() 7179 subc := natsSubSync(t, ncc, "C") 7180 7181 checkSubInterest(t, sh1, "hub1", "A", time.Second) 7182 checkSubNoInterest(t, sh1, "hub1", "B", time.Second) 7183 checkSubInterest(t, sh1, "hub1", "C", time.Second) 7184 7185 checkSubInterest(t, sh2, "hub2", "A", time.Second) 7186 checkSubInterest(t, sh2, "hub2", "B", time.Second) 7187 checkSubNoInterest(t, sh2, "hub2", "C", time.Second) 7188 7189 nch1 := natsConnect(t, sh1.ClientURL(), nats.UserInfo("hub1", "pwd")) 7190 defer nch1.Close() 7191 nch2 := natsConnect(t, sh2.ClientURL(), nats.UserInfo("hub2", "pwd")) 7192 defer nch2.Close() 7193 7194 checkNoMsg := func(sub *nats.Subscription) { 7195 t.Helper() 7196 if msg, err := sub.NextMsg(50 * time.Millisecond); err != nats.ErrTimeout { 7197 t.Fatalf("Unexpected message: %s", msg.Data) 7198 } 7199 } 7200 7201 checkSub := func(sub *nats.Subscription, subj, payload string) { 7202 t.Helper() 7203 msg := natsNexMsg(t, sub, time.Second) 7204 require_Equal(t, subj, msg.Subject) 7205 require_Equal(t, payload, string(msg.Data)) 7206 // Make sure we don't get duplicates 7207 checkNoMsg(sub) 7208 } 7209 7210 natsPub(t, nch1, "A", []byte("msgA1")) 7211 checkSub(suba, "A", "msgA1") 7212 natsPub(t, nch1, "B", []byte("msgB1")) 7213 checkNoMsg(subb) 7214 natsPub(t, nch1, "C", []byte("msgC1")) 7215 checkSub(subc, "C", "msgC1") 7216 7217 natsPub(t, nch2, "A", []byte("msgA2")) 7218 checkSub(suba, "A", "msgA2") 7219 natsPub(t, nch2, "B", []byte("msgB2")) 7220 checkSub(subb, "B", "msgB2") 7221 natsPub(t, nch2, "C", []byte("msgC2")) 7222 checkNoMsg(subc) 7223 } 7224 7225 func TestLeafNodeSlowConsumer(t *testing.T) { 7226 ao := DefaultOptions() 7227 ao.LeafNode.Host = "127.0.0.1" 7228 ao.LeafNode.Port = -1 7229 ao.WriteDeadline = 1 * time.Millisecond 7230 a := RunServer(ao) 7231 defer a.Shutdown() 7232 7233 c, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", ao.LeafNode.Port)) 7234 if err != nil { 7235 t.Fatalf("Error connecting: %v", err) 7236 } 7237 time.Sleep(5 * time.Millisecond) 7238 a.mu.Lock() 7239 checkFor(t, 2*time.Second, 15*time.Millisecond, func() error { 7240 a.grMu.Lock() 7241 defer a.grMu.Unlock() 7242 for _, cli := range a.grTmpClients { 7243 cli.out.wdl = time.Nanosecond 7244 return nil 7245 } 7246 return nil 7247 }) 7248 a.mu.Unlock() 7249 7250 // Only leafnode slow consumers that made it past connect are tracked 7251 // in the slow consumers counter. 7252 if _, err := c.Write([]byte("CONNECT {}\r\n")); err != nil { 7253 t.Fatalf("Error writing connect: %v", err) 7254 } 7255 // Read info 7256 br := bufio.NewReader(c) 7257 br.ReadLine() 7258 for i := 0; i < 10; i++ { 7259 if _, err := c.Write([]byte("PING\r\n")); err != nil { 7260 t.Fatalf("Unexpected error writing PING: %v", err) 7261 } 7262 } 7263 defer c.Close() 7264 timeout := time.Now().Add(time.Second) 7265 var ( 7266 got uint64 7267 expected uint64 = 1 7268 ) 7269 for time.Now().Before(timeout) { 7270 got = a.NumSlowConsumersLeafs() 7271 if got == expected { 7272 return 7273 } 7274 time.Sleep(1 * time.Millisecond) 7275 } 7276 t.Fatalf("Timed out waiting for slow consumer leafnodes, got: %v, expected: %v", got, expected) 7277 } 7278 7279 // https://github.com/nats-io/nats-server/issues/4367 7280 func TestLeafNodeDQMultiAccountExportImport(t *testing.T) { 7281 bConf := createConfFile(t, []byte(` 7282 listen: 127.0.0.1:-1 7283 server_name: cluster-b-0 7284 accounts { 7285 $SYS: { users: [ { user: admin, password: pwd } ] }, 7286 AGG: { 7287 exports: [ { service: "PING.>" } ] 7288 users: [ { user: agg, password: agg } ] 7289 } 7290 } 7291 leaf { listen: 127.0.0.1:-1 } 7292 `)) 7293 7294 sb, ob := RunServerWithConfig(bConf) 7295 defer sb.Shutdown() 7296 7297 tmpl := ` 7298 listen: 127.0.0.1:-1 7299 server_name: %s 7300 jetstream: { store_dir: '%s' } 7301 cluster { 7302 name: %s 7303 listen: 127.0.0.1:%d 7304 routes = [%s] 7305 } 7306 accounts { 7307 $SYS: { users: [ { user: admin, password: pwd } ] }, 7308 A: { 7309 mappings: { "A.>" : ">" } 7310 exports: [ { service: A.> } ] 7311 users: [ { user: a, password: a } ] 7312 }, 7313 AGG: { 7314 imports: [ { service: { subject: A.>, account: A } } ] 7315 users: [ { user: agg, password: agg } ] 7316 }, 7317 } 7318 leaf { 7319 remotes: [ { 7320 urls: [ nats-leaf://agg:agg@127.0.0.1:{LEAF_PORT} ] 7321 account: AGG 7322 } ] 7323 } 7324 ` 7325 tmpl = strings.Replace(tmpl, "{LEAF_PORT}", fmt.Sprintf("%d", ob.LeafNode.Port), 1) 7326 c := createJetStreamCluster(t, tmpl, "cluster-a", "cluster-a-", 3, 22110, false) 7327 defer c.shutdown() 7328 7329 // Make sure all servers are connected via leafnode to the hub, the b server. 7330 for _, s := range c.servers { 7331 checkLeafNodeConnectedCount(t, s, 1) 7332 } 7333 7334 // Connect to a server in the cluster and create a DQ listener. 7335 nc, _ := jsClientConnect(t, c.randomServer(), nats.UserInfo("a", "a")) 7336 defer nc.Close() 7337 7338 var got atomic.Int32 7339 7340 natsQueueSub(t, nc, "PING", "Q", func(m *nats.Msg) { 7341 got.Add(1) 7342 m.Respond([]byte("REPLY")) 7343 }) 7344 7345 // Now connect to B and send the request. 7346 ncb, _ := jsClientConnect(t, sb, nats.UserInfo("agg", "agg")) 7347 defer ncb.Close() 7348 7349 _, err := ncb.Request("A.PING", []byte("REQUEST"), time.Second) 7350 require_NoError(t, err) 7351 require_Equal(t, got.Load(), 1) 7352 } 7353 7354 // https://github.com/nats-io/nats-server/issues/4934 7355 func TestLeafNodeServerReloadSubjectMappings(t *testing.T) { 7356 stmpl := ` 7357 listen: 127.0.0.1:-1 7358 server_name: test-server 7359 mappings = { "source1": "target" } 7360 leaf { listen: 127.0.0.1:-1 } 7361 ` 7362 conf := createConfFile(t, []byte(stmpl)) 7363 s, o := RunServerWithConfig(conf) 7364 defer s.Shutdown() 7365 7366 tmpl := ` 7367 listen: 127.0.0.1:-1 7368 server_name: test-leaf 7369 leaf { 7370 remotes: [ { 7371 urls: [ nats-leaf://127.0.0.1:{LEAF_PORT} ] 7372 } ] 7373 } 7374 ` 7375 tmpl = strings.Replace(tmpl, "{LEAF_PORT}", fmt.Sprintf("%d", o.LeafNode.Port), 1) 7376 lConf := createConfFile(t, []byte(tmpl)) 7377 l, _ := RunServerWithConfig(lConf) 7378 defer l.Shutdown() 7379 7380 checkLeafNodeConnected(t, l) 7381 7382 // Create our subscriber. 7383 nc := natsConnect(t, s.ClientURL()) 7384 defer nc.Close() 7385 sub := natsSubSync(t, nc, "target") 7386 natsFlush(t, nc) 7387 7388 // Create our publisher. 7389 ncl := natsConnect(t, l.ClientURL()) 7390 defer ncl.Close() 7391 // Publish our message. 7392 ncl.Publish("source1", []byte("OK")) 7393 7394 // Make sure we receive it. 7395 checkSubsPending(t, sub, 1) 7396 7397 // Now change mapping. 7398 reloadUpdateConfig(t, s, conf, strings.Replace(stmpl, "source1", "source2", 1)) 7399 // Also make sure we do not have subscription interest for source1 on leaf anymore. 7400 checkSubInterest(t, l, globalAccountName, "source2", 2*time.Second) 7401 7402 // Publish our new message. 7403 ncl.Publish("source2", []byte("OK")) 7404 7405 // Make sure we receive it. 7406 checkSubsPending(t, sub, 2) 7407 7408 // Also make sure we do not have subscription interest for source1 on leaf anymore. 7409 checkSubNoInterest(t, l, globalAccountName, "source1", 2*time.Second) 7410 } 7411 7412 // https://github.com/nats-io/nats-server/issues/5099 7413 func TestLeafNodeServerReloadSubjectMappingsWithSameSubject(t *testing.T) { 7414 stmpl := ` 7415 listen: 127.0.0.1:-1 7416 server_name: test-server 7417 mappings = { "source": "target1" } 7418 leaf { listen: 127.0.0.1:-1 } 7419 ` 7420 conf := createConfFile(t, []byte(stmpl)) 7421 s, o := RunServerWithConfig(conf) 7422 defer s.Shutdown() 7423 7424 tmpl := ` 7425 listen: 127.0.0.1:-1 7426 server_name: test-leaf 7427 leaf { 7428 remotes: [ { urls: [ nats-leaf://127.0.0.1:{LEAF_PORT} ] } ] 7429 } 7430 ` 7431 tmpl = strings.Replace(tmpl, "{LEAF_PORT}", fmt.Sprintf("%d", o.LeafNode.Port), 1) 7432 lConf := createConfFile(t, []byte(tmpl)) 7433 l, _ := RunServerWithConfig(lConf) 7434 defer l.Shutdown() 7435 7436 checkLeafNodeConnected(t, l) 7437 7438 // Create our subscriber. 7439 nc := natsConnect(t, s.ClientURL()) 7440 defer nc.Close() 7441 sub1 := natsSubSync(t, nc, "target1") 7442 sub2 := natsSubSync(t, nc, "target2") 7443 natsFlush(t, nc) 7444 7445 // Create our publisher. 7446 ncl := natsConnect(t, l.ClientURL()) 7447 defer ncl.Close() 7448 // Publish our message. 7449 ncl.Publish("source", []byte("OK")) 7450 7451 // Make sure we receive it. 7452 checkSubsPending(t, sub1, 1) 7453 // Make sure the other does not. 7454 checkSubsPending(t, sub2, 0) 7455 7456 // Now change mapping, but only the "to" subject, keeping same "from" 7457 reloadUpdateConfig(t, s, conf, strings.Replace(stmpl, "target1", "target2", 1)) 7458 checkLeafNodeConnected(t, l) 7459 7460 // Publish our new message. 7461 ncl.Publish("source", []byte("OK")) 7462 7463 // Make sure we receive it. 7464 checkSubsPending(t, sub2, 1) 7465 // Make sure the other does not. 7466 checkSubsPending(t, sub1, 1) 7467 } 7468 7469 func TestLeafNodeNkeyAuth(t *testing.T) { 7470 conf := createConfFile(t, []byte(` 7471 listen: 127.0.0.1:-1 7472 server_name: test-server 7473 leaf { 7474 listen: 127.0.0.1:-1 7475 authorization: { nkey: UCSTG5CRF5GEJERAFKUUYRODGABTBVWY2NPE4GGKRQVQOH74PIAKTVKO } 7476 } 7477 `)) 7478 s, o := RunServerWithConfig(conf) 7479 defer s.Shutdown() 7480 7481 tmpl := ` 7482 listen: 127.0.0.1:-1 7483 server_name: test-leaf 7484 leaf { 7485 remotes: [ { 7486 url: nats-leaf://127.0.0.1:{LEAF_PORT} 7487 seed: SUACJN3OSKWWPQXME4JUNFJ3PARXPO657GGNWNU7PK7G3AUQQYHLW26XH4 7488 } ] 7489 } 7490 ` 7491 tmpl = strings.Replace(tmpl, "{LEAF_PORT}", fmt.Sprintf("%d", o.LeafNode.Port), 1) 7492 lConf := createConfFile(t, []byte(tmpl)) 7493 l, _ := RunServerWithConfig(lConf) 7494 defer l.Shutdown() 7495 7496 checkLeafNodeConnected(t, l) 7497 } 7498 7499 func TestLeafNodeAccountNkeysAuth(t *testing.T) { 7500 conf := createConfFile(t, []byte(` 7501 listen: 127.0.0.1:-1 7502 server_name: test-server 7503 leaf { 7504 listen: 127.0.0.1:-1 7505 } 7506 accounts { 7507 A { users [ {nkey: UCSTG5CRF5GEJERAFKUUYRODGABTBVWY2NPE4GGKRQVQOH74PIAKTVKO } ] } 7508 } 7509 `)) 7510 s, o := RunServerWithConfig(conf) 7511 defer s.Shutdown() 7512 7513 tmpl := ` 7514 listen: 127.0.0.1:-1 7515 server_name: test-leaf 7516 leaf { 7517 remotes: [ { 7518 url: nats-leaf://127.0.0.1:{LEAF_PORT} 7519 seed: SUACJN3OSKWWPQXME4JUNFJ3PARXPO657GGNWNU7PK7G3AUQQYHLW26XH4 7520 } ] 7521 } 7522 ` 7523 tmpl = strings.Replace(tmpl, "{LEAF_PORT}", fmt.Sprintf("%d", o.LeafNode.Port), 1) 7524 lConf := createConfFile(t, []byte(tmpl)) 7525 l, _ := RunServerWithConfig(lConf) 7526 defer l.Shutdown() 7527 7528 checkLeafNodeConnected(t, l) 7529 } 7530 7531 // https://github.com/nats-io/nats-server/issues/5117 7532 func TestLeafNodeLoopDetectionOnActualLoop(t *testing.T) { 7533 // Setup: B --[leaf]--> A C --[leaf]--> A C --[leaf] --> B 7534 accConf := ` 7535 accounts: { 7536 APP: { 7537 users: [ { user:u, password: u, 7538 permissions: { publish = "u.>", subscribe = "u.>" }} ] 7539 } 7540 $SYS: { users = [ {user: "s", password: "s"} ] } 7541 }` 7542 7543 confA := createConfFile(t, []byte(fmt.Sprintf(` 7544 server_name: a1 7545 port: -1 7546 cluster: { name: A } 7547 leafnodes { 7548 port: 17422 7549 } 7550 %s`, accConf))) 7551 7552 confB := createConfFile(t, []byte(fmt.Sprintf(` 7553 server_name: b1 7554 port: -1 7555 cluster: { name: B } 7556 leafnodes { 7557 port: 17432 7558 remotes [ 7559 { urls: ["nats-leaf://u:u@localhost:17422"], account: "APP" } 7560 ] 7561 reconnect: "2s" 7562 } 7563 %s`, accConf))) 7564 7565 confC := createConfFile(t, []byte(fmt.Sprintf(` 7566 server_name: c1 7567 port: -1 7568 cluster: { name: C } 7569 leafnodes { 7570 port: 17442 7571 remotes [ 7572 { urls: ["nats-leaf://u:u@localhost:17422"], account: "APP" } 7573 # This one creates the loop 7574 { urls: ["nats-leaf://u:u@localhost:17432"], account: "APP" } 7575 ] 7576 reconnect: "0.5s" 7577 } 7578 %s`, accConf))) 7579 7580 // Start order will be B -> C -> A 7581 // We will force C to connect to A first before B using different reconnect intervals. 7582 // If B connects first we detect loops fine. If C connects first we do not. 7583 7584 srvB, _ := RunServerWithConfig(confB) 7585 defer srvB.Shutdown() 7586 lb := &loopDetectedLogger{ch: make(chan string, 1)} 7587 srvB.SetLogger(lb, false, false) 7588 7589 srvC, _ := RunServerWithConfig(confC) 7590 defer srvC.Shutdown() 7591 lc := &loopDetectedLogger{ch: make(chan string, 1)} 7592 srvC.SetLogger(lc, false, false) 7593 7594 // C should connect to B 7595 checkLeafNodeConnectedCount(t, srvC, 1) 7596 7597 srvA, _ := RunServerWithConfig(confA) 7598 defer srvA.Shutdown() 7599 la := &loopDetectedLogger{ch: make(chan string, 1)} 7600 srvA.SetLogger(la, false, false) 7601 7602 select { 7603 case <-la.ch: 7604 case <-lb.ch: 7605 case <-lc.ch: 7606 case <-time.After(5 * time.Second): 7607 t.Fatalf("Did not get any error regarding loop") 7608 } 7609 }