| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package browspr |
| |
| import ( |
| "bytes" |
| "encoding/hex" |
| "encoding/json" |
| "strings" |
| "testing" |
| "time" |
| |
| "v.io/v23" |
| "v.io/v23/context" |
| "v.io/v23/naming" |
| "v.io/v23/options" |
| "v.io/v23/rpc" |
| "v.io/v23/security" |
| "v.io/v23/vdl" |
| vdltime "v.io/v23/vdlroot/time" |
| "v.io/v23/vom" |
| |
| "v.io/x/ref/runtime/factories/generic" |
| "v.io/x/ref/services/mounttable/mounttablelib" |
| "v.io/x/ref/services/wspr/internal/app" |
| "v.io/x/ref/services/wspr/internal/lib" |
| "v.io/x/ref/test" |
| ) |
| |
| //go:generate v23 test generate |
| |
| func startMounttable(ctx *context.T) (rpc.Server, naming.Endpoint, error) { |
| mt, err := mounttablelib.NewMountTableDispatcher("", "", "mounttable") |
| if err != nil { |
| return nil, nil, err |
| } |
| |
| s, err := v23.NewServer(ctx, options.ServesMountTable(true)) |
| if err != nil { |
| return nil, nil, err |
| } |
| |
| endpoints, err := s.Listen(v23.GetListenSpec(ctx)) |
| if err != nil { |
| return nil, nil, err |
| } |
| |
| if err := s.ServeDispatcher("", mt); err != nil { |
| return nil, nil, err |
| } |
| |
| return s, endpoints[0], nil |
| } |
| |
| type mockServer struct{} |
| |
| func (s mockServer) BasicCall(_ *context.T, _ rpc.StreamServerCall, txt string) (string, error) { |
| return "[" + txt + "]", nil |
| } |
| |
| func startMockServer(ctx *context.T, desiredName string) (rpc.Server, naming.Endpoint, error) { |
| // Create a new server instance. |
| s, err := v23.NewServer(ctx) |
| if err != nil { |
| return nil, nil, err |
| } |
| |
| endpoints, err := s.Listen(v23.GetListenSpec(ctx)) |
| if err != nil { |
| return nil, nil, err |
| } |
| |
| if err := s.Serve(desiredName, mockServer{}, nil); err != nil { |
| return nil, nil, err |
| } |
| |
| return s, endpoints[0], nil |
| } |
| |
| func TestBrowspr(t *testing.T) { |
| ctx, shutdown := test.InitForTest() |
| defer shutdown() |
| |
| proxySpec := rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}}} |
| proxyShutdown, proxyEndpoint, err := generic.NewProxy(ctx, proxySpec, security.AllowEveryone()) |
| if err != nil { |
| t.Fatalf("Failed to start proxy: %v", err) |
| } |
| defer proxyShutdown() |
| |
| mtServer, mtEndpoint, err := startMounttable(ctx) |
| if err != nil { |
| t.Fatalf("Failed to start mounttable server: %v", err) |
| } |
| defer mtServer.Stop() |
| root := mtEndpoint.Name() |
| if err := v23.GetNamespace(ctx).SetRoots(root); err != nil { |
| t.Fatalf("Failed to set namespace roots: %v", err) |
| } |
| |
| mockServerName := "mock/server" |
| mockServer, mockServerEndpoint, err := startMockServer(ctx, mockServerName) |
| if err != nil { |
| t.Fatalf("Failed to start mock server: %v", err) |
| } |
| defer mockServer.Stop() |
| |
| then := time.Now() |
| found: |
| for { |
| status := mockServer.Status() |
| for _, v := range status.Mounts { |
| if v.Name == mockServerName && v.Server == mockServerEndpoint.String() && !v.LastMount.IsZero() { |
| if v.LastMountErr != nil { |
| t.Fatalf("Failed to mount %s: %v", v.Name, v.LastMountErr) |
| } |
| break found |
| } |
| } |
| if time.Now().Sub(then) > time.Minute { |
| t.Fatalf("Failed to find mounted server and endpoint: %v: %v", mockServerName, mtEndpoint) |
| } |
| time.Sleep(100 * time.Millisecond) |
| } |
| mountEntry, err := v23.GetNamespace(ctx).Resolve(ctx, mockServerName) |
| if err != nil { |
| t.Fatalf("Error fetching published names from mounttable: %v", err) |
| } |
| |
| servers := []string{} |
| for _, s := range mountEntry.Servers { |
| if strings.Index(s.Server, "@tcp") != -1 { |
| servers = append(servers, s.Server) |
| } |
| } |
| if len(servers) != 1 || servers[0] != mockServerEndpoint.String() { |
| t.Fatalf("Incorrect names retrieved from mounttable: %v", mountEntry) |
| } |
| |
| spec := v23.GetListenSpec(ctx) |
| spec.Proxy = proxyEndpoint.String() |
| |
| receivedResponse := make(chan bool, 1) |
| var receivedInstanceId int32 |
| var receivedType string |
| var receivedMsg string |
| |
| var postMessageHandler = func(instanceId int32, ty, msg string) { |
| receivedInstanceId = instanceId |
| receivedType = ty |
| receivedMsg = msg |
| receivedResponse <- true |
| } |
| |
| v23.GetNamespace(ctx).SetRoots(root) |
| browspr := NewBrowspr(ctx, postMessageHandler, &spec, "/mock:1234/identd", []string{root}) |
| |
| // browspr sets its namespace root to use the "ws" protocol, but we want to force "tcp" here. |
| browspr.namespaceRoots = []string{root} |
| |
| browspr.accountManager.SetMockBlesser(newMockBlesserService(v23.GetPrincipal(ctx))) |
| |
| msgInstanceId := int32(11) |
| msgOrigin := "http://test-origin.com" |
| |
| // Associate the origin with the root accounts' blessings, otherwise a |
| // dummy account will be used and will be rejected by the authorizer. |
| accountName := "test-account" |
| bp := v23.GetPrincipal(browspr.ctx) |
| if err := browspr.principalManager.AddAccount(accountName, bp.BlessingStore().Default()); err != nil { |
| t.Fatalf("Failed to add account: %v", err) |
| } |
| if err := browspr.accountManager.AssociateAccount(msgOrigin, accountName, nil); err != nil { |
| t.Fatalf("Failed to associate account: %v", err) |
| } |
| |
| rpc := app.RpcRequest{ |
| Name: mockServerName, |
| Method: "BasicCall", |
| NumInArgs: 1, |
| NumOutArgs: 1, |
| IsStreaming: false, |
| Deadline: vdltime.Deadline{}, |
| } |
| |
| var buf bytes.Buffer |
| encoder := vom.NewEncoder(&buf) |
| if err := encoder.Encode(rpc); err != nil { |
| t.Fatalf("Failed to vom encode rpc message: %v", err) |
| } |
| if err := encoder.Encode("InputValue"); err != nil { |
| t.Fatalf("Failed to vom encode rpc message: %v", err) |
| } |
| vomRPC := hex.EncodeToString(buf.Bytes()) |
| |
| msg, err := json.Marshal(app.Message{ |
| Id: 1, |
| Data: vomRPC, |
| Type: app.VeyronRequestMessage, |
| }) |
| if err != nil { |
| t.Fatalf("Failed to marshall app message to json: %v", err) |
| } |
| |
| createInstanceMessage := CreateInstanceMessage{ |
| InstanceId: msgInstanceId, |
| Origin: msgOrigin, |
| NamespaceRoots: nil, |
| Proxy: "", |
| } |
| _, err = browspr.HandleCreateInstanceRpc(vdl.ValueOf(createInstanceMessage)) |
| |
| err = browspr.HandleMessage(msgInstanceId, msgOrigin, string(msg)) |
| if err != nil { |
| t.Fatalf("Error while handling message: %v", err) |
| } |
| |
| <-receivedResponse |
| |
| if receivedInstanceId != msgInstanceId { |
| t.Errorf("Received unexpected instance id: %d. Expected: %d", receivedInstanceId, msgInstanceId) |
| } |
| if receivedType != "browsprMsg" { |
| t.Errorf("Received unexpected response type. Expected: %q, but got %q", "browsprMsg", receivedType) |
| } |
| |
| var outMsg app.Message |
| if err := lib.VomDecode(receivedMsg, &outMsg); err != nil { |
| t.Fatalf("Failed to unmarshall outgoing message: %v", err) |
| } |
| if outMsg.Id != int32(1) { |
| t.Errorf("Id was %v, expected %v", outMsg.Id, int32(1)) |
| } |
| if outMsg.Type != app.VeyronRequestMessage { |
| t.Errorf("Message type was %v, expected %v", outMsg.Type, app.MessageType(0)) |
| } |
| |
| var responseMsg lib.Response |
| if err := lib.VomDecode(outMsg.Data, &responseMsg); err != nil { |
| t.Fatalf("Failed to unmarshall outgoing response: %v", err) |
| } |
| if responseMsg.Type != lib.ResponseFinal { |
| t.Errorf("Data was %q, expected %q", outMsg.Data, `["[InputValue]"]`) |
| } |
| var outArg string |
| var ok bool |
| if outArg, ok = responseMsg.Message.(string); !ok { |
| t.Errorf("Got unexpected response message body of type %T, expected type string", responseMsg.Message) |
| } |
| var result app.RpcResponse |
| if err := lib.VomDecode(outArg, &result); err != nil { |
| t.Errorf("Failed to vom decode args from %v: %v", outArg, err) |
| } |
| if got, want := result.OutArgs[0], vdl.StringValue("[InputValue]"); !vdl.EqualValue(got, want) { |
| t.Errorf("Result got %v, want %v", got, want) |
| } |
| } |