go.uber.org/yarpc@v1.72.1/transport/tchannel/channel_inbound_test.go (about)

     1  // Copyright (c) 2022 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package tchannel
    22  
    23  import (
    24  	"bytes"
    25  	"context"
    26  	"fmt"
    27  	"io/ioutil"
    28  	"testing"
    29  
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  	"github.com/uber/tchannel-go"
    33  	tjson "github.com/uber/tchannel-go/json"
    34  	"github.com/uber/tchannel-go/testutils"
    35  	"go.uber.org/yarpc"
    36  	"go.uber.org/yarpc/api/transport"
    37  	"go.uber.org/yarpc/encoding/json"
    38  	"go.uber.org/yarpc/encoding/raw"
    39  	"go.uber.org/yarpc/internal/testtime"
    40  )
    41  
    42  func TestChannelInboundStartNew(t *testing.T) {
    43  	ch, err := tchannel.NewChannel("foo", nil)
    44  	require.NoError(t, err)
    45  
    46  	x, err := NewChannelTransport(WithChannel(ch))
    47  	require.NoError(t, err)
    48  
    49  	i := x.NewInbound()
    50  	i.SetRouter(yarpc.NewMapRouter("foo"))
    51  	// Can't do Equal because we want to match the pointer, not a
    52  	// DeepEqual.
    53  	assert.True(t, ch == i.Channel(), "channel does not match")
    54  	require.NoError(t, i.Start())
    55  	require.NoError(t, x.Start())
    56  
    57  	assert.Equal(t, tchannel.ChannelListening, ch.State())
    58  	assert.NoError(t, i.Stop())
    59  	assert.NoError(t, x.Stop())
    60  	assert.Equal(t, tchannel.ChannelClosed, ch.State())
    61  }
    62  
    63  func TestChannelInboundStartAlreadyListening(t *testing.T) {
    64  	ch, err := tchannel.NewChannel("foo", nil)
    65  	require.NoError(t, err)
    66  
    67  	require.NoError(t, ch.ListenAndServe("127.0.0.1:0"))
    68  	assert.Equal(t, tchannel.ChannelListening, ch.State())
    69  
    70  	x, err := NewChannelTransport(WithChannel(ch))
    71  	require.NoError(t, err)
    72  
    73  	i := x.NewInbound()
    74  
    75  	i.SetRouter(yarpc.NewMapRouter("foo"))
    76  	require.NoError(t, i.Start())
    77  	require.NoError(t, x.Start())
    78  	assert.Equal(t, tchannel.ChannelListening, ch.State())
    79  
    80  	assert.NoError(t, i.Stop())
    81  	assert.NoError(t, x.Stop())
    82  	assert.Equal(t, tchannel.ChannelClosed, ch.State())
    83  }
    84  
    85  func TestChannelInboundStopWithoutStarting(t *testing.T) {
    86  	ch, err := tchannel.NewChannel("foo", nil)
    87  	require.NoError(t, err)
    88  
    89  	x, err := NewChannelTransport(WithChannel(ch))
    90  	require.NoError(t, err)
    91  
    92  	i := x.NewInbound()
    93  	assert.NoError(t, i.Stop())
    94  }
    95  
    96  func TestChannelInboundInvalidAddress(t *testing.T) {
    97  	x, err := NewChannelTransport(ServiceName("foo"), ListenAddr("not valid"))
    98  	require.NoError(t, err)
    99  
   100  	i := x.NewInbound()
   101  	i.SetRouter(yarpc.NewMapRouter("foo"))
   102  	assert.Nil(t, i.Start())
   103  	defer i.Stop()
   104  	assert.Error(t, x.Start())
   105  	defer x.Stop()
   106  }
   107  
   108  func TestChannelInboundExistingMethods(t *testing.T) {
   109  	// Create a channel with an existing "echo" method.
   110  	ch, err := tchannel.NewChannel("foo", nil)
   111  	require.NoError(t, err)
   112  	tjson.Register(ch, tjson.Handlers{
   113  		"echo": func(ctx tjson.Context, req map[string]string) (map[string]string, error) {
   114  			return req, nil
   115  		},
   116  	}, nil)
   117  
   118  	x, err := NewChannelTransport(WithChannel(ch))
   119  	require.NoError(t, err)
   120  
   121  	i := x.NewInbound()
   122  	i.SetRouter(yarpc.NewMapRouter("foo"))
   123  	require.NoError(t, i.Start())
   124  	defer i.Stop()
   125  	require.NoError(t, x.Start())
   126  	defer x.Stop()
   127  
   128  	// Make a call to the "echo" method which should call our pre-registered method.
   129  	ctx, cancel := tjson.NewContext(testtime.Second)
   130  	defer cancel()
   131  
   132  	var resp map[string]string
   133  	arg := map[string]string{"k": "v"}
   134  
   135  	svc := ch.ServiceName()
   136  	peer := ch.Peers().GetOrAdd(ch.PeerInfo().HostPort)
   137  	err = tjson.CallPeer(ctx, peer, svc, "echo", arg, &resp)
   138  	require.NoError(t, err, "Call failed")
   139  	assert.Equal(t, arg, resp, "Response mismatch")
   140  }
   141  
   142  func TestChannelInboundMaskedMethods(t *testing.T) {
   143  	// Create a channel with an existing "echo" method.
   144  	ch, err := tchannel.NewChannel("foo", nil)
   145  	require.NoError(t, err)
   146  	tjson.Register(ch, tjson.Handlers{
   147  		"echo": func(ctx tjson.Context, req map[string]string) (map[string]string, error) {
   148  			// i should not be
   149  			return nil, fmt.Errorf("SVM NON DEBEAM")
   150  		},
   151  	}, nil)
   152  
   153  	x, err := NewChannelTransport(WithChannel(ch))
   154  	require.NoError(t, err)
   155  
   156  	// Override TChannel version of echo
   157  	i := x.NewInbound()
   158  	r := yarpc.NewMapRouter("foo")
   159  	echo := func(ctx context.Context, req map[string]string) (map[string]string, error) {
   160  		return req, nil
   161  	}
   162  	r.Register(json.Procedure("echo", echo))
   163  	i.SetRouter(r)
   164  	require.NoError(t, i.Start())
   165  	defer i.Stop()
   166  	require.NoError(t, x.Start())
   167  	defer x.Stop()
   168  
   169  	// Make a call to the "echo" method which should call our pre-registered method.
   170  	ctx, cancel := tjson.NewContext(testtime.Second)
   171  	defer cancel()
   172  
   173  	var resp map[string]string
   174  	arg := map[string]string{"k": "v"}
   175  
   176  	svc := ch.ServiceName()
   177  	peer := ch.Peers().GetOrAdd(ch.PeerInfo().HostPort)
   178  	err = tjson.CallPeer(ctx, peer, svc, "echo", arg, &resp)
   179  	require.NoError(t, err, "Call failed")
   180  	assert.Equal(t, arg, resp, "Response mismatch")
   181  }
   182  
   183  func TestChannelInboundSubServices(t *testing.T) {
   184  	chserv := testutils.NewServer(t, nil)
   185  	defer chserv.Close()
   186  	chservEndpoint := chserv.PeerInfo().HostPort
   187  
   188  	itransport, err := NewChannelTransport(ServiceName("myservice"), WithChannel(chserv))
   189  	require.NoError(t, err)
   190  
   191  	router := yarpc.NewMapRouter("myservice")
   192  
   193  	i := itransport.NewInbound()
   194  	i.SetRouter(router)
   195  
   196  	nophandlerspec := transport.NewUnaryHandlerSpec(nophandler{})
   197  
   198  	router.Register([]transport.Procedure{
   199  		{Name: "hello", HandlerSpec: nophandlerspec},
   200  		{Service: "subservice", Name: "hello", HandlerSpec: nophandlerspec},
   201  		{Service: "subservice", Name: "world", HandlerSpec: nophandlerspec},
   202  		{Service: "subservice2", Name: "hello", HandlerSpec: nophandlerspec},
   203  		{Service: "subservice2", Name: "monde", HandlerSpec: nophandlerspec},
   204  	})
   205  
   206  	require.NoError(t, i.Start())
   207  	require.NoError(t, itransport.Start())
   208  
   209  	otransport, err := NewChannelTransport(ServiceName("caller"))
   210  	require.NoError(t, err)
   211  	o := otransport.NewSingleOutbound(chservEndpoint)
   212  
   213  	require.NoError(t, o.Start())
   214  	defer o.Stop()
   215  
   216  	for _, tt := range []struct {
   217  		service   string
   218  		procedure string
   219  	}{
   220  		{"myservice", "hello"},
   221  		{"subservice", "hello"},
   222  		{"subservice", "world"},
   223  		{"subservice2", "hello"},
   224  		{"subservice2", "monde"},
   225  	} {
   226  		ctx, cancel := context.WithTimeout(context.Background(), 200*testtime.Millisecond)
   227  		defer cancel()
   228  		res, err := o.Call(
   229  			ctx,
   230  			&transport.Request{
   231  				Caller:    "caller",
   232  				Service:   tt.service,
   233  				Procedure: tt.procedure,
   234  				Encoding:  raw.Encoding,
   235  				Body:      bytes.NewReader([]byte{}),
   236  			},
   237  		)
   238  		if !assert.NoError(t, err, "failed to make call") {
   239  			continue
   240  		}
   241  		if !assert.Equal(t, false, res.ApplicationError, "not application error") {
   242  			continue
   243  		}
   244  		body, err := ioutil.ReadAll(res.Body)
   245  		if !assert.NoError(t, err) {
   246  			continue
   247  		}
   248  		assert.Equal(t, string(body), tt.service)
   249  	}
   250  
   251  	require.NoError(t, i.Stop())
   252  	require.NoError(t, itransport.Stop())
   253  }