package main import ( "bufio" "context" "encoding/json" "fmt" "io" "net" "net/http" _ "net/http/pprof" "os" "strconv" "strings" "sync" "time" "github.com/singchia/frontier/api/dataplane/v1/edge" "github.com/spf13/pflag" armlog "github.com/jumboframes/armorigo/log" "github.com/singchia/geminio" ) var ( sns sync.Map methodSlice []string ) type LabelData struct { Label string `json:"label"` Data []byte `json:"data"` } func main() { methodSlice = []string{} network := pflag.String("network", "tcp", "network to dial") address := pflag.String("address", "127.0.0.1:30012", "address to dial") loglevel := pflag.String("loglevel", "info", "log level, trace debug info warn error") meta := pflag.String("meta", "test", "meta to set on connection") methods := pflag.String("methods", "", "method name, support echo") label := pflag.String("label", "label-01", "label to message or rpc") pflag.Parse() dialer := func() (net.Conn, error) { return net.Dial(*network, *address) } go func() { http.ListenAndServe("0.0.0.0:6062", nil) }() // log level, err := armlog.ParseLevel(*loglevel) if err != nil { fmt.Println("parse log level err:", err) return } armlog.SetLevel(level) armlog.SetOutput(os.Stdout) // get edge cli, err := edge.NewEdge(dialer, edge.OptionEdgeLog(armlog.DefaultLog), edge.OptionEdgeMeta([]byte(*meta))) if err != nil { armlog.Info("new edge err:", err) return } //sms := cli.ListStreams() //sns.Store("1", sms[0]) if *methods != "" { methodSlice = strings.Split(*methods, ",") } go func() { time.Sleep(200 * time.Millisecond) // register for _, method := range methodSlice { switch method { case "echo": err = cli.Register(context.TODO(), "echo", echo) if err != nil { fmt.Printf("\n> register echo err: %s\n", err) return } } } }() // receive on edge go func() { for { msg, err := cli.Receive(context.TODO()) if err == io.EOF { return } if err != nil { fmt.Println("\n> receive err:", err) fmt.Println(">>> ") continue } msg.Done() fmt.Printf("\n> receive msg, edgeID: %d streamID: %d data: %s\n", msg.ClientID(), msg.StreamID(), string(msg.Data())) fmt.Print(">>> ") } }() go func() { for { st, err := cli.AcceptStream() if err == io.EOF { return } else if err != nil { fmt.Println("\n> accept stream err:", err) fmt.Print(">>> ") continue } fmt.Println("\n> accept stream", st.StreamID()) fmt.Print(">>> ") sns.Store(strconv.FormatUint(st.StreamID(), 10), st) go handleStream(st) } }() cursor := "1" fmt.Print(">>> ") // the command-line protocol // 1. close // 2. quit // 3. switch {streamID} // 4. open {service} // 5. close {streamID} // 6. publish {msg} #note to switch to stream first // 7. publish {topic} {msg} // 8. call {method} {req} scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { text := scanner.Text() parts := strings.Split(text, " ") switch len(parts) { case 1: // 1. close // 2. quit if parts[0] == "help" { fmt.Println(`the command-line protocol 1. close 2. quit 3. open {service} 4. close {streamID} 5. switch {streamID} 6. publish {msg} #note to switch to stream first 7. publish {topic} {msg} 8. call {method} {req}`) goto NEXT } if parts[0] == "quit" || parts[0] == "close" { cli.Close() goto END } case 2: // 1. open {service} // 2. close {streamID} // 3. switch {streamID} // 4. publish {msg} if parts[0] == "open" { service := parts[1] st, err := cli.OpenStream(service) if err != nil { fmt.Println("> open stream err:", err) goto NEXT } fmt.Println("> open stream success:", st.StreamID()) sns.Store(strconv.FormatUint(st.StreamID(), 10), st) go handleStream(st) goto NEXT } if parts[0] == "close" { // close sessionID session := parts[1] sn, ok := sns.LoadAndDelete(session) if !ok { fmt.Printf("> stream id: %s not found\n", session) goto NEXT } sn.(geminio.Stream).Close() fmt.Println("> close stream success:", session) goto NEXT } if parts[0] == "switch" { session := parts[1] if session == "1" { cursor = session fmt.Println("> swith stream success:", session) goto NEXT } _, ok := sns.Load(session) if !ok { fmt.Println("> swith stream failed, not found:", session) goto NEXT } cursor = session fmt.Println("> swith stream success:", session) goto NEXT } if cursor != "1" && (parts[0] == "publish") { sn, ok := sns.Load(cursor) if !ok { fmt.Printf("> stream: %s not found\n", cursor) goto NEXT } stream := sn.(geminio.Stream) if parts[0] == "publish" { ld := &LabelData{ Label: *label, Data: []byte(parts[1]), } data, _ := json.Marshal(ld) msg := stream.NewMessage(data) err := stream.Publish(context.TODO(), msg) if err != nil { fmt.Println("> publish err:", err) goto NEXT } fmt.Println("> publish success") goto NEXT } } case 3: // 1. publish {topic} {msg} // 2. call {method} {req} if cursor != "1" { // in stream sn, ok := sns.Load(cursor) if !ok { fmt.Printf("> stream: %s not found\n", cursor) goto NEXT } stream := sn.(geminio.Stream) if parts[0] == "call" { req := stream.NewRequest([]byte(parts[2])) rsp, err := stream.Call(context.TODO(), string(parts[1]), req) if err != nil { fmt.Println("> call err:", err) goto NEXT } fmt.Println("> call success, ret:", string(rsp.Data())) goto NEXT } } if parts[0] == "publish" { ld := &LabelData{ Label: *label, Data: []byte(parts[2]), } data, _ := json.Marshal(ld) msg := cli.NewMessage(data) err := cli.Publish(context.TODO(), string(parts[1]), msg) if err != nil { fmt.Println("> publish err:", err) goto NEXT } fmt.Println("> publish success") goto NEXT } if parts[0] == "call" { ld := &LabelData{ Label: *label, Data: []byte(parts[2]), } data, _ := json.Marshal(ld) req := cli.NewRequest(data) rsp, err := cli.Call(context.TODO(), string(parts[1]), req) if err != nil { fmt.Println("> call err:", err) goto NEXT } fmt.Println("> call success, ret:", string(rsp.Data())) goto NEXT } } fmt.Println("> illegal operation") NEXT: if cursor != "1" { fmt.Printf("[%20s] >>> ", cursor) } else { fmt.Print(">>> ") } } END: time.Sleep(time.Second) } func handleStream(stream geminio.Stream) { go func() { for { msg, err := stream.Receive(context.TODO()) if err != nil { fmt.Printf("\n> streamID: %d receive err: %s\n", stream.StreamID(), err) fmt.Print(">>> ") return } msg.Done() fmt.Printf("\n> receive msg, edgeID: %d streamID: %d data: %s\n", msg.ClientID(), msg.StreamID(), string(msg.Data())) fmt.Print(">>> ") } }() go func() { for { data := make([]byte, 1024) _, err := stream.Read(data) if err != nil { fmt.Printf("\n> streamID: %d read err: %s\n", stream.StreamID(), err) fmt.Print(">>> ") return } fmt.Println("\n> read data:", stream.ClientID(), string(data)) fmt.Print(">>> ") } }() go func() { time.Sleep(200 * time.Millisecond) for _, method := range methodSlice { switch method { case "echo": err := stream.Register(context.TODO(), "echo", echo) if err != nil { armlog.Info("> register echo err:", err) return } } } }() } func echo(ctx context.Context, req geminio.Request, rsp geminio.Response) { edgeID := req.ClientID() fmt.Printf("\n> rpc called, method: %s edgeID: %d streamID: %d data: %s\n", "echo", edgeID, req.StreamID(), string(req.Data())) fmt.Print(">>> ") rsp.SetData(req.Data()) }