// sendOneInvite sends invitations to all the addresses in addrs and returns the one that accepted it. // All addrs are assumed to be equivalent and thus at most one Invite RPC will succeed. // // TODO: This is aiming to replicate what the RPC stack does for all the // addresses a single name resolved to. Should all these addresses discovered // somehow be encapsulated in a single object name? func sendOneInvite(ctx *context.T, addrs []string) string { // Give at most 1 second for these connections to be made, if they // can't be made then consider the peer bad and ignore it. // TODO: Should these RPCs also use the "connection timeout" that might be implemented // as per proposal: https://docs.google.com/a/google.com/document/d/1prtxGhSR5TaL0lc_iDRC0Q6H1Drbg2T0x7MWVb_ZCSM/edit?usp=sharing ctx, cancel := context.WithTimeout(ctx, maxInvitationWaitTime) defer cancel() ch := make(chan string) for _, addr := range addrs { go func(addr string) { err := spec.ScreenClient(addr).Invite(ctx, options.ServerAuthorizer{security.AllowEveryone()}) ctx.Infof("Invitation to %v sent, error: %v", addr, err) if err == nil { ch <- addr return } ch <- "" }(addr) } for i := range addrs { if ret := <-ch; len(ret) > 0 { // Drain the rest and return go func() { i++ for i < len(addrs) { <-ch } }() return ret } } return "" }
func (v23pd *dispatcher) Lookup(ctx *context.T, suffix string) (interface{}, security.Authorizer, error) { ctx.Infof("dispatcher.Lookup for suffix: %s", suffix) return fakeService{ appctx: v23pd.appctx, suffix: suffix, ids: bindings.NewCounter(), }, security.AllowEveryone(), nil }
func (nm *networkManager) Invite(ctx *context.T, call rpc.ServerCall) error { inviter := call.RemoteEndpoint().Name() response := make(chan error) nm.inviteRPCs <- Invitation{ Name: inviter, Color: selectColor(call.Security().RemoteBlessings().PublicKey()), Response: response, Withdrawn: ctx.Done(), } if err := <-response; err != nil { return err } blessings, rejected := security.RemoteBlessingNames(ctx, call.Security()) ctx.Infof("Accepted invitation from %v@%v (rejected blessings: %v)", blessings, inviter, rejected) return nil }
func channel2rpc(ctx *context.T, src <-chan *spec.Triangle, dst string, errch chan<- error, myScreen chan<- *spec.Triangle) { for t := range src { // This is an "interactive" game, if an RPC doesn't succeed in say ctxTimeout, cancel := context.WithTimeout(ctx, maxTriangleGiveTime) if err := spec.ScreenClient(dst).Give(ctxTimeout, *t, options.ServerAuthorizer{security.AllowEveryone()}); err != nil { cancel() returnTriangle(t, myScreen) ctx.Infof("%q.Give failed: %v, aborting connection with remote screen", dst, err) errch <- err break } cancel() } for t := range src { returnTriangle(t, myScreen) } ctx.VI(1).Infof("Exiting goroutine with connection to %q", dst) }
func (nm *networkManager) Give(ctx *context.T, call rpc.ServerCall, t spec.Triangle) error { if ctx.V(3) { blessings, rejected := security.RemoteBlessingNames(ctx, call.Security()) ctx.Infof("Took a triangle from %v@%v (rejected blessings: %v)", blessings, call.RemoteEndpoint().Name(), rejected) } // Transform from sender's coordinates to our coordinates. // The assumption is that if the triangle was to the left of the // sender's coordinate system, then it will appear on our right and // vice-versa. switch { case t.X < -1: t.X += 2 case t.X > 1: t.X -= 2 } nm.myScreen <- &t return nil }
// Invoke calls the mojom service based on the suffix and converts the mojom // results (a struct) to Vanadium results (a slice of *vom.RawBytes). // Note: The argptrs from Prepare are reused here. The vom bytes should have // been decoded into these argptrs, so there are actual values inside now. func (fs fakeService) Invoke(ctx *context.T, call rpc.StreamServerCall, method string, argptrs []interface{}) (results []interface{}, _ error) { // fs.suffix consists of the mojo url and the application/interface name. // The last part should be the name; everything else is the url. parts := strings.Split(fs.suffix, "/") mojourl := strings.Join(parts[:len(parts)-1], "/") // e.g., mojo:go_remote_echo_server. May be defined in a BUILD.gn file. mojoname := parts[len(parts)-1] // e.g., mojo::examples::RemoteEcho. Defined from the interface + module. // Create the generic message pipe. r is a bindings.InterfaceRequest, and // p is a bindings.InterfacePointer. r, p := bindings.CreateMessagePipeForMojoInterface() v := v23ServiceRequest{ request: r, name: mojoname, } // v is an application.ServiceRequest with mojoname // Connect to the mojourl. fs.appctx.ConnectToApplication(mojourl).ConnectToService(&v) // Then assign a new router the FakeService. // This will never conflict because each FakeService is only invoked once. fs.router = bindings.NewRouter(p.PassMessagePipe(), bindings.GetAsyncWaiter()) defer fs.Close_Proxy() ctx.Infof("Fake Service Invoke (Remote Signature: %q -- %q)", mojourl, mojoname) // Vanadium relies on type information, so we will retrieve that first. mojomInterface, desc, err := fs.callRemoteSignature(mojourl, mojoname) if err != nil { return nil, err } ctx.Infof("Fake Service Invoke Signature %v", mojomInterface) ctx.Infof("Fake Service Invoke (Remote Method: %v)", method) // With the type information, we can make the method call to the remote interface. methodResults, err := fs.callRemoteMethod(ctx, method, mojomInterface, desc, argptrs) if err != nil { ctx.Errorf("Method called failed: %v", err) return nil, err } ctx.Infof("Fake Service Invoke Results %v", methodResults) // Convert methodResult to results. results = make([]interface{}, len(methodResults)) for i := range methodResults { results[i] = &methodResults[i] } return results, nil }
// A helper function that sends a remote message that expects a response. func (fs fakeService) callRemoteWithResponse(ctx *context.T, message *bindings.Message) (outMessage *bindings.Message, err error) { ctx.Infof("callRemoteGeneric: Send message along the router") readResult := <-fs.router.AcceptWithResponse(message) if err = readResult.Error; err != nil { return } ctx.Infof("callRemoteGeneric: Audit response message header flag") // The message flag we receive back must be a bindings.MessageIsResponseFlag if readResult.Message.Header.Flags != bindings.MessageIsResponseFlag { err = &bindings.ValidationError{bindings.MessageHeaderInvalidFlags, fmt.Sprintf("invalid message header flag: %v", readResult.Message.Header.Flags), } return } ctx.Infof("callRemoteGeneric: Audit response message header type") // While the mojo service we called into will return a header whose // type must match our outgoing one. if got, want := readResult.Message.Header.Type, message.Header.Type; got != want { err = &bindings.ValidationError{bindings.MessageHeaderUnknownMethod, fmt.Sprintf("invalid method in response: expected %v, got %v", want, got), } return } return readResult.Message, nil }
func seekInvites(ctx *context.T, disc discovery.T, server rpc.Server, updates <-chan bool) { var ( ad = &discovery.Advertisement{ InterfaceName: interfaceName, Attributes: discovery.Attributes{ "OS": runtime.GOOS, }, } cancel func() chStopped <-chan struct{} start = func() { // Start the advertisement, update cancelCtx, cancel and chStopped var err error var advCtx *context.T advCtx, cancel = context.WithCancel(ctx) if chStopped, err = discutil.AdvertiseServer(advCtx, disc, server, "", ad, nil); err != nil { cancel() ctx.Infof("Failed to advertise %#v: %v", *ad, err) return } ctx.Infof("Started advertising: %#v", *ad) } stop = func() { if chStopped == nil { return } cancel() <-chStopped ctx.Infof("Stopped advertising: %#v", *ad) chStopped = nil } ) start() go func() { for shouldStart := range updates { if shouldStart { start() continue } stop() } }() }
func sendInvites(ctx *context.T, disc discovery.T, notify chan<- string) { ctx.Infof("Scanning for peers to invite") ctx, cancel := context.WithCancel(ctx) defer cancel() updates, err := disc.Scan(ctx, fmt.Sprintf("v.InterfaceName=%q", interfaceName)) if err != nil { ctx.Panic(err) } for u := range updates { if u.IsLost() { continue } ctx.Infof("Sending invitations to %+v", u.Addresses()) if addr := sendOneInvite(ctx, u.Addresses()); len(addr) > 0 { notify <- addr go func() { for range updates { } }() return } } ctx.Infof("Stopped scanning for peers to invite without finding one") }
func btAndDiscoveryFunc(ctx *context.T, w io.Writer) error { bothf := func(ctx *context.T, w io.Writer, format string, args ...interface{}) { fmt.Fprintf(w, format, args...) ctx.Infof(format, args...) } defer bothf(ctx, w, "finishing!") dis, err := v23.NewDiscovery(ctx) if err != nil { bothf(ctx, w, "Can't create discovery %v", err) return err } ctx = v23.WithListenSpec(ctx, rpc.ListenSpec{Addrs: rpc.ListenAddrs{{Protocol: "bt", Address: "/0"}}}) _, server, err := v23.WithNewServer(ctx, "", &echoServer{}, security.AllowEveryone()) if err != nil { bothf(ctx, w, "Can't create server %v", err) return err } ctx.Infof("Server listening on %v", server.Status().Endpoints) ctx.Infof("Server listen errors: %v", server.Status().ListenErrors) interfaces := []string{ "v.io/x/jni/impl/google/services/vango/Echo", "v.io/x/jni/impl/google/services/vango/Echo2", "v.io/x/jni/impl/google/services/vango/Echo3", "v.io/x/jni/impl/google/services/vango/Echo4", } type adstate struct { ad *discovery.Advertisement stop func() } ads := []adstate{} for _, name := range interfaces { ad := &discovery.Advertisement{ InterfaceName: name, Attributes: discovery.Attributes{ "one": "A value of some kind", "two": "Yet another value", "three": "More and more", "four": "This is insane", }, } nctx, ncancel := context.WithCancel(ctx) ch, err := libdiscovery.AdvertiseServer(nctx, dis, server, "", ad, nil) if err != nil { bothf(nctx, w, "Can't advertise server %v", err) return err } stop := func() { ncancel() <-ch } ads = append(ads, adstate{ad, stop}) } type updateState struct { ch <-chan discovery.Update stop func() } var updates []updateState for _, name := range interfaces { nctx, ncancel := context.WithCancel(ctx) u, err := dis.Scan(nctx, `v.InterfaceName="`+name+`"`) if err != nil { bothf(nctx, w, "Can't scan %v", err) return err } stop := func() { ncancel() } updates = append(updates, updateState{u, stop}) } for _, u := range updates[1:] { go func(up updateState) { for _ = range up.ch { } }(u) } makeopt := func(ad discovery.Advertisement) options.Preresolved { me := &naming.MountEntry{ IsLeaf: true, } for _, a := range ad.Addresses { addr, _ := naming.SplitAddressName(a) me.Servers = append(me.Servers, naming.MountedServer{ Server: addr, }) } return options.Preresolved{Resolution: me} } alive := map[discovery.AdId]options.Preresolved{} ticker := time.NewTicker(time.Second) for { select { case <-ticker.C: if len(alive) == 0 { bothf(ctx, w, "No live connections to dial.") } for _, opt := range alive { dialtime := options.ConnectionTimeout(5 * time.Second) channeltime := options.ChannelTimeout(2 * time.Second) data := make([]byte, 1024) summary, err := runTimedCall(ctx, "A timed call.", string(data), opt, dialtime, channeltime) if err != nil { bothf(ctx, w, "failed call %s, %v, %v", summary, err, opt.Resolution.Servers) } else { bothf(ctx, w, "succeeded call: %s, %v", summary, opt.Resolution.Servers) } } case u := <-updates[0].ch: if u.IsLost() { bothf(ctx, w, "lost %v", u.Addresses()) delete(alive, u.Id()) } else { bothf(ctx, w, "found %v", u.Addresses()) alive[u.Id()] = makeopt(u.Advertisement()) } } } }
// AllFunc runs a server, advertises it, scans for other servers and makes an // Echo RPC to every advertised remote server. func AllFunc(ctx *context.T, output io.Writer) error { ls := rpc.ListenSpec{Proxy: "proxy"} addRegisteredProto(&ls, "tcp", ":0") addRegisteredProto(&ls, "bt", "/0") fmt.Fprintf(output, "Listening on: %+v (and proxy)\n", ls.Addrs) ctx, server, err := v23.WithNewServer( v23.WithListenSpec(ctx, ls), mountName(ctx, "all"), &echoServer{}, security.AllowEveryone()) if err != nil { return err } ad := &discovery.Advertisement{ InterfaceName: interfaceName, Attributes: discovery.Attributes{ "Hello": "There", }, } d, err := v23.NewDiscovery(ctx) if err != nil { return err } stoppedAd, err := libdiscovery.AdvertiseServer(ctx, d, server, "", ad, nil) if err != nil { return err } updates, err := d.Scan(ctx, "v.InterfaceName=\""+interfaceName+"\"") if err != nil { return err } var ( status = server.Status() counter = 0 peerByAdId = make(map[discovery.AdId]*peer) lastCall = make(map[discovery.AdId]time.Time) callResults = make(chan string) activeCalls = 0 quit = false myaddrs = serverAddrs(status) ticker = time.NewTicker(time.Second) call = func(p *peer) { counter++ activeCalls++ lastCall[p.adId] = time.Now() go func(msg string) { summary, err := p.call(ctx, msg) if err != nil { ctx.Infof("Failed to call [%v]: %v", p.description, err) callResults <- "" return } callResults <- summary }(fmt.Sprintf("Hello #%d", counter)) } statRequest = make(chan chan<- string) ) defer ticker.Stop() stats.NewStringFunc(vangoStat, func() string { r := make(chan string) statRequest <- r return <-r }) defer stats.Delete(vangoStat) fmt.Fprintln(output, "My AdID:", ad.Id) fmt.Fprintln(output, "My addrs:", myaddrs) ctx.Infof("SERVER STATUS: %+v", status) for !quit { select { case <-ctx.Done(): quit = true case <-status.Dirty: status = server.Status() newaddrs := serverAddrs(status) changed := len(newaddrs) != len(myaddrs) if !changed { for i := range newaddrs { if newaddrs[i] != myaddrs[i] { changed = true break } } } if changed { myaddrs = newaddrs fmt.Fprintln(output, "My addrs:", myaddrs) } ctx.Infof("SERVER STATUS: %+v", status) case u, scanning := <-updates: if !scanning { fmt.Fprintln(output, "SCANNING STOPPED") quit = true break } if u.IsLost() { if p, ok := peerByAdId[u.Id()]; ok { fmt.Fprintln(output, "LOST:", p.description) } delete(peerByAdId, u.Id()) delete(lastCall, u.Id()) break } p, err := newPeer(ctx, u) if err != nil { ctx.Info(err) break } peerByAdId[p.adId] = p fmt.Fprintln(output, "FOUND:", p.description) call(p) case r := <-callResults: activeCalls-- if len(r) > 0 { fmt.Fprintln(output, r) } case <-stoppedAd: fmt.Fprintln(output, "STOPPED ADVERTISING") stoppedAd = nil case <-ticker.C: // Call all peers that haven't been called in a while now := time.Now() for id, t := range lastCall { if now.Sub(t) > rpcTimeout { call(peerByAdId[id]) } } case s := <-statRequest: idx := 1 ret := new(bytes.Buffer) fmt.Fprintln(ret, "ACTIVE CALLS:", activeCalls) fmt.Fprintln(ret, "PEERS") for id, p := range peerByAdId { fmt.Fprintf(ret, "%2d) %s -- %v\n", idx, p.description, lastCall[id]) } s <- ret.String() } } fmt.Println(output, "EXITING: Cleaning up") for activeCalls > 0 { <-callResults activeCalls-- } // Exhaust the scanned updates queue. // (The channel will be closed as a by-product of the context being Done). for range updates { } fmt.Fprintln(output, "EXITING: Done") return nil }
// The fake service knows nothing about method signatures. func (fs fakeService) MethodSignature(ctx *context.T, call rpc.ServerCall, method string) (signature.Method, error) { ctx.Infof("Fake Service Method Signature???") return signature.Method{}, nil }
// The fake service has no signature. func (fs fakeService) Signature(ctx *context.T, call rpc.ServerCall) ([]signature.Interface, error) { ctx.Infof("Fake Service Signature???") return nil, nil }