trpc.group/trpc-go/trpc-go@v1.0.3/client/client_test.go (about) 1 // 2 // 3 // Tencent is pleased to support the open source community by making tRPC available. 4 // 5 // Copyright (C) 2023 THL A29 Limited, a Tencent company. 6 // All rights reserved. 7 // 8 // If you have downloaded a copy of the tRPC source code from Tencent, 9 // please note that tRPC source code is licensed under the Apache 2.0 License, 10 // A copy of the Apache 2.0 License is included in this file. 11 // 12 // 13 14 package client_test 15 16 import ( 17 "context" 18 "errors" 19 "fmt" 20 "net" 21 "testing" 22 "time" 23 24 "github.com/stretchr/testify/assert" 25 "github.com/stretchr/testify/require" 26 trpcpb "trpc.group/trpc/trpc-protocol/pb/go/trpc" 27 28 trpc "trpc.group/trpc-go/trpc-go" 29 "trpc.group/trpc-go/trpc-go/client" 30 "trpc.group/trpc-go/trpc-go/codec" 31 "trpc.group/trpc-go/trpc-go/errs" 32 "trpc.group/trpc-go/trpc-go/filter" 33 "trpc.group/trpc-go/trpc-go/naming/registry" 34 "trpc.group/trpc-go/trpc-go/naming/selector" 35 "trpc.group/trpc-go/trpc-go/transport" 36 37 _ "trpc.group/trpc-go/trpc-go" 38 ) 39 40 // go test -v -coverprofile=cover.out 41 // go tool cover -func=cover.out 42 43 func TestMain(m *testing.M) { 44 transport.DefaultClientTransport = &fakeTransport{} 45 selector.Register("fake", &fakeSelector{}) // fake://{endpoint} 46 transport.RegisterClientTransport("fake", &fakeTransport{}) 47 m.Run() 48 } 49 50 func TestClient(t *testing.T) { 51 ctx := context.Background() 52 codec.RegisterSerializer(0, &codec.NoopSerialization{}) 53 codec.Register("fake", nil, &fakeCodec{}) 54 55 cli := client.New() 56 require.Equal(t, cli, client.DefaultClient) 57 58 // test if response is valid 59 reqBody := &codec.Body{Data: []byte("body")} 60 rspBody := &codec.Body{} 61 require.Nil(t, cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("ip://127.0.0.1:8080"), 62 client.WithTimeout(time.Second), client.WithProtocol("fake"))) 63 require.Equal(t, []byte("body"), rspBody.Data) 64 65 // test setting req/resp head 66 reqhead := ®istry.Node{} 67 rsphead := ®istry.Node{} 68 require.Nil(t, cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("ip://127.0.0.1:8080"), 69 client.WithReqHead(reqhead), client.WithRspHead(rsphead), client.WithProtocol("fake"))) 70 71 // test client options 72 require.Nil(t, cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("ip://127.0.0.1:8080"), 73 client.WithTimeout(time.Second), 74 client.WithServiceName("trpc.app.callee.service"), 75 client.WithCallerServiceName("trpc.app.caller.service"), 76 client.WithSerializationType(codec.SerializationTypeNoop), 77 client.WithCompressType(codec.CompressTypeGzip), 78 client.WithCurrentSerializationType(codec.SerializationTypeNoop), 79 client.WithCurrentCompressType(codec.CompressTypeNoop), 80 client.WithMetaData("key", []byte("value")), 81 client.WithProtocol("fake"))) 82 83 // test selecting node with network: udp 84 require.Nil(t, cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("fake://udpnetwork"), 85 client.WithTimeout(time.Second), client.WithProtocol("fake"))) 86 87 // test selecting node with network: unknown, which will use tcp by default 88 require.Nil(t, cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("fake://unknownnetwork"), 89 client.WithTimeout(time.Second), client.WithProtocol("fake"))) 90 91 // test setting namespace in msg 92 ctx = context.Background() 93 ctx, msg := codec.WithNewMessage(ctx) 94 msg.WithNamespace("Development") // getServiceInfoOptions will set env info according to the namespace 95 require.Nil(t, cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("ip://127.0.0.1:8080"), 96 client.WithTimeout(time.Second), client.WithProtocol("fake"))) 97 require.Equal(t, []byte("body"), rspBody.Data) 98 99 // test that env info from upstream service has higher priority 100 ctx = context.Background() 101 ctx, msg = codec.WithNewMessage(ctx) 102 msg.WithEnvTransfer("faketransfer") // env info from upstream service exists 103 require.Nil(t, cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("ip://127.0.0.1:8080"), 104 client.WithTimeout(time.Second), client.WithProtocol("fake"))) 105 require.Equal(t, []byte("body"), rspBody.Data) 106 107 // test disabling service router, which will clear env info from msg 108 ctx = context.Background() 109 ctx, msg = codec.WithNewMessage(ctx) 110 msg.WithEnvTransfer("faketransfer") // env info from upstream service exists 111 require.Nil(t, cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("ip://127.0.0.1:8080"), 112 client.WithTimeout(time.Second), client.WithProtocol("fake"), 113 client.WithDisableServiceRouter())) // opts that disables service router 114 require.Equal(t, []byte("body"), rspBody.Data) 115 require.Equal(t, msg.EnvTransfer(), "") // env info from upstream service was cleared 116 117 // test setting CalleeMethod in opts 118 // updateMsg will then update CalleeMethod in msg 119 ctx = context.Background() 120 ctx, msg = codec.WithNewMessage(ctx) 121 require.Nil(t, cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("ip://127.0.0.1:8080"), 122 client.WithTimeout(time.Second), client.WithProtocol("fake"), 123 client.WithCalleeMethod("fakemethod"))) // opts 中指定了 CalleeMethod 124 require.Equal(t, msg.CalleeMethod(), "fakemethod") // msg 中的 CalleeMethod 被更新 125 126 // test that the parameters can be extracted from msg in the prev filter 127 ctx = context.Background() 128 ctx, msg = codec.WithNewMessage(ctx) 129 rid := uint32(100000) 130 msg.WithRequestID(uint32(rid)) 131 132 require.Nil(t, cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("ip://127.0.0.1:8080"), 133 client.WithTimeout(time.Second), client.WithProtocol("fake"), 134 client.WithFilter(func(ctx context.Context, req interface{}, rsp interface{}, f filter.ClientHandleFunc) (err error) { 135 msg := trpc.Message(ctx) 136 require.Equal(t, rid, msg.RequestID()) 137 return f(ctx, req, rsp) 138 }))) 139 140 // test setting CallType in opts 141 // updateMsg will then update CallType in msg 142 ctx = context.Background() 143 head := &trpcpb.RequestProtocol{} 144 ctx, msg = codec.WithNewMessage(ctx) 145 require.Nil(t, cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("ip://127.0.0.1:8080"), 146 client.WithProtocol("fake"), 147 client.WithSendOnly(), 148 client.WithReqHead(head), 149 )) 150 require.Equal(t, msg.CallType(), codec.SendOnly) 151 } 152 153 func TestClientFail(t *testing.T) { 154 ctx := context.Background() 155 codec.RegisterSerializer(0, &codec.NoopSerialization{}) 156 codec.Register("fake", nil, &fakeCodec{}) 157 158 cli := client.New() 159 require.Equal(t, cli, client.DefaultClient) 160 161 reqBody := &codec.Body{Data: []byte("body")} 162 rspBody := &codec.Body{} 163 // test code failure 164 require.NotNil(t, cli.Invoke(ctx, reqBody, rspBody, 165 client.WithTarget("ip://127.0.0.1:8080"), 166 client.WithTimeout(time.Second), 167 client.WithSerializationType(codec.SerializationTypeNoop))) 168 169 // test invalid target 170 err := cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("ip/:/127.0.0.1:8080"), 171 client.WithProtocol("fake")) 172 require.NotNil(t, err) 173 require.Contains(t, err.Error(), "invalid") 174 175 // test target selector that not exists 176 err = cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("cl6://127.0.0.1:8080"), 177 client.WithProtocol("fake")) 178 require.NotNil(t, err) 179 require.Contains(t, err.Error(), "not exist") 180 181 // test recording selected node 182 node := ®istry.Node{} 183 require.Nil(t, cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("ip://127.0.0.1:8080"), 184 client.WithSelectorNode(node), client.WithProtocol("fake"))) 185 require.Equal(t, node.Address, "127.0.0.1:8080") 186 require.Equal(t, node.ServiceName, "127.0.0.1:8080") 187 require.Empty(t, node.Network) 188 189 // test encode failure 190 reqBody = &codec.Body{Data: []byte("failbody")} 191 require.NotNil(t, cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("ip://127.0.0.1:8080"), 192 client.WithProtocol("fake"), client.WithSerializationType(codec.SerializationTypeNoop))) 193 194 // test network failure 195 reqBody = &codec.Body{Data: []byte("callfail")} 196 err = cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("ip://127.0.0.1:8080"), 197 client.WithProtocol("fake"), client.WithSerializationType(codec.SerializationTypeNoop)) 198 assert.NotNil(t, err) 199 200 // test response failure 201 reqBody = &codec.Body{Data: []byte("businessfail")} 202 err = cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("ip://127.0.0.1:8080"), 203 client.WithProtocol("fake"), client.WithSerializationType(codec.SerializationTypeNoop)) 204 205 reqBody = &codec.Body{Data: []byte("msgfail")} 206 err = cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("ip://127.0.0.1:8080"), 207 client.WithProtocol("fake"), client.WithSerializationType(codec.SerializationTypeNoop)) 208 assert.NotNil(t, err) 209 210 // test nil rsp 211 reqBody = &codec.Body{Data: []byte("nilrsp")} 212 err = cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("ip://127.0.0.1:8080"), 213 client.WithProtocol("fake"), client.WithSerializationType(codec.SerializationTypeNoop)) 214 assert.Nil(t, err) 215 216 // test timeout 217 reqBody = &codec.Body{Data: []byte("timeout")} 218 err = cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("ip://127.0.0.1:8080"), 219 client.WithProtocol("fake"), client.WithSerializationType(codec.SerializationTypeNoop)) 220 assert.NotNil(t, err) 221 222 // test select node failure 223 reqBody = &codec.Body{Data: []byte("body")} 224 err = cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("fake://selectfail"), 225 client.WithTimeout(time.Second), client.WithProtocol("fake")) 226 assert.NotNil(t, err) 227 228 // test selecting the node with empty addr 229 err = cli.Invoke(ctx, reqBody, rspBody, client.WithTarget("fake://emptynode"), 230 client.WithTimeout(time.Second), client.WithProtocol("fake")) 231 assert.NotNil(t, err) 232 233 } 234 235 func TestClientAddrResolve(t *testing.T) { 236 ctx := context.Background() 237 codec.RegisterSerializer(0, &codec.NoopSerialization{}) 238 codec.Register("fake", nil, &fakeCodec{}) 239 cli := client.New() 240 241 reqBody := &codec.Body{Data: []byte("body")} 242 rspBody := &codec.Body{} 243 // test target with ip schema 244 nctx, _ := codec.WithNewMessage(ctx) 245 _ = cli.Invoke(nctx, reqBody, rspBody, client.WithTarget("ip://127.0.0.1:8080"), client.WithProtocol("fake")) 246 assert.Equal(t, "127.0.0.1:8080", codec.Message(nctx).RemoteAddr().String()) 247 248 // test target with ip schema and network: tcp 249 nctx, _ = codec.WithNewMessage(ctx) 250 _ = cli.Invoke(nctx, reqBody, rspBody, 251 client.WithTarget("ip://127.0.0.1:8080"), 252 client.WithNetwork("tcp"), 253 client.WithProtocol("fake"), 254 ) 255 require.Equal(t, "127.0.0.1:8080", codec.Message(nctx).RemoteAddr().String()) 256 257 // test target with hostname schema 258 nctx, _ = codec.WithNewMessage(ctx) 259 _ = cli.Invoke(nctx, reqBody, rspBody, client.WithTarget("ip://www.qq.com:8080"), client.WithProtocol("fake")) 260 assert.Nil(t, codec.Message(nctx).RemoteAddr()) 261 262 // test calling target with ip schema failure 263 nctx, msg := codec.WithNewMessage(ctx) 264 reqBody = &codec.Body{Data: []byte("callfail")} 265 err := cli.Invoke(nctx, reqBody, rspBody, client.WithTarget("ip://127.0.0.1:8080"), client.WithProtocol("fake")) 266 assert.NotNil(t, err) 267 assert.Equal(t, "127.0.0.1:8080", msg.RemoteAddr().String()) 268 269 // test target with unix schema 270 nctx, _ = codec.WithNewMessage(ctx) 271 _ = cli.Invoke(nctx, reqBody, rspBody, 272 client.WithTarget("unix://temp.sock"), 273 client.WithNetwork("unix"), 274 client.WithProtocol("fake"), 275 ) 276 require.Equal(t, "temp.sock", codec.Message(nctx).RemoteAddr().String()) 277 } 278 279 func TestTimeout(t *testing.T) { 280 codec.RegisterSerializer(0, &codec.NoopSerialization{}) 281 codec.Register("fake", nil, &fakeCodec{}) 282 target, protocol := "ip://127.0.0.1:8080", "fake" 283 284 cli := client.New() 285 rspBody := &codec.Body{} 286 err := cli.Invoke(context.Background(), 287 &codec.Body{Data: []byte("timeout")}, rspBody, 288 client.WithTarget(target), 289 client.WithProtocol(protocol)) 290 require.NotNil(t, err) 291 e, ok := err.(*errs.Error) 292 require.True(t, ok) 293 require.Equal(t, errs.RetClientTimeout, e.Code) 294 295 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) 296 defer cancel() 297 err = cli.Invoke(ctx, 298 &codec.Body{Data: []byte("timeout")}, rspBody, 299 client.WithTarget(target), 300 client.WithProtocol(protocol)) 301 require.NotNil(t, err) 302 e, ok = err.(*errs.Error) 303 require.True(t, ok) 304 require.Equal(t, errs.RetClientFullLinkTimeout, e.Code) 305 } 306 307 func TestSameCalleeMultiServiceName(t *testing.T) { 308 callee := "trpc.test.pbcallee" 309 serviceNames := []string{ 310 "trpc.test.helloworld0", 311 "trpc.test.helloworld1", 312 "trpc.test.helloworld2", 313 "trpc.test.helloworld3", 314 } 315 for i := range serviceNames { 316 if i != 2 { 317 require.Nil(t, client.RegisterClientConfig(callee, &client.BackendConfig{ 318 ServiceName: serviceNames[i], 319 Compression: codec.CompressTypeSnappy, 320 })) 321 continue 322 } 323 require.Nil(t, client.RegisterClientConfig(callee, &client.BackendConfig{ 324 ServiceName: serviceNames[i], 325 Compression: codec.CompressTypeBlockSnappy, 326 })) 327 } 328 ctx, msg := codec.EnsureMessage(context.Background()) 329 msg.WithCalleeServiceName(callee) 330 require.NotNil(t, client.DefaultClient.Invoke(ctx, nil, nil, client.WithServiceName(serviceNames[0]))) 331 require.Equal(t, codec.CompressTypeSnappy, msg.CompressType()) 332 ctx, msg = codec.EnsureMessage(context.Background()) 333 msg.WithCalleeServiceName(callee) 334 require.NotNil(t, client.DefaultClient.Invoke(ctx, nil, nil, client.WithServiceName(serviceNames[2]))) 335 require.Equal(t, codec.CompressTypeBlockSnappy, msg.CompressType()) 336 } 337 338 func TestMultiplexedUseLatestMsg(t *testing.T) { 339 codec.RegisterSerializer(0, &codec.NoopSerialization{}) 340 const target = "ip://127.0.0.1:8080" 341 342 rspBody := &codec.Body{} 343 require.Nil(t, client.New().Invoke(context.Background(), 344 &codec.Body{Data: []byte(t.Name())}, rspBody, 345 client.WithTarget(target), 346 client.WithTransport(&multiplexedTransport{ 347 require: func(_ context.Context, _ []byte, opts ...transport.RoundTripOption) { 348 var o transport.RoundTripOptions 349 for _, opt := range opts { 350 opt(&o) 351 } 352 require.NotZero(t, o.Msg.RequestID()) 353 }}), 354 client.WithMultiplexed(true), 355 client.WithFilter(func(ctx context.Context, req, rsp interface{}, next filter.ClientHandleFunc) error { 356 // make a copy of the msg, after next, copy the new msg back. 357 oldMsg := codec.Message(ctx) 358 ctx, msg := codec.WithNewMessage(ctx) 359 codec.CopyMsg(msg, oldMsg) 360 err := next(ctx, req, rsp) 361 codec.CopyMsg(oldMsg, msg) 362 return err 363 }), 364 )) 365 } 366 367 func TestFixTimeout(t *testing.T) { 368 codec.RegisterSerializer(0, &codec.NoopSerialization{}) 369 codec.Register("fake", nil, &fakeCodec{}) 370 target, protocol := "ip://127.0.0.1:8080", "fake" 371 372 cli := client.New() 373 374 rspBody := &codec.Body{} 375 t.Run("RetClientCanceled", func(t *testing.T) { 376 ctx, cancel := context.WithCancel(context.Background()) 377 cancel() 378 err := cli.Invoke(ctx, 379 &codec.Body{Data: []byte("clientCanceled")}, rspBody, 380 client.WithTarget(target), 381 client.WithProtocol(protocol)) 382 require.Equal(t, errs.RetClientCanceled, errs.Code(err)) 383 }) 384 385 t.Run("RetClientFullLinkTimeout", func(t *testing.T) { 386 ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(1*time.Millisecond)) 387 defer cancel() 388 var d time.Duration 389 deadline, ok := t.Deadline() 390 if !ok { 391 d = 5 * time.Second 392 } else { 393 const arbitraryCleanupMargin = 1 * time.Second 394 d = time.Until(deadline) - arbitraryCleanupMargin 395 } 396 timer := time.NewTimer(d) 397 defer timer.Stop() 398 select { 399 case <-timer.C: 400 t.Fatalf(" context not timed out after %v", d) 401 case <-ctx.Done(): 402 } 403 if e := ctx.Err(); e != context.DeadlineExceeded { 404 t.Errorf("c.Err() == %v; want %v", e, context.DeadlineExceeded) 405 } 406 err := cli.Invoke(ctx, 407 &codec.Body{Data: []byte("fixTimeout")}, rspBody, 408 client.WithTarget(target), 409 client.WithProtocol(protocol)) 410 require.Equal(t, errs.RetClientFullLinkTimeout, errs.Code(err)) 411 }) 412 } 413 414 func TestSelectorRemoteAddrUseUserProvidedParser(t *testing.T) { 415 selector.Register(t.Name(), &fSelector{ 416 selectNode: func(s string, option ...selector.Option) (*registry.Node, error) { 417 return ®istry.Node{ 418 Network: t.Name(), 419 Address: t.Name(), 420 ParseAddr: func(network, address string) net.Addr { 421 return newUnresolvedAddr(network, address) 422 }}, nil 423 }, 424 report: func(node *registry.Node, duration time.Duration, err error) error { return nil }, 425 }) 426 fake := "fake" 427 codec.Register(fake, nil, &fakeCodec{}) 428 ctx := trpc.BackgroundContext() 429 require.NotNil(t, client.New().Invoke(ctx, "failbody", nil, 430 client.WithServiceName(t.Name()), 431 client.WithProtocol(fake), 432 client.WithTarget(fmt.Sprintf("%s://xxx", t.Name())))) 433 addr := trpc.Message(ctx).RemoteAddr() 434 require.NotNil(t, addr) 435 require.Equal(t, t.Name(), addr.Network()) 436 require.Equal(t, t.Name(), addr.String()) 437 } 438 439 type multiplexedTransport struct { 440 require func(context.Context, []byte, ...transport.RoundTripOption) 441 fakeTransport 442 } 443 444 func (t *multiplexedTransport) RoundTrip( 445 ctx context.Context, 446 req []byte, 447 opts ...transport.RoundTripOption, 448 ) ([]byte, error) { 449 t.require(ctx, req, opts...) 450 return t.fakeTransport.RoundTrip(ctx, req, opts...) 451 } 452 453 type fakeTransport struct { 454 send func() error 455 recv func() ([]byte, error) 456 close func() 457 } 458 459 func (c *fakeTransport) RoundTrip(ctx context.Context, req []byte, 460 roundTripOpts ...transport.RoundTripOption) (rsp []byte, err error) { 461 time.Sleep(time.Millisecond * 2) 462 if string(req) == "callfail" { 463 return nil, errors.New("transport call fail") 464 } 465 466 if string(req) == "timeout" { 467 return nil, &errs.Error{ 468 Type: errs.ErrorTypeFramework, 469 Code: errs.RetClientTimeout, 470 Msg: "transport call fail", 471 } 472 } 473 474 if string(req) == "nilrsp" { 475 return nil, nil 476 } 477 return req, nil 478 } 479 480 func (c *fakeTransport) Send(ctx context.Context, req []byte, opts ...transport.RoundTripOption) error { 481 if c.send != nil { 482 return c.send() 483 } 484 return nil 485 } 486 487 func (c *fakeTransport) Recv(ctx context.Context, opts ...transport.RoundTripOption) ([]byte, error) { 488 if c.recv != nil { 489 return c.recv() 490 } 491 return []byte("body"), nil 492 } 493 494 func (c *fakeTransport) Init(ctx context.Context, opts ...transport.RoundTripOption) error { 495 return nil 496 } 497 func (c *fakeTransport) Close(ctx context.Context) { 498 if c.close != nil { 499 c.close() 500 } 501 } 502 503 type fakeCodec struct { 504 } 505 506 func (c *fakeCodec) Encode(msg codec.Msg, reqBody []byte) (reqBuf []byte, err error) { 507 if string(reqBody) == "failbody" { 508 return nil, errors.New("encode fail") 509 } 510 return reqBody, nil 511 } 512 513 func (c *fakeCodec) Decode(msg codec.Msg, rspBuf []byte) (rspBody []byte, err error) { 514 if string(rspBuf) == "businessfail" { 515 return nil, errors.New("businessfail") 516 } 517 518 if string(rspBuf) == "msgfail" { 519 msg.WithClientRspErr(errors.New("msgfail")) 520 return nil, nil 521 } 522 return rspBuf, nil 523 } 524 525 type fakeSelector struct { 526 } 527 528 func (c *fakeSelector) Select(serviceName string, opt ...selector.Option) (*registry.Node, error) { 529 if serviceName == "selectfail" { 530 return nil, errors.New("selectfail") 531 } 532 533 if serviceName == "emptynode" { 534 return ®istry.Node{}, nil 535 } 536 537 if serviceName == "udpnetwork" { 538 return ®istry.Node{ 539 Network: "udp", 540 Address: "127.0.0.1:8080", 541 }, nil 542 } 543 544 if serviceName == "unknownnetwork" { 545 return ®istry.Node{ 546 Network: "unknown", 547 Address: "127.0.0.1:8080", 548 }, nil 549 } 550 551 return nil, errors.New("unknown servicename") 552 } 553 554 func (c *fakeSelector) Report(node *registry.Node, cost time.Duration, err error) error { 555 return nil 556 } 557 558 type fSelector struct { 559 selectNode func(string, ...selector.Option) (*registry.Node, error) 560 report func(*registry.Node, time.Duration, error) error 561 } 562 563 func (s *fSelector) Select(serviceName string, opts ...selector.Option) (*registry.Node, error) { 564 return s.selectNode(serviceName, opts...) 565 } 566 567 func (s *fSelector) Report(node *registry.Node, cost time.Duration, err error) error { 568 return s.report(node, cost, err) 569 } 570 571 // newUnresolvedAddr returns a new unresolvedAddr. 572 func newUnresolvedAddr(network, address string) *unresolvedAddr { 573 return &unresolvedAddr{network: network, address: address} 574 } 575 576 var _ net.Addr = (*unresolvedAddr)(nil) 577 578 // unresolvedAddr is a net.Addr which returns the original network or address. 579 type unresolvedAddr struct { 580 network string 581 address string 582 } 583 584 // Network returns the unresolved original network. 585 func (a *unresolvedAddr) Network() string { 586 return a.network 587 } 588 589 // String returns the unresolved original address. 590 func (a *unresolvedAddr) String() string { 591 return a.address 592 }