| // 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" |
| "strings" |
| "testing" |
| "time" |
| |
| "v.io/v23" |
| "v.io/v23/context" |
| "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/lib/xrpc" |
| "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 |
| |
| type mockServer struct{} |
| |
| func (s mockServer) BasicCall(_ *context.T, _ rpc.StreamServerCall, txt string) (string, error) { |
| return "[" + txt + "]", nil |
| } |
| |
| func parseBrowsperResponse(data string, t *testing.T) (uint64, uint64, []byte) { |
| receivedBytes, err := hex.DecodeString(data) |
| if err != nil { |
| t.Fatalf("Failed to hex decode outgoing message: %v", err) |
| } |
| |
| id, bytesRead, err := lib.BinaryDecodeUint(receivedBytes) |
| if err != nil { |
| t.Fatalf("Failed to read mesage id: %v", err) |
| } |
| |
| receivedBytes = receivedBytes[bytesRead:] |
| messageType, bytesRead, err := lib.BinaryDecodeUint(receivedBytes) |
| if err != nil { |
| t.Fatalf("Failed to read message type: %v", err) |
| } |
| receivedBytes = receivedBytes[bytesRead:] |
| return id, messageType, receivedBytes |
| } |
| |
| func TestBrowspr(t *testing.T) { |
| ctx, shutdown := test.V23Init() |
| defer shutdown() |
| |
| proxySpec := rpc.ListenSpec{ |
| Addrs: rpc.ListenAddrs{ |
| // This '0' label is required by go vet. |
| // TODO(suharshs): Remove the '0' label once []ListenAddr is used |
| // instead of ListenAdders. |
| 0: { |
| Protocol: "tcp", |
| Address: "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() |
| |
| mt, err := mounttablelib.NewMountTableDispatcher("", "", "mounttable") |
| if err != nil { |
| t.Fatalf("Failed to create mounttable: %v", err) |
| } |
| s, err := xrpc.NewDispatchingServer(ctx, "", mt, options.ServesMountTable(true)) |
| if err != nil { |
| t.Fatalf("Failed to start mounttable server: %v", err) |
| } |
| mtEndpoint := s.Status().Endpoints[0] |
| 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, err := xrpc.NewServer(ctx, mockServerName, mockServer{}, nil) |
| if err != nil { |
| t.Fatalf("Failed to start mock server: %v", err) |
| } |
| mockServerEndpoint := mockServer.Status().Endpoints[0] |
| |
| 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 messageId uint64 |
| var messageType uint64 |
| var receivedBytes []byte |
| typeReader := lib.NewTypeReader() |
| |
| var postMessageHandler = func(instanceId int32, ty, msg string) { |
| id, mType, bin := parseBrowsperResponse(msg, t) |
| if mType == lib.ResponseTypeMessage { |
| var decodedBytes []byte |
| if err := vom.Decode(bin, &decodedBytes); err != nil { |
| t.Fatalf("Failed to decode type bytes: %v", err) |
| } |
| typeReader.Add(hex.EncodeToString(decodedBytes)) |
| return |
| } |
| receivedInstanceId = instanceId |
| receivedType = ty |
| messageType = mType |
| messageId = id |
| receivedBytes = bin |
| 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 typeBuf bytes.Buffer |
| typeEncoder := vom.NewTypeEncoder(&typeBuf) |
| var buf bytes.Buffer |
| encoder := vom.NewEncoderWithTypeEncoder(&buf, typeEncoder) |
| 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 := app.Message{ |
| Id: 1, |
| Data: vomRPC, |
| Type: app.VeyronRequestMessage, |
| } |
| |
| typeMessage := app.Message{ |
| Id: 0, |
| Data: hex.EncodeToString(typeBuf.Bytes()), |
| Type: app.TypeMessage, |
| } |
| createInstanceMessage := CreateInstanceMessage{ |
| InstanceId: msgInstanceId, |
| Origin: msgOrigin, |
| NamespaceRoots: nil, |
| Proxy: "", |
| } |
| _, err = browspr.HandleCreateInstanceRpc(vdl.ValueOf(createInstanceMessage)) |
| |
| err = browspr.HandleMessage(msgInstanceId, msgOrigin, typeMessage) |
| if err != nil { |
| t.Fatalf("Error while handling type message: %v", err) |
| } |
| |
| err = browspr.HandleMessage(msgInstanceId, msgOrigin, 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) |
| } |
| |
| if messageId != 1 { |
| t.Errorf("Id was %v, expected %v", messageId, int32(1)) |
| } |
| |
| if lib.ResponseType(messageType) != lib.ResponseFinal { |
| t.Errorf("Message type was %v, expected %v", messageType, lib.ResponseFinal) |
| } |
| |
| var data string |
| if err := vom.NewDecoder(bytes.NewBuffer(receivedBytes)).Decode(&data); err != nil { |
| t.Fatalf("Failed to unmarshall outgoing response: %v", err) |
| } |
| |
| var result app.RpcResponse |
| dataBytes, err := hex.DecodeString(data) |
| if err != nil { |
| t.Errorf("Failed to hex decode from %v: %v", data, err) |
| } |
| |
| decoder := vom.NewDecoderWithTypeDecoder(bytes.NewBuffer(dataBytes), vom.NewTypeDecoder(typeReader)) |
| if err := decoder.Decode(&result); err != nil { |
| t.Errorf("Failed to vom decode args from %v: %v", data, err) |
| } |
| if got, want := result.OutArgs[0], vdl.StringValue("[InputValue]"); !vdl.EqualValue(got, want) { |
| t.Errorf("Result got %v, want %v", got, want) |
| } |
| } |