github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/grid/grid_test.go (about) 1 // Copyright (c) 2015-2023 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package grid 19 20 import ( 21 "bytes" 22 "context" 23 "errors" 24 "fmt" 25 "os" 26 "runtime" 27 "strconv" 28 "strings" 29 "testing" 30 "time" 31 32 "github.com/minio/minio/internal/logger/target/testlogger" 33 ) 34 35 func TestSingleRoundtrip(t *testing.T) { 36 defer testlogger.T.SetLogTB(t)() 37 errFatal := func(err error) { 38 t.Helper() 39 if err != nil { 40 t.Fatal(err) 41 } 42 } 43 grid, err := SetupTestGrid(2) 44 errFatal(err) 45 remoteHost := grid.Hosts[1] 46 local := grid.Managers[0] 47 48 // 1: Echo 49 errFatal(local.RegisterSingleHandler(handlerTest, func(payload []byte) ([]byte, *RemoteErr) { 50 t.Log("1: server payload: ", len(payload), "bytes.") 51 return append([]byte{}, payload...), nil 52 })) 53 // 2: Return as error 54 errFatal(local.RegisterSingleHandler(handlerTest2, func(payload []byte) ([]byte, *RemoteErr) { 55 t.Log("2: server payload: ", len(payload), "bytes.") 56 err := RemoteErr(payload) 57 return nil, &err 58 })) 59 60 remote := grid.Managers[1] 61 62 // 1: Echo 63 errFatal(remote.RegisterSingleHandler(handlerTest, func(payload []byte) ([]byte, *RemoteErr) { 64 t.Log("1: server payload: ", len(payload), "bytes.") 65 return append([]byte{}, payload...), nil 66 })) 67 // 2: Return as error 68 errFatal(remote.RegisterSingleHandler(handlerTest2, func(payload []byte) ([]byte, *RemoteErr) { 69 t.Log("2: server payload: ", len(payload), "bytes.") 70 err := RemoteErr(payload) 71 return nil, &err 72 })) 73 74 // local to remote 75 remoteConn := local.Connection(remoteHost) 76 remoteConn.WaitForConnect(context.Background()) 77 defer testlogger.T.SetErrorTB(t)() 78 79 t.Run("localToRemote", func(t *testing.T) { 80 const testPayload = "Hello Grid World!" 81 82 start := time.Now() 83 resp, err := remoteConn.Request(context.Background(), handlerTest, []byte(testPayload)) 84 errFatal(err) 85 if string(resp) != testPayload { 86 t.Errorf("want %q, got %q", testPayload, string(resp)) 87 } 88 t.Log("Roundtrip:", time.Since(start)) 89 }) 90 91 t.Run("localToRemoteErr", func(t *testing.T) { 92 const testPayload = "Hello Grid World!" 93 start := time.Now() 94 resp, err := remoteConn.Request(context.Background(), handlerTest2, []byte(testPayload)) 95 t.Log("Roundtrip:", time.Since(start)) 96 if len(resp) != 0 { 97 t.Errorf("want nil, got %q", string(resp)) 98 } 99 if err != RemoteErr(testPayload) { 100 t.Errorf("want error %v(%T), got %v(%T)", RemoteErr(testPayload), RemoteErr(testPayload), err, err) 101 } 102 t.Log("Roundtrip:", time.Since(start)) 103 }) 104 105 t.Run("localToRemoteHuge", func(t *testing.T) { 106 testPayload := bytes.Repeat([]byte("?"), 1<<20) 107 108 start := time.Now() 109 resp, err := remoteConn.Request(context.Background(), handlerTest, testPayload) 110 errFatal(err) 111 if string(resp) != string(testPayload) { 112 t.Errorf("want %q, got %q", testPayload, string(resp)) 113 } 114 t.Log("Roundtrip:", time.Since(start)) 115 }) 116 117 t.Run("localToRemoteErrHuge", func(t *testing.T) { 118 testPayload := bytes.Repeat([]byte("!"), 1<<10) 119 120 start := time.Now() 121 resp, err := remoteConn.Request(context.Background(), handlerTest2, testPayload) 122 if len(resp) != 0 { 123 t.Errorf("want nil, got %q", string(resp)) 124 } 125 if err != RemoteErr(testPayload) { 126 t.Errorf("want error %v(%T), got %v(%T)", RemoteErr(testPayload), RemoteErr(testPayload), err, err) 127 } 128 t.Log("Roundtrip:", time.Since(start)) 129 }) 130 } 131 132 func TestSingleRoundtripNotReady(t *testing.T) { 133 defer testlogger.T.SetLogTB(t)() 134 errFatal := func(t testing.TB, err error) { 135 t.Helper() 136 if err != nil { 137 t.Fatal(err) 138 } 139 } 140 grid, err := SetupTestGrid(2) 141 errFatal(t, err) 142 remoteHost := grid.Hosts[1] 143 local := grid.Managers[0] 144 145 // 1: Echo 146 errFatal(t, local.RegisterSingleHandler(handlerTest, func(payload []byte) ([]byte, *RemoteErr) { 147 t.Log("1: server payload: ", len(payload), "bytes.") 148 return append([]byte{}, payload...), nil 149 })) 150 // 2: Return as error 151 errFatal(t, local.RegisterSingleHandler(handlerTest2, func(payload []byte) ([]byte, *RemoteErr) { 152 t.Log("2: server payload: ", len(payload), "bytes.") 153 err := RemoteErr(payload) 154 return nil, &err 155 })) 156 157 // Do not register remote handlers 158 159 // local to remote 160 remoteConn := local.Connection(remoteHost) 161 remoteConn.WaitForConnect(context.Background()) 162 defer testlogger.T.SetErrorTB(t)() 163 164 t.Run("localToRemote", func(t *testing.T) { 165 const testPayload = "Hello Grid World!" 166 // Single requests should have remote errors. 167 _, err := remoteConn.Request(context.Background(), handlerTest, []byte(testPayload)) 168 if v, ok := err.(*RemoteErr); !ok || v.Error() != "Invalid Handler for type" { 169 t.Fatalf("Unexpected error: %v, %T", err, err) 170 } 171 // Streams should not be able to set up until registered. 172 // Thus, the error is a local error. 173 _, err = remoteConn.NewStream(context.Background(), handlerTest, []byte(testPayload)) 174 if !errors.Is(err, ErrUnknownHandler) { 175 t.Fatalf("Unexpected error: %v, %T", err, err) 176 } 177 }) 178 } 179 180 func TestSingleRoundtripGenerics(t *testing.T) { 181 defer testlogger.T.SetLogTB(t)() 182 errFatal := func(err error) { 183 t.Helper() 184 if err != nil { 185 t.Fatal(err) 186 } 187 } 188 grid, err := SetupTestGrid(2) 189 errFatal(err) 190 remoteHost := grid.Hosts[1] 191 local := grid.Managers[0] 192 remote := grid.Managers[1] 193 194 // 1: Echo 195 h1 := NewSingleHandler[*testRequest, *testResponse](handlerTest, func() *testRequest { 196 return &testRequest{} 197 }, func() *testResponse { 198 return &testResponse{} 199 }) 200 // Handles incoming requests, returns a response 201 handler1 := func(req *testRequest) (resp *testResponse, err *RemoteErr) { 202 resp = h1.NewResponse() 203 *resp = testResponse{ 204 OrgNum: req.Num, 205 OrgString: req.String, 206 Embedded: *req, 207 } 208 return resp, nil 209 } 210 // Return error 211 h2 := NewSingleHandler[*testRequest, *testResponse](handlerTest2, newTestRequest, newTestResponse) 212 handler2 := func(req *testRequest) (resp *testResponse, err *RemoteErr) { 213 r := RemoteErr(req.String) 214 return nil, &r 215 } 216 errFatal(h1.Register(local, handler1)) 217 errFatal(h2.Register(local, handler2)) 218 219 errFatal(h1.Register(remote, handler1)) 220 errFatal(h2.Register(remote, handler2)) 221 222 // local to remote connection 223 remoteConn := local.Connection(remoteHost) 224 const testPayload = "Hello Grid World!" 225 226 start := time.Now() 227 req := testRequest{Num: 1, String: testPayload} 228 resp, err := h1.Call(context.Background(), remoteConn, &req) 229 errFatal(err) 230 if resp.OrgString != testPayload { 231 t.Errorf("want %q, got %q", testPayload, resp.OrgString) 232 } 233 t.Log("Roundtrip:", time.Since(start)) 234 h1.PutResponse(resp) 235 236 start = time.Now() 237 resp, err = h2.Call(context.Background(), remoteConn, &testRequest{Num: 1, String: testPayload}) 238 t.Log("Roundtrip:", time.Since(start)) 239 if err != RemoteErr(testPayload) { 240 t.Errorf("want error %v(%T), got %v(%T)", RemoteErr(testPayload), RemoteErr(testPayload), err, err) 241 } 242 if resp != nil { 243 t.Errorf("want nil, got %q", resp) 244 } 245 h2.PutResponse(resp) 246 t.Log("Roundtrip:", time.Since(start)) 247 } 248 249 func TestSingleRoundtripGenericsRecycle(t *testing.T) { 250 defer testlogger.T.SetLogTB(t)() 251 errFatal := func(err error) { 252 t.Helper() 253 if err != nil { 254 t.Fatal(err) 255 } 256 } 257 grid, err := SetupTestGrid(2) 258 errFatal(err) 259 remoteHost := grid.Hosts[1] 260 local := grid.Managers[0] 261 remote := grid.Managers[1] 262 263 // 1: Echo 264 h1 := NewSingleHandler[*MSS, *MSS](handlerTest, NewMSS, NewMSS) 265 // Handles incoming requests, returns a response 266 handler1 := func(req *MSS) (resp *MSS, err *RemoteErr) { 267 resp = h1.NewResponse() 268 for k, v := range *req { 269 (*resp)[k] = v 270 } 271 return resp, nil 272 } 273 // Return error 274 h2 := NewSingleHandler[*MSS, *MSS](handlerTest2, NewMSS, NewMSS) 275 handler2 := func(req *MSS) (resp *MSS, err *RemoteErr) { 276 defer req.Recycle() 277 r := RemoteErr(req.Get("err")) 278 return nil, &r 279 } 280 errFatal(h1.Register(local, handler1)) 281 errFatal(h2.Register(local, handler2)) 282 283 errFatal(h1.Register(remote, handler1)) 284 errFatal(h2.Register(remote, handler2)) 285 286 // local to remote connection 287 remoteConn := local.Connection(remoteHost) 288 const testPayload = "Hello Grid World!" 289 290 start := time.Now() 291 req := NewMSSWith(map[string]string{"test": testPayload}) 292 resp, err := h1.Call(context.Background(), remoteConn, req) 293 errFatal(err) 294 if resp.Get("test") != testPayload { 295 t.Errorf("want %q, got %q", testPayload, resp.Get("test")) 296 } 297 t.Log("Roundtrip:", time.Since(start)) 298 h1.PutResponse(resp) 299 300 start = time.Now() 301 resp, err = h2.Call(context.Background(), remoteConn, NewMSSWith(map[string]string{"err": testPayload})) 302 t.Log("Roundtrip:", time.Since(start)) 303 if err != RemoteErr(testPayload) { 304 t.Errorf("want error %v(%T), got %v(%T)", RemoteErr(testPayload), RemoteErr(testPayload), err, err) 305 } 306 if resp != nil { 307 t.Errorf("want nil, got %q", resp) 308 } 309 t.Log("Roundtrip:", time.Since(start)) 310 h2.PutResponse(resp) 311 } 312 313 func TestStreamSuite(t *testing.T) { 314 defer testlogger.T.SetErrorTB(t)() 315 errFatal := func(err error) { 316 t.Helper() 317 if err != nil { 318 t.Fatal(err) 319 } 320 } 321 grid, err := SetupTestGrid(2) 322 errFatal(err) 323 t.Cleanup(grid.Cleanup) 324 325 local := grid.Managers[0] 326 localHost := grid.Hosts[0] 327 remote := grid.Managers[1] 328 remoteHost := grid.Hosts[1] 329 330 connLocalToRemote := local.Connection(remoteHost) 331 connRemoteLocal := remote.Connection(localHost) 332 333 t.Run("testStreamRoundtrip", func(t *testing.T) { 334 defer timeout(5 * time.Second)() 335 testStreamRoundtrip(t, local, remote) 336 assertNoActive(t, connRemoteLocal) 337 assertNoActive(t, connLocalToRemote) 338 }) 339 t.Run("testStreamCancel", func(t *testing.T) { 340 defer timeout(5 * time.Second)() 341 testStreamCancel(t, local, remote) 342 assertNoActive(t, connRemoteLocal) 343 assertNoActive(t, connLocalToRemote) 344 }) 345 t.Run("testStreamDeadline", func(t *testing.T) { 346 defer timeout(5 * time.Second)() 347 testStreamDeadline(t, local, remote) 348 assertNoActive(t, connRemoteLocal) 349 assertNoActive(t, connLocalToRemote) 350 }) 351 t.Run("testServerOutCongestion", func(t *testing.T) { 352 defer timeout(1 * time.Minute)() 353 testServerOutCongestion(t, local, remote) 354 assertNoActive(t, connRemoteLocal) 355 assertNoActive(t, connLocalToRemote) 356 }) 357 t.Run("testServerInCongestion", func(t *testing.T) { 358 defer timeout(1 * time.Minute)() 359 testServerInCongestion(t, local, remote) 360 assertNoActive(t, connRemoteLocal) 361 assertNoActive(t, connLocalToRemote) 362 }) 363 t.Run("testGenericsStreamRoundtrip", func(t *testing.T) { 364 defer timeout(1 * time.Minute)() 365 testGenericsStreamRoundtrip(t, local, remote) 366 assertNoActive(t, connRemoteLocal) 367 assertNoActive(t, connLocalToRemote) 368 }) 369 t.Run("testGenericsStreamRoundtripSubroute", func(t *testing.T) { 370 defer timeout(1 * time.Minute)() 371 testGenericsStreamRoundtripSubroute(t, local, remote) 372 assertNoActive(t, connRemoteLocal) 373 assertNoActive(t, connLocalToRemote) 374 }) 375 t.Run("testServerStreamResponseBlocked", func(t *testing.T) { 376 defer timeout(1 * time.Minute)() 377 testServerStreamResponseBlocked(t, local, remote) 378 assertNoActive(t, connRemoteLocal) 379 assertNoActive(t, connLocalToRemote) 380 }) 381 } 382 383 func testStreamRoundtrip(t *testing.T, local, remote *Manager) { 384 defer testlogger.T.SetErrorTB(t)() 385 defer timeout(5 * time.Second)() 386 errFatal := func(err error) { 387 t.Helper() 388 if err != nil { 389 t.Fatal(err) 390 } 391 } 392 393 // We fake a local and remote server. 394 remoteHost := remote.HostName() 395 396 // 1: Echo 397 register := func(manager *Manager) { 398 errFatal(manager.RegisterStreamingHandler(handlerTest, StreamHandler{ 399 Handle: func(ctx context.Context, payload []byte, request <-chan []byte, resp chan<- []byte) *RemoteErr { 400 for in := range request { 401 b := append([]byte{}, payload...) 402 b = append(b, in...) 403 resp <- b 404 } 405 t.Log(GetCaller(ctx).Name, "Handler done") 406 return nil 407 }, 408 OutCapacity: 1, 409 InCapacity: 1, 410 })) 411 // 2: Return as error 412 errFatal(manager.RegisterStreamingHandler(handlerTest2, StreamHandler{ 413 Handle: func(ctx context.Context, payload []byte, request <-chan []byte, resp chan<- []byte) *RemoteErr { 414 for in := range request { 415 t.Log("2: Got err request", string(in)) 416 err := RemoteErr(append(payload, in...)) 417 return &err 418 } 419 return nil 420 }, 421 OutCapacity: 1, 422 InCapacity: 1, 423 })) 424 } 425 register(local) 426 register(remote) 427 428 // local to remote 429 remoteConn := local.Connection(remoteHost) 430 const testPayload = "Hello Grid World!" 431 432 start := time.Now() 433 stream, err := remoteConn.NewStream(context.Background(), handlerTest, []byte(testPayload)) 434 errFatal(err) 435 var n int 436 stream.Requests <- []byte(strconv.Itoa(n)) 437 for resp := range stream.responses { 438 errFatal(resp.Err) 439 t.Logf("got resp: %+v", string(resp.Msg)) 440 if string(resp.Msg) != testPayload+strconv.Itoa(n) { 441 t.Errorf("want %q, got %q", testPayload+strconv.Itoa(n), string(resp.Msg)) 442 } 443 if n == 10 { 444 close(stream.Requests) 445 continue 446 } 447 n++ 448 t.Log("sending new client request") 449 stream.Requests <- []byte(strconv.Itoa(n)) 450 } 451 t.Log("EOF. 10 Roundtrips:", time.Since(start)) 452 } 453 454 func testStreamCancel(t *testing.T, local, remote *Manager) { 455 defer testlogger.T.SetErrorTB(t)() 456 errFatal := func(err error) { 457 t.Helper() 458 if err != nil { 459 t.Fatal(err) 460 } 461 } 462 463 // We fake a local and remote server. 464 remoteHost := remote.HostName() 465 466 // 1: Echo 467 serverCanceled := make(chan struct{}) 468 register := func(manager *Manager) { 469 errFatal(manager.RegisterStreamingHandler(handlerTest, StreamHandler{ 470 Handle: func(ctx context.Context, payload []byte, request <-chan []byte, resp chan<- []byte) *RemoteErr { 471 <-ctx.Done() 472 serverCanceled <- struct{}{} 473 t.Log(GetCaller(ctx).Name, "Server Context canceled") 474 return nil 475 }, 476 OutCapacity: 1, 477 InCapacity: 0, 478 })) 479 errFatal(manager.RegisterStreamingHandler(handlerTest2, StreamHandler{ 480 Handle: func(ctx context.Context, payload []byte, request <-chan []byte, resp chan<- []byte) *RemoteErr { 481 <-ctx.Done() 482 serverCanceled <- struct{}{} 483 t.Log(GetCaller(ctx).Name, "Server Context canceled") 484 return nil 485 }, 486 OutCapacity: 1, 487 InCapacity: 1, 488 })) 489 } 490 register(local) 491 register(remote) 492 493 // local to remote 494 testHandler := func(t *testing.T, handler HandlerID) { 495 remoteConn := local.Connection(remoteHost) 496 const testPayload = "Hello Grid World!" 497 498 ctx, cancel := context.WithCancel(context.Background()) 499 st, err := remoteConn.NewStream(ctx, handlerTest, []byte(testPayload)) 500 errFatal(err) 501 clientCanceled := make(chan time.Time, 1) 502 err = nil 503 go func(t *testing.T) { 504 for resp := range st.responses { 505 t.Log("got resp:", string(resp.Msg), "err:", resp.Err) 506 if err != nil { 507 t.Log("ERROR: got second error:", resp.Err, "first:", err) 508 continue 509 } 510 err = resp.Err 511 } 512 t.Log("Client Context canceled. err state:", err) 513 clientCanceled <- time.Now() 514 }(t) 515 start := time.Now() 516 cancel() 517 <-serverCanceled 518 t.Log("server cancel time:", time.Since(start)) 519 clientEnd := <-clientCanceled 520 if !errors.Is(err, context.Canceled) { 521 t.Error("expected context.Canceled, got", err) 522 } 523 t.Log("client after", time.Since(clientEnd)) 524 } 525 // local to remote, unbuffered 526 t.Run("unbuffered", func(t *testing.T) { 527 testHandler(t, handlerTest) 528 }) 529 530 t.Run("buffered", func(t *testing.T) { 531 testHandler(t, handlerTest2) 532 }) 533 } 534 535 // testStreamDeadline will test if server 536 func testStreamDeadline(t *testing.T, local, remote *Manager) { 537 defer testlogger.T.SetErrorTB(t)() 538 errFatal := func(err error) { 539 t.Helper() 540 if err != nil { 541 t.Fatal(err) 542 } 543 } 544 545 const wantDL = 50 * time.Millisecond 546 // We fake a local and remote server. 547 remoteHost := remote.HostName() 548 549 // 1: Echo 550 serverCanceled := make(chan time.Duration, 1) 551 register := func(manager *Manager) { 552 errFatal(manager.RegisterStreamingHandler(handlerTest, StreamHandler{ 553 Handle: func(ctx context.Context, payload []byte, request <-chan []byte, resp chan<- []byte) *RemoteErr { 554 started := time.Now() 555 dl, _ := ctx.Deadline() 556 if testing.Verbose() { 557 fmt.Println(GetCaller(ctx).Name, "Server deadline:", time.Until(dl)) 558 } 559 <-ctx.Done() 560 serverCanceled <- time.Since(started) 561 if testing.Verbose() { 562 fmt.Println(GetCaller(ctx).Name, "Server Context canceled with", ctx.Err(), "after", time.Since(started)) 563 } 564 return nil 565 }, 566 OutCapacity: 1, 567 InCapacity: 0, 568 })) 569 errFatal(manager.RegisterStreamingHandler(handlerTest2, StreamHandler{ 570 Handle: func(ctx context.Context, payload []byte, request <-chan []byte, resp chan<- []byte) *RemoteErr { 571 started := time.Now() 572 dl, _ := ctx.Deadline() 573 if testing.Verbose() { 574 fmt.Println(GetCaller(ctx).Name, "Server deadline:", time.Until(dl)) 575 } 576 <-ctx.Done() 577 serverCanceled <- time.Since(started) 578 if testing.Verbose() { 579 fmt.Println(GetCaller(ctx).Name, "Server Context canceled with", ctx.Err(), "after", time.Since(started)) 580 } 581 return nil 582 }, 583 OutCapacity: 1, 584 InCapacity: 1, 585 })) 586 } 587 register(local) 588 register(remote) 589 // Double remote DL 590 local.debugMsg(debugAddToDeadline, wantDL) 591 defer local.debugMsg(debugAddToDeadline, time.Duration(0)) 592 remote.debugMsg(debugAddToDeadline, wantDL) 593 defer remote.debugMsg(debugAddToDeadline, time.Duration(0)) 594 595 testHandler := func(t *testing.T, handler HandlerID) { 596 remoteConn := local.Connection(remoteHost) 597 const testPayload = "Hello Grid World!" 598 599 ctx, cancel := context.WithTimeout(context.Background(), wantDL) 600 defer cancel() 601 st, err := remoteConn.NewStream(ctx, handler, []byte(testPayload)) 602 errFatal(err) 603 clientCanceled := make(chan time.Duration, 1) 604 go func() { 605 started := time.Now() 606 for resp := range st.responses { 607 err = resp.Err 608 } 609 clientCanceled <- time.Since(started) 610 }() 611 serverEnd := <-serverCanceled 612 clientEnd := <-clientCanceled 613 t.Log("server cancel time:", serverEnd) 614 t.Log("client cancel time:", clientEnd) 615 if !errors.Is(err, context.DeadlineExceeded) { 616 t.Error("expected context.DeadlineExceeded, got", err) 617 } 618 } 619 // local to remote, unbuffered 620 t.Run("unbuffered", func(t *testing.T) { 621 testHandler(t, handlerTest) 622 }) 623 624 t.Run("buffered", func(t *testing.T) { 625 testHandler(t, handlerTest2) 626 }) 627 } 628 629 func testServerOutCongestion(t *testing.T, local, remote *Manager) { 630 defer testlogger.T.SetErrorTB(t)() 631 errFatal := func(err error) { 632 t.Helper() 633 if err != nil { 634 t.Fatal(err) 635 } 636 } 637 638 // We fake a local and remote server. 639 remoteHost := remote.HostName() 640 641 // 1: Echo 642 serverSent := make(chan struct{}) 643 register := func(manager *Manager) { 644 errFatal(manager.RegisterStreamingHandler(handlerTest, StreamHandler{ 645 Handle: func(ctx context.Context, payload []byte, request <-chan []byte, resp chan<- []byte) *RemoteErr { 646 // Send many responses. 647 // Test that this doesn't block. 648 for i := byte(0); i < 100; i++ { 649 select { 650 case resp <- []byte{i}: 651 // ok 652 case <-ctx.Done(): 653 return NewRemoteErr(ctx.Err()) 654 } 655 if i == 0 { 656 close(serverSent) 657 } 658 } 659 return nil 660 }, 661 OutCapacity: 1, 662 InCapacity: 0, 663 })) 664 errFatal(manager.RegisterSingleHandler(handlerTest2, func(payload []byte) ([]byte, *RemoteErr) { 665 // Simple roundtrip 666 return append([]byte{}, payload...), nil 667 })) 668 } 669 register(local) 670 register(remote) 671 672 remoteConn := local.Connection(remoteHost) 673 const testPayload = "Hello Grid World!" 674 675 ctx, cancel := context.WithTimeout(context.Background(), time.Minute) 676 defer cancel() 677 st, err := remoteConn.NewStream(ctx, handlerTest, []byte(testPayload)) 678 errFatal(err) 679 680 // Wait for the server to send the first response. 681 <-serverSent 682 683 // Now do 100 other requests to ensure that the server doesn't block. 684 for i := 0; i < 100; i++ { 685 _, err := remoteConn.Request(ctx, handlerTest2, []byte(testPayload)) 686 errFatal(err) 687 } 688 // Drain responses 689 got := 0 690 for resp := range st.responses { 691 // t.Log("got response", resp) 692 errFatal(resp.Err) 693 if resp.Msg[0] != byte(got) { 694 t.Error("expected response", got, "got", resp.Msg[0]) 695 } 696 got++ 697 } 698 if got != 100 { 699 t.Error("expected 100 responses, got", got) 700 } 701 } 702 703 func testServerInCongestion(t *testing.T, local, remote *Manager) { 704 defer testlogger.T.SetErrorTB(t)() 705 errFatal := func(err error) { 706 t.Helper() 707 if err != nil { 708 t.Fatal(err) 709 } 710 } 711 712 // We fake a local and remote server. 713 remoteHost := remote.HostName() 714 715 // 1: Echo 716 processHandler := make(chan struct{}) 717 register := func(manager *Manager) { 718 errFatal(manager.RegisterStreamingHandler(handlerTest, StreamHandler{ 719 Handle: func(ctx context.Context, payload []byte, request <-chan []byte, resp chan<- []byte) *RemoteErr { 720 // Block incoming requests. 721 var n byte 722 <-processHandler 723 for { 724 select { 725 case in, ok := <-request: 726 if !ok { 727 return nil 728 } 729 if in[0] != n { 730 return NewRemoteErrString(fmt.Sprintf("expected incoming %d, got %d", n, in[0])) 731 } 732 n++ 733 resp <- append([]byte{}, in...) 734 case <-ctx.Done(): 735 return NewRemoteErr(ctx.Err()) 736 } 737 } 738 }, 739 OutCapacity: 5, 740 InCapacity: 5, 741 })) 742 errFatal(manager.RegisterSingleHandler(handlerTest2, func(payload []byte) ([]byte, *RemoteErr) { 743 // Simple roundtrip 744 return append([]byte{}, payload...), nil 745 })) 746 } 747 register(local) 748 register(remote) 749 750 remoteConn := local.Connection(remoteHost) 751 const testPayload = "Hello Grid World!" 752 753 ctx, cancel := context.WithTimeout(context.Background(), time.Minute) 754 defer cancel() 755 st, err := remoteConn.NewStream(ctx, handlerTest, []byte(testPayload)) 756 errFatal(err) 757 758 // Start sending requests. 759 go func() { 760 for i := byte(0); i < 100; i++ { 761 st.Requests <- []byte{i} 762 } 763 close(st.Requests) 764 }() 765 // Now do 100 other requests to ensure that the server doesn't block. 766 for i := 0; i < 100; i++ { 767 _, err := remoteConn.Request(ctx, handlerTest2, []byte(testPayload)) 768 errFatal(err) 769 } 770 // Start processing requests. 771 close(processHandler) 772 773 // Drain responses 774 got := 0 775 for resp := range st.responses { 776 // t.Log("got response", resp) 777 errFatal(resp.Err) 778 if resp.Msg[0] != byte(got) { 779 t.Error("expected response", got, "got", resp.Msg[0]) 780 } 781 got++ 782 } 783 if got != 100 { 784 t.Error("expected 100 responses, got", got) 785 } 786 } 787 788 func testGenericsStreamRoundtrip(t *testing.T, local, remote *Manager) { 789 defer testlogger.T.SetErrorTB(t)() 790 defer timeout(5 * time.Second)() 791 errFatal := func(err error) { 792 t.Helper() 793 if err != nil { 794 t.Fatal(err) 795 } 796 } 797 798 // We fake a local and remote server. 799 remoteHost := remote.HostName() 800 handler := NewStream[*testRequest, *testRequest, *testResponse](handlerTest, newTestRequest, newTestRequest, newTestResponse) 801 handler.InCapacity = 1 802 handler.OutCapacity = 1 803 const payloads = 10 804 805 // 1: Echo 806 register := func(manager *Manager) { 807 errFatal(handler.Register(manager, func(ctx context.Context, pp *testRequest, in <-chan *testRequest, out chan<- *testResponse) *RemoteErr { 808 n := 0 809 for i := range in { 810 if n > payloads { 811 panic("too many requests") 812 } 813 814 // t.Log("Got request:", *i) 815 out <- &testResponse{ 816 OrgNum: i.Num + pp.Num, 817 OrgString: pp.String + i.String, 818 Embedded: *i, 819 } 820 n++ 821 } 822 return nil 823 })) 824 } 825 register(local) 826 register(remote) 827 828 // local to remote 829 remoteConn := local.Connection(remoteHost) 830 const testPayload = "Hello Grid World!" 831 832 start := time.Now() 833 stream, err := handler.Call(context.Background(), remoteConn, &testRequest{Num: 1, String: testPayload}) 834 errFatal(err) 835 go func() { 836 defer close(stream.Requests) 837 for i := 0; i < payloads; i++ { 838 // t.Log("sending new client request") 839 stream.Requests <- &testRequest{Num: i, String: testPayload} 840 } 841 }() 842 var n int 843 err = stream.Results(func(resp *testResponse) error { 844 const wantString = testPayload + testPayload 845 if resp.OrgString != testPayload+testPayload { 846 t.Errorf("want %q, got %q", wantString, resp.OrgString) 847 } 848 if resp.OrgNum != n+1 { 849 t.Errorf("want %d, got %d", n+1, resp.OrgNum) 850 } 851 handler.PutResponse(resp) 852 n++ 853 return nil 854 }) 855 errFatal(err) 856 t.Log("EOF.", payloads, " Roundtrips:", time.Since(start)) 857 } 858 859 func testGenericsStreamRoundtripSubroute(t *testing.T, local, remote *Manager) { 860 defer testlogger.T.SetErrorTB(t)() 861 defer timeout(5 * time.Second)() 862 errFatal := func(err error) { 863 t.Helper() 864 if err != nil { 865 t.Fatal(err) 866 } 867 } 868 869 // We fake a local and remote server. 870 remoteHost := remote.HostName() 871 handler := NewStream[*testRequest, *testRequest, *testResponse](handlerTest, newTestRequest, newTestRequest, newTestResponse) 872 handler.InCapacity = 1 873 handler.OutCapacity = 1 874 const payloads = 10 875 876 // 1: Echo 877 register := func(manager *Manager) { 878 errFatal(handler.Register(manager, func(ctx context.Context, pp *testRequest, in <-chan *testRequest, out chan<- *testResponse) *RemoteErr { 879 sub := GetSubroute(ctx) 880 if sub != "subroute/1" { 881 t.Fatal("expected subroute/1, got", sub) 882 } 883 n := 0 884 for i := range in { 885 if n > payloads { 886 panic("too many requests") 887 } 888 889 // t.Log("Got request:", *i) 890 out <- &testResponse{ 891 OrgNum: i.Num + pp.Num, 892 OrgString: pp.String + i.String, 893 Embedded: *i, 894 } 895 n++ 896 } 897 return nil 898 }, "subroute", "1")) 899 } 900 register(local) 901 register(remote) 902 903 // local to remote 904 remoteConn := local.Connection(remoteHost) 905 const testPayload = "Hello Grid World!" 906 // Add subroute 907 remoteSub := remoteConn.Subroute(strings.Join([]string{"subroute", "1"}, "/")) 908 909 start := time.Now() 910 stream, err := handler.Call(context.Background(), remoteSub, &testRequest{Num: 1, String: testPayload}) 911 errFatal(err) 912 go func() { 913 defer close(stream.Requests) 914 for i := 0; i < payloads; i++ { 915 // t.Log("sending new client request") 916 stream.Requests <- &testRequest{Num: i, String: testPayload} 917 } 918 }() 919 var n int 920 err = stream.Results(func(resp *testResponse) error { 921 // t.Logf("got resp: %+v", *resp.Msg) 922 const wantString = testPayload + testPayload 923 if resp.OrgString != testPayload+testPayload { 924 t.Errorf("want %q, got %q", wantString, resp.OrgString) 925 } 926 if resp.OrgNum != n+1 { 927 t.Errorf("want %d, got %d", n+1, resp.OrgNum) 928 } 929 handler.PutResponse(resp) 930 n++ 931 return nil 932 }) 933 934 errFatal(err) 935 t.Log("EOF.", payloads, " Roundtrips:", time.Since(start)) 936 } 937 938 // testServerStreamResponseBlocked will test if server can handle a blocked response stream 939 func testServerStreamResponseBlocked(t *testing.T, local, remote *Manager) { 940 defer testlogger.T.SetErrorTB(t)() 941 errFatal := func(err error) { 942 t.Helper() 943 if err != nil { 944 t.Fatal(err) 945 } 946 } 947 948 // We fake a local and remote server. 949 remoteHost := remote.HostName() 950 951 // 1: Echo 952 serverSent := make(chan struct{}) 953 serverCanceled := make(chan struct{}) 954 register := func(manager *Manager) { 955 errFatal(manager.RegisterStreamingHandler(handlerTest, StreamHandler{ 956 Handle: func(ctx context.Context, payload []byte, _ <-chan []byte, resp chan<- []byte) *RemoteErr { 957 // Send many responses. 958 // Test that this doesn't block. 959 for i := byte(0); i < 100; i++ { 960 select { 961 case resp <- []byte{i}: 962 // ok 963 case <-ctx.Done(): 964 close(serverCanceled) 965 return NewRemoteErr(ctx.Err()) 966 } 967 if i == 1 { 968 close(serverSent) 969 } 970 } 971 return nil 972 }, 973 OutCapacity: 1, 974 InCapacity: 0, 975 })) 976 } 977 register(local) 978 register(remote) 979 980 remoteConn := local.Connection(remoteHost) 981 const testPayload = "Hello Grid World!" 982 983 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 984 985 st, err := remoteConn.NewStream(ctx, handlerTest, []byte(testPayload)) 986 errFatal(err) 987 988 // Wait for the server to send the first response. 989 <-serverSent 990 991 // Read back from the stream and block. 992 nowBlocking := make(chan struct{}) 993 stopBlocking := make(chan struct{}) 994 defer close(stopBlocking) 995 go func() { 996 st.Results(func(b []byte) error { 997 close(nowBlocking) 998 // Block until test is done. 999 <-stopBlocking 1000 return nil 1001 }) 1002 }() 1003 1004 <-nowBlocking 1005 // Wait for the receiver channel to fill. 1006 for len(st.responses) != cap(st.responses) { 1007 time.Sleep(time.Millisecond) 1008 } 1009 cancel() 1010 <-serverCanceled 1011 local.debugMsg(debugIsOutgoingClosed, st.muxID, func(closed bool) { 1012 if !closed { 1013 t.Error("expected outgoing closed") 1014 } else { 1015 t.Log("outgoing was closed") 1016 } 1017 }) 1018 1019 // Drain responses and check if error propagated. 1020 err = st.Results(func(b []byte) error { 1021 return nil 1022 }) 1023 if !errors.Is(err, context.Canceled) { 1024 t.Error("expected context.Canceled, got", err) 1025 } 1026 } 1027 1028 func timeout(after time.Duration) (cancel func()) { 1029 c := time.After(after) 1030 cc := make(chan struct{}) 1031 go func() { 1032 select { 1033 case <-cc: 1034 return 1035 case <-c: 1036 buf := make([]byte, 1<<20) 1037 stacklen := runtime.Stack(buf, true) 1038 fmt.Printf("=== Timeout, assuming deadlock ===\n*** goroutine dump...\n%s\n*** end\n", string(buf[:stacklen])) 1039 os.Exit(2) 1040 } 1041 }() 1042 return func() { 1043 close(cc) 1044 } 1045 } 1046 1047 func assertNoActive(t *testing.T, c *Connection) { 1048 t.Helper() 1049 // Tiny bit racy for tests, but we try to play nice. 1050 for i := 10; i >= 0; i-- { 1051 runtime.Gosched() 1052 stats := c.Stats() 1053 if stats.IncomingStreams != 0 { 1054 if i > 0 { 1055 time.Sleep(100 * time.Millisecond) 1056 continue 1057 } 1058 var found []uint64 1059 c.inStream.Range(func(key uint64, value *muxServer) bool { 1060 found = append(found, key) 1061 return true 1062 }) 1063 t.Errorf("expected no active streams, got %d incoming: %v", stats.IncomingStreams, found) 1064 } 1065 if stats.OutgoingStreams != 0 { 1066 if i > 0 { 1067 time.Sleep(100 * time.Millisecond) 1068 continue 1069 } 1070 var found []uint64 1071 c.outgoing.Range(func(key uint64, value *muxClient) bool { 1072 found = append(found, key) 1073 return true 1074 }) 1075 t.Errorf("expected no active streams, got %d outgoing: %v", stats.OutgoingStreams, found) 1076 } 1077 return 1078 } 1079 } 1080 1081 // Inserted manually. 1082 func _() { 1083 // An "invalid array index" compiler error signifies that the constant values have changed. 1084 // Re-run the stringer command to generate them again. 1085 var x [1]struct{} 1086 _ = x[StateUnconnected-0] 1087 _ = x[StateConnecting-1] 1088 _ = x[StateConnected-2] 1089 _ = x[StateConnectionError-3] 1090 _ = x[StateShutdown-4] 1091 } 1092 1093 const stateName = "UnconnectedConnectingConnectedConnectionErrorShutdown" 1094 1095 var stateIndex = [...]uint8{0, 11, 21, 30, 45, 53} 1096 1097 func (i State) String() string { 1098 if i >= State(len(stateIndex)-1) { 1099 return "State(" + strconv.FormatInt(int64(i), 10) + ")" 1100 } 1101 return stateName[stateIndex[i]:stateIndex[i+1]] 1102 }