func (f *impl) Link(ctx *context.T, sc ifc.BridgeLinkServerCall, topics []ifc.Topic) error { ctx.Info("Link for topics ", topics) done := make(chan error, 2) cc, mu, err := mqttConnect() if err != nil { return err } // transmitter: do mqtt subscribe, then copy messages to output stream go func() { done <- transmitter(topics, sc.SendStream(), cc, mu) }() // receiver: read the input stream and copy to mqtt via cc.Publish go func() { done <- receiver(sc.RecvStream(), cc, mu) }() err = <-done // Stop sender by closing cc.Incoming cc.Disconnect() return err }
// 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 }