func Benchmark_TChannel_YARPCToYARPC(b *testing.B) { serverCh, err := tchannel.NewChannel("server", nil) require.NoError(b, err, "failed to build server TChannel") serverCfg := yarpc.Config{ Name: "server", Inbounds: []transport.Inbound{ytchannel.NewInbound(serverCh)}, } clientCh, err := tchannel.NewChannel("client", nil) require.NoError(b, err, "failed to build client TChannel") // no defer close on channels because YARPC will take care of that withDispatcher(b, serverCfg, func(server yarpc.Dispatcher) { server.Register(raw.Procedure("echo", yarpcEcho)) // Need server already started to build client config clientCfg := yarpc.Config{ Name: "client", Outbounds: yarpc.Outbounds{ "server": { Unary: ytchannel.NewOutbound(clientCh, ytchannel.HostPort(serverCh.PeerInfo().HostPort)), }, }, } withDispatcher(b, clientCfg, func(client yarpc.Dispatcher) { b.ResetTimer() runYARPCClient(b, raw.New(client.Channel("server"))) }) }) }
func Benchmark_TChannel_YARPCToTChannel(b *testing.B) { serverCh, err := tchannel.NewChannel("server", nil) require.NoError(b, err, "failed to build server TChannel") defer serverCh.Close() serverCh.Register(traw.Wrap(tchannelEcho{t: b}), "echo") require.NoError(b, serverCh.ListenAndServe(":0"), "failed to start up TChannel") clientCh, err := tchannel.NewChannel("client", nil) require.NoError(b, err, "failed to build client TChannel") clientCfg := yarpc.Config{ Name: "client", Outbounds: yarpc.Outbounds{ "server": { Unary: ytchannel.NewOutbound(clientCh, ytchannel.HostPort(serverCh.PeerInfo().HostPort)), }, }, } withDispatcher(b, clientCfg, func(client yarpc.Dispatcher) { b.ResetTimer() runYARPCClient(b, raw.New(client.Channel("server"))) }) }
func withConnectedClient(t *testing.T, recorder *Recorder, f func(raw.Client)) { serverHTTP := http.NewInbound(":0") serverDisp := yarpc.NewDispatcher(yarpc.Config{ Name: "server", Inbounds: []transport.Inbound{serverHTTP}, }) serverDisp.Register(raw.Procedure("hello", func(ctx context.Context, reqMeta yarpc.ReqMeta, body []byte) ([]byte, yarpc.ResMeta, error) { return append(body, []byte(", World")...), nil, nil })) require.NoError(t, serverDisp.Start()) defer serverDisp.Stop() clientDisp := yarpc.NewDispatcher(yarpc.Config{ Name: "client", Outbounds: yarpc.Outbounds{ "server": { Unary: http.NewOutbound(fmt.Sprintf("http://%s", serverHTTP.Addr().String())), }, }, Filter: recorder, }) require.NoError(t, clientDisp.Start()) defer clientDisp.Stop() client := raw.New(clientDisp.Channel("server")) f(client) }
func rawCall(dispatcher yarpc.Dispatcher, headers yarpc.Headers, procedure string, token []byte) ([]byte, yarpc.CallResMeta, error) { client := raw.New(dispatcher.Channel(serverName)) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() reqMeta := yarpc.NewReqMeta().Procedure(procedure).Headers(headers) resBody, resMeta, err := client.Call(ctx, reqMeta, token) return resBody, resMeta, err }
func withDisconnectedClient(t *testing.T, recorder *Recorder, f func(raw.Client)) { clientDisp := yarpc.NewDispatcher(yarpc.Config{ Name: "client", Outbounds: yarpc.Outbounds{ "server": { Unary: http.NewOutbound("http://localhost:65535"), }, }, Filter: recorder, }) require.NoError(t, clientDisp.Start()) defer clientDisp.Stop() client := raw.New(clientDisp.Channel("server")) f(client) }
func Benchmark_HTTP_YARPCToNetHTTP(b *testing.B) { clientCfg := yarpc.Config{ Name: "client", Outbounds: yarpc.Outbounds{ "server": { Unary: yhttp.NewOutbound("http://localhost:8998"), }, }, } withHTTPServer(b, ":8998", httpEcho(b), func() { withDispatcher(b, clientCfg, func(client yarpc.Dispatcher) { b.ResetTimer() runYARPCClient(b, raw.New(client.Channel("server"))) }) }) }
// Raw implements the 'raw' behavior. func Raw(t crossdock.T) { t = createEchoT("raw", t) fatals := crossdock.Fatals(t) dispatcher := disp.Create(t) fatals.NoError(dispatcher.Start(), "could not start Dispatcher") defer dispatcher.Stop() client := raw.New(dispatcher.Channel("yarpc-test")) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() token := random.Bytes(5) resBody, _, err := client.Call(ctx, yarpc.NewReqMeta().Procedure("echo/raw"), token) crossdock.Fatals(t).NoError(err, "call to echo/raw failed: %v", err) crossdock.Assert(t).True(bytes.Equal(token, resBody), "server said: %v", resBody) }
// runRaw tests if a yarpc client returns a remote timeout error behind the // TimeoutError interface when a remote http handler returns a handler timeout. func runRaw(t crossdock.T, disp yarpc.Dispatcher) { assert := crossdock.Assert(t) fatals := crossdock.Fatals(t) ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() ch := raw.New(disp.Channel("yarpc-test")) _, _, err := ch.Call(ctx, yarpc.NewReqMeta().Procedure("handlertimeout/raw"), nil) fatals.Error(err, "expected an error") if transport.IsBadRequestError(err) { t.Skipf("handlertimeout/raw method not implemented: %v", err) return } assert.True(transport.IsTimeoutError(err), "returns a TimeoutError: %T", err) form := strings.HasPrefix(err.Error(), `Timeout: call to procedure "handlertimeout/raw" of service "service" from caller "caller" timed out after`) assert.True(form, "must be a remote handler timeout: %q", err.Error()) }
// Raw starts an http run using raw encoding func Raw(t crossdock.T, dispatcher yarpc.Dispatcher) { fatals := crossdock.Fatals(t) client := raw.New(dispatcher.Channel("oneway-test")) ctx := context.Background() token := []byte(getRandomID()) ack, err := client.CallOneway(ctx, yarpc.NewReqMeta().Procedure("echo/raw"), token) // ensure channel hasn't been filled yet select { case <-serverCalledBack: fatals.FailNow("oneway raw test failed", "client waited for server to fill channel") default: } fatals.NoError(err, "call to oneway/raw failed: %v", err) fatals.NotNil(ack, "ack is nil") serverToken := <-serverCalledBack fatals.Equal(token, serverToken, "Client/Server token mismatch.") }
func Benchmark_HTTP_YARPCToYARPC(b *testing.B) { serverCfg := yarpc.Config{ Name: "server", Inbounds: []transport.Inbound{yhttp.NewInbound(":8999")}, } clientCfg := yarpc.Config{ Name: "client", Outbounds: yarpc.Outbounds{ "server": { Unary: yhttp.NewOutbound("http://localhost:8999"), }, }, } withDispatcher(b, serverCfg, func(server yarpc.Dispatcher) { server.Register(raw.Procedure("echo", yarpcEcho)) withDispatcher(b, clientCfg, func(client yarpc.Dispatcher) { b.ResetTimer() runYARPCClient(b, raw.New(client.Channel("server"))) }) }) }
// Run tests if a yarpc client returns correctly a client timeout error behind // the TimeoutError interface when the context deadline is reached while the // server is taking too long to respond. func Run(t crossdock.T) { assert := crossdock.Assert(t) fatals := crossdock.Fatals(t) ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() dispatcher := disp.Create(t) fatals.NoError(dispatcher.Start(), "could not start Dispatcher") defer dispatcher.Stop() ch := raw.New(dispatcher.Channel("yarpc-test")) _, _, err := ch.Call(ctx, yarpc.NewReqMeta().Procedure("sleep/raw"), nil) fatals.Error(err, "expected a failure for timeout") if transport.IsBadRequestError(err) { t.Skipf("sleep/raw method not implemented: %v", err) return } assert.True(transport.IsTimeoutError(err), "returns a TimeoutError: %T", err) trans := t.Param(params.Transport) switch trans { case "http": form := strings.HasPrefix(err.Error(), `client timeout for procedure "sleep/raw" of service "yarpc-test" after`) assert.True(form, "should be a client timeout: %q", err.Error()) case "tchannel": form := strings.HasPrefix(err.Error(), `timeout`) assert.True(form, "should be a remote timeout (we cant represent client timeout with tchannel): %q", err.Error()) default: fatals.Fail("", "unknown transport %q", trans) } }
func TestHandlerPanic(t *testing.T) { inbound := NewInbound("localhost:0") serverDispatcher := yarpc.NewDispatcher(yarpc.Config{ Name: "yarpc-test", Inbounds: []transport.Inbound{inbound}, }) serverDispatcher.Register([]transport.Registrant{ { Procedure: "panic", HandlerSpec: transport.NewUnaryHandlerSpec(panickedHandler{}), }, }) require.NoError(t, serverDispatcher.Start()) defer serverDispatcher.Stop() clientDispatcher := yarpc.NewDispatcher(yarpc.Config{ Name: "yarpc-test-client", Outbounds: yarpc.Outbounds{ "yarpc-test": { Unary: NewOutbound(fmt.Sprintf("http://%s", inbound.Addr().String())), }, }, }) require.NoError(t, clientDispatcher.Start()) defer clientDispatcher.Stop() client := raw.New(clientDispatcher.Channel("yarpc-test")) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() _, _, err := client.Call(ctx, yarpc.NewReqMeta().Procedure("panic"), []byte{}) assert.True(t, transport.IsUnexpectedError(err), "Must be an UnexpectedError") assert.Equal(t, `UnexpectedError: error for procedure "panic" of service "yarpc-test": panic: oops I panicked!`, err.Error()) }
// Run runs the headers behavior func Run(t crossdock.T) { t = createHeadersT(t) fatals := crossdock.Fatals(t) assert := crossdock.Assert(t) checks := crossdock.Checks(t) dispatcher := disp.Create(t) fatals.NoError(dispatcher.Start(), "could not start Dispatcher") defer dispatcher.Stop() var caller headerCaller encoding := t.Param(params.Encoding) switch encoding { case "raw": caller = rawCaller{raw.New(dispatcher.Channel("yarpc-test"))} case "json": caller = jsonCaller{json.New(dispatcher.Channel("yarpc-test"))} case "thrift": caller = thriftCaller{echoclient.New(dispatcher.Channel("yarpc-test"))} default: fatals.Fail("", "unknown encoding %q", encoding) } token1 := random.String(10) token2 := random.String(10) tests := []struct { desc string give yarpc.Headers want yarpc.Headers }{ { "valid headers", yarpc.NewHeaders().With("token1", token1).With("token2", token2), yarpc.NewHeaders().With("token1", token1).With("token2", token2), }, { "non-string values", yarpc.NewHeaders().With("token", "42"), yarpc.NewHeaders().With("token", "42"), }, { "empty strings", yarpc.NewHeaders().With("token", ""), yarpc.NewHeaders().With("token", ""), }, { "no headers", yarpc.Headers{}, yarpc.NewHeaders(), }, { "empty map", yarpc.NewHeaders(), yarpc.NewHeaders(), }, { "varying casing", yarpc.NewHeaders().With("ToKeN1", token1).With("tOkEn2", token2), yarpc.NewHeaders().With("token1", token1).With("token2", token2), }, { "http header conflict", yarpc.NewHeaders().With("Rpc-Procedure", "does not exist"), yarpc.NewHeaders().With("rpc-procedure", "does not exist"), }, { "mixed case value", yarpc.NewHeaders().With("token", "MIXED case Value"), yarpc.NewHeaders().With("token", "MIXED case Value"), }, } for _, tt := range tests { got, err := caller.Call(tt.give) if checks.NoError(err, "%v: call failed", tt.desc) { gotHeaders := internal.RemoveVariableHeaderKeys(got) assert.Equal(tt.want, gotHeaders, "%v: returns valid headers", tt.desc) } } }