diff --git a/.github/workflows/tarball.yml b/.github/workflows/tarball.yml index b63eef3..664103b 100644 --- a/.github/workflows/tarball.yml +++ b/.github/workflows/tarball.yml @@ -72,7 +72,7 @@ jobs: - name: Build run: | echo "Building with $(nproc) threads" - make -C tmp build -j$(nproc) + make -C tmp build client -j$(nproc) UNKNOWN=$(./tmp/bin/signaling -version | grep unknown || true) if [ -n "$UNKNOWN" ]; then \ echo "Found unknown version: $UNKNOWN"; \ @@ -83,6 +83,11 @@ jobs: echo "Found unknown version: $UNKNOWN"; \ exit 1; \ fi + UNKNOWN=$(./tmp/bin/client -version | grep unknown || true) + if [ -n "$UNKNOWN" ]; then \ + echo "Found unknown version: $UNKNOWN"; \ + exit 1; \ + fi test: strategy: diff --git a/client/main.go b/client/main.go index 942e94b..ab87a30 100644 --- a/client/main.go +++ b/client/main.go @@ -25,6 +25,7 @@ import ( "bytes" "encoding/json" "flag" + "fmt" "io" "log" pseudorand "math/rand" @@ -34,6 +35,7 @@ import ( "os" "os/signal" "runtime" + "runtime/pprof" "sync" "sync/atomic" "time" @@ -42,15 +44,25 @@ import ( "github.com/gorilla/mux" "github.com/gorilla/websocket" "github.com/mailru/easyjson" + "github.com/mailru/easyjson/jlexer" + "github.com/mailru/easyjson/jwriter" signaling "github.com/strukturag/nextcloud-spreed-signaling" ) var ( + version = "unreleased" + + showVersion = flag.Bool("version", false, "show version and quit") + addr = flag.String("addr", "localhost:28080", "http service address") config = flag.String("config", "server.conf", "config file to use") + cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") + + memprofile = flag.String("memprofile", "", "write memory profile to file") + maxClients = flag.Int("maxClients", 100, "number of client connections") backendSecret []byte @@ -77,6 +89,41 @@ type MessagePayload struct { Now time.Time `json:"now"` } +func (m *MessagePayload) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + w.RawByte('{') + w.RawString("\"now\":") + w.Raw(m.Now.MarshalJSON()) + w.RawByte('}') + return w.Buffer.BuildBytes(), w.Error +} + +func (m *MessagePayload) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + r.Delim('{') + for !r.IsDelim('}') { + key := r.UnsafeFieldName(false) + r.WantColon() + switch key { + case "now": + if r.IsNull() { + r.Skip() + } else { + if data := r.Raw(); r.Ok() { + r.AddError((m.Now).UnmarshalJSON(data)) + } + } + default: + r.SkipRecursive() + } + r.WantComma() + } + r.Delim('}') + r.Consumed() + + return r.Error() +} + type SignalingClient struct { readyWg *sync.WaitGroup // +checklocksignore: Only written to from constructor. @@ -198,7 +245,7 @@ func (c *SignalingClient) PublicSessionId() signaling.PublicSessionId { func (c *SignalingClient) processMessageMessage(message *signaling.ServerMessage) { var msg MessagePayload - if err := json.Unmarshal(message.Message.Data, &msg); err != nil { + if err := msg.UnmarshalJSON(message.Message.Data); err != nil { log.Println("Error in unmarshal", err) return } @@ -352,7 +399,7 @@ func (c *SignalingClient) SendMessages(clients []*SignalingClient) { msgdata := MessagePayload{ Now: now, } - data, _ := json.Marshal(msgdata) + data, _ := msgdata.MarshalJSON() msg := &signaling.ClientMessage{ Type: "message", Message: &signaling.MessageClientMessage{ @@ -453,6 +500,11 @@ func main() { flag.Parse() log.SetFlags(0) + if *showVersion { + fmt.Printf("nextcloud-spreed-signaling-client version %s/%s\n", version, runtime.Version()) + os.Exit(0) + } + config, err := goconf.ReadConfigFile(*config) if err != nil { log.Fatal("Could not read configuration: ", err) @@ -463,6 +515,34 @@ func main() { log.Printf("Using a maximum of %d CPUs", runtime.GOMAXPROCS(0)) + if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + log.Fatal(err) + } + + if err := pprof.StartCPUProfile(f); err != nil { + log.Fatalf("Error writing CPU profile to %s: %s", *cpuprofile, err) + } + log.Printf("Writing CPU profile to %s ...", *cpuprofile) + defer pprof.StopCPUProfile() + } + + if *memprofile != "" { + f, err := os.Create(*memprofile) + if err != nil { + log.Fatal(err) + } + + defer func() { + log.Printf("Writing Memory profile to %s ...", *memprofile) + runtime.GC() + if err := pprof.WriteHeapProfile(f); err != nil { + log.Printf("Error writing Memory profile to %s: %s", *memprofile, err) + } + }() + } + interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt)