// calls remote service and writes response to s.response func (s *requestSender) MakeCall(ctx context.Context, res *[]byte) <-chan error { errC := make(chan error) go func() { defer close(errC) peer := s.channel.Peers().GetOrAdd(s.destination) call, err := peer.BeginCall(ctx, s.service, s.endpoint, &tchannel.CallOptions{ Format: s.format, }) if err != nil { errC <- err return } var arg3 []byte if s.format == tchannel.Thrift { _, arg3, _, err = raw.WriteArgs(call, []byte{0, 0}, s.request) } else { _, arg3, _, err = raw.WriteArgs(call, nil, s.request) } if err != nil { errC <- err return } *res = arg3 errC <- nil }() return errC }
// calls remote service and writes response to s.response func (s *requestSender) MakeCall(ctx context.Context, res *[]byte, fwdError *error, appError *error) <-chan bool { done := make(chan bool, 1) go func() { defer close(done) peer := s.channel.Peers().GetOrAdd(s.destination) call, err := peer.BeginCall(ctx, s.service, s.endpoint, &tchannel.CallOptions{ Format: s.format, }) if err != nil { *fwdError = err done <- true return } var arg3 []byte if s.format == tchannel.Thrift { _, arg3, _, err = raw.WriteArgs(call, []byte{0, 0}, s.request) } else { var resp *tchannel.OutboundCallResponse _, arg3, resp, err = raw.WriteArgs(call, nil, s.request) // check if the response is an application level error if err == nil && resp.ApplicationError() { // parse the json from the application level error errResp := struct { Type string `json:"type"` Message string `json:"message"` }{} err = json.Unmarshal(arg3, &errResp) // if parsing succeeded return the error as an application error if err == nil { *appError = errors.New(errResp.Message) done <- true return } } } if err != nil { *fwdError = err done <- true return } *res = arg3 done <- true }() return done }
func (t *closeSemanticsTest) call(from *Channel, to *Channel) error { call, err := t.startCall(from, to, "call") if err == nil { _, _, _, err = raw.WriteArgs(call, nil, nil) } return err }
func TestReuseConnection(t *testing.T) { ctx, cancel := NewContext(time.Second) defer cancel() // Since we're specifically testing that connections between hosts are re-used, // we can't interpose a relay in this test. s1Opts := testutils.NewOpts().SetServiceName("s1").NoRelay() testutils.WithTestServer(t, s1Opts, func(ts *testutils.TestServer) { ch2 := ts.NewServer(&testutils.ChannelOpts{ServiceName: "s2"}) hostPort2 := ch2.PeerInfo().HostPort defer ch2.Close() ts.Register(raw.Wrap(newTestHandler(t)), "echo") ch2.Register(raw.Wrap(newTestHandler(t)), "echo") outbound, err := ts.Server().BeginCall(ctx, hostPort2, "s2", "echo", nil) require.NoError(t, err) outboundConn, outboundNetConn := OutboundConnection(outbound) // Try to make another call at the same time, should reuse the same connection. outbound2, err := ts.Server().BeginCall(ctx, hostPort2, "s2", "echo", nil) require.NoError(t, err) outbound2Conn, _ := OutboundConnection(outbound) assert.Equal(t, outboundConn, outbound2Conn) // Wait for the connection to be marked as active in ch2. assert.True(t, testutils.WaitFor(time.Second, func() bool { return ch2.IntrospectState(nil).NumConnections > 0 }), "ch2 does not have any active connections") // When ch2 tries to call the test server, it should reuse the existing // inbound connection the test server. Of course, this only works if the // test server -> ch2 call wasn't relayed. outbound3, err := ch2.BeginCall(ctx, ts.HostPort(), "s1", "echo", nil) require.NoError(t, err) _, outbound3NetConn := OutboundConnection(outbound3) assert.Equal(t, outboundNetConn.RemoteAddr(), outbound3NetConn.LocalAddr()) assert.Equal(t, outboundNetConn.LocalAddr(), outbound3NetConn.RemoteAddr()) // Ensure all calls can complete in parallel. var wg sync.WaitGroup for _, call := range []*OutboundCall{outbound, outbound2, outbound3} { wg.Add(1) go func(call *OutboundCall) { defer wg.Done() resp1, resp2, _, err := raw.WriteArgs(call, []byte("arg2"), []byte("arg3")) require.NoError(t, err) assert.Equal(t, resp1, []byte("arg2"), "result does match argument") assert.Equal(t, resp2, []byte("arg3"), "result does match argument") }(call) } wg.Wait() }) }
func runTChannelClient(b *testing.B, c *tchannel.Channel, hostPort string) { headers := []byte{0x00, 0x00} // TODO: YARPC TChannel should support empty arg2 for i := 0; i < b.N; i++ { ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() call, err := c.BeginCall(ctx, hostPort, "server", "echo", &tchannel.CallOptions{Format: tchannel.Raw}) require.NoError(b, err, "BeginCall %v failed", i+1) _, _, _, err = traw.WriteArgs(call, headers, _reqBody) require.NoError(b, err, "request %v failed", i+1) } }
func TestReuseConnection(t *testing.T) { ctx, cancel := NewContext(time.Second) defer cancel() s1Opts := &testutils.ChannelOpts{ServiceName: "s1"} WithVerifiedServer(t, s1Opts, func(ch1 *Channel, hostPort1 string) { s2Opts := &testutils.ChannelOpts{ServiceName: "s2"} WithVerifiedServer(t, s2Opts, func(ch2 *Channel, hostPort2 string) { ch1.Register(raw.Wrap(newTestHandler(t)), "echo") ch2.Register(raw.Wrap(newTestHandler(t)), "echo") // We need the servers to have their peers set before making outgoing calls // for the outgoing calls to contain the correct peerInfo. require.True(t, testutils.WaitFor(time.Second, func() bool { return !ch1.PeerInfo().IsEphemeral() && !ch2.PeerInfo().IsEphemeral() })) outbound, err := ch1.BeginCall(ctx, hostPort2, "s2", "echo", nil) require.NoError(t, err) outboundConn, outboundNetConn := OutboundConnection(outbound) // Try to make another call at the same time, should reuse the same connection. outbound2, err := ch1.BeginCall(ctx, hostPort2, "s2", "echo", nil) require.NoError(t, err) outbound2Conn, _ := OutboundConnection(outbound) assert.Equal(t, outboundConn, outbound2Conn) // When ch2 tries to call ch1, it should reuse the inbound connection from ch1. outbound3, err := ch2.BeginCall(ctx, hostPort1, "s1", "echo", nil) require.NoError(t, err) _, outbound3NetConn := OutboundConnection(outbound3) assert.Equal(t, outboundNetConn.RemoteAddr(), outbound3NetConn.LocalAddr()) assert.Equal(t, outboundNetConn.LocalAddr(), outbound3NetConn.RemoteAddr()) // Ensure all calls can complete in parallel. var wg sync.WaitGroup for _, call := range []*OutboundCall{outbound, outbound2, outbound3} { wg.Add(1) go func(call *OutboundCall) { defer wg.Done() resp1, resp2, _, err := raw.WriteArgs(call, []byte("arg2"), []byte("arg3")) require.NoError(t, err) assert.Equal(t, resp1, []byte("arg2"), "result does match argument") assert.Equal(t, resp2, []byte("arg3"), "result does match argument") }(call) } wg.Wait() }) }) }
func TestInboundConnection_CallOptions(t *testing.T) { ctx, cancel := NewContext(time.Second) defer cancel() testutils.WithTestServer(t, nil, func(server *testutils.TestServer) { server.RegisterFunc("test", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { assert.Equal(t, "client", CurrentCall(ctx).CallerName(), "Expected caller name to be passed through") return &raw.Res{}, nil }) backendName := server.ServiceName() proxyCh := server.NewServer(&testutils.ChannelOpts{ServiceName: "proxy"}) defer proxyCh.Close() subCh := proxyCh.GetSubChannel(backendName) subCh.SetHandler(HandlerFunc(func(ctx context.Context, inbound *InboundCall) { outbound, err := proxyCh.BeginCall(ctx, server.HostPort(), backendName, inbound.MethodString(), inbound.CallOptions()) require.NoError(t, err, "Create outbound call failed") arg2, arg3, _, err := raw.WriteArgs(outbound, []byte("hello"), []byte("world")) require.NoError(t, err, "Write outbound call failed") require.NoError(t, raw.WriteResponse(inbound.Response(), &raw.Res{ Arg2: arg2, Arg3: arg3, }), "Write response failed") })) clientCh := server.NewClient(&testutils.ChannelOpts{ ServiceName: "client", }) defer clientCh.Close() _, _, _, err := raw.Call(ctx, clientCh, proxyCh.PeerInfo().HostPort, backendName, "test", nil, nil) require.NoError(t, err, "Call through proxy failed") }) }