From a245d045c5948f60c3a5102788166a27688eb88a Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Thu, 9 Apr 2026 11:21:34 +0200 Subject: [PATCH 1/4] feat(sse): support multiple topic subscriptions replaced SSE library since the previous one only supported a single topic. --- go.mod | 3 +- go.sum | 7 +- services/sse/pkg/server/http/server.go | 18 +- services/sse/pkg/service/service.go | 98 +--- services/sse/pkg/service/sse.go | 113 ++++ vendor/github.com/r3labs/sse/v2/.gitignore | 2 - vendor/github.com/r3labs/sse/v2/.golangci.yml | 15 - .../github.com/r3labs/sse/v2/CONTRIBUTING.md | 80 --- vendor/github.com/r3labs/sse/v2/LICENSE | 373 -------------- vendor/github.com/r3labs/sse/v2/Makefile | 20 - vendor/github.com/r3labs/sse/v2/README.md | 191 ------- vendor/github.com/r3labs/sse/v2/client.go | 390 -------------- vendor/github.com/r3labs/sse/v2/event.go | 114 ---- vendor/github.com/r3labs/sse/v2/event_log.go | 43 -- vendor/github.com/r3labs/sse/v2/http.go | 120 ----- vendor/github.com/r3labs/sse/v2/server.go | 156 ------ vendor/github.com/r3labs/sse/v2/stream.go | 153 ------ vendor/github.com/r3labs/sse/v2/subscriber.go | 24 - vendor/github.com/tmaxmax/go-sse/.gitignore | 1 + .../github.com/tmaxmax/go-sse/.golangci.yml | 58 +++ vendor/github.com/tmaxmax/go-sse/CHANGELOG.md | 323 ++++++++++++ vendor/github.com/tmaxmax/go-sse/LICENSE | 21 + vendor/github.com/tmaxmax/go-sse/README.md | 485 ++++++++++++++++++ vendor/github.com/tmaxmax/go-sse/client.go | 229 +++++++++ .../tmaxmax/go-sse/client_connection.go | 277 ++++++++++ vendor/github.com/tmaxmax/go-sse/codecov.yml | 3 + vendor/github.com/tmaxmax/go-sse/event.go | 133 +++++ .../tmaxmax/go-sse/internal/parser/chunk.go | 41 ++ .../tmaxmax/go-sse/internal/parser/field.go | 44 ++ .../go-sse/internal/parser/field_parser.go | 129 +++++ .../tmaxmax/go-sse/internal/parser/parser.go | 117 +++++ vendor/github.com/tmaxmax/go-sse/joe.go | 327 ++++++++++++ vendor/github.com/tmaxmax/go-sse/message.go | 354 +++++++++++++ .../tmaxmax/go-sse/message_fields.go | 181 +++++++ vendor/github.com/tmaxmax/go-sse/replay.go | 388 ++++++++++++++ vendor/github.com/tmaxmax/go-sse/server.go | 233 +++++++++ vendor/github.com/tmaxmax/go-sse/session.go | 160 ++++++ .../gopkg.in/cenkalti/backoff.v1/.gitignore | 22 - .../gopkg.in/cenkalti/backoff.v1/.travis.yml | 9 - vendor/gopkg.in/cenkalti/backoff.v1/LICENSE | 20 - vendor/gopkg.in/cenkalti/backoff.v1/README.md | 30 -- .../gopkg.in/cenkalti/backoff.v1/backoff.go | 66 --- .../gopkg.in/cenkalti/backoff.v1/context.go | 60 --- .../cenkalti/backoff.v1/exponential.go | 156 ------ vendor/gopkg.in/cenkalti/backoff.v1/retry.go | 78 --- vendor/gopkg.in/cenkalti/backoff.v1/ticker.go | 81 --- vendor/gopkg.in/cenkalti/backoff.v1/tries.go | 35 -- vendor/modules.txt | 10 +- 48 files changed, 3645 insertions(+), 2346 deletions(-) create mode 100644 services/sse/pkg/service/sse.go delete mode 100644 vendor/github.com/r3labs/sse/v2/.gitignore delete mode 100644 vendor/github.com/r3labs/sse/v2/.golangci.yml delete mode 100644 vendor/github.com/r3labs/sse/v2/CONTRIBUTING.md delete mode 100644 vendor/github.com/r3labs/sse/v2/LICENSE delete mode 100644 vendor/github.com/r3labs/sse/v2/Makefile delete mode 100644 vendor/github.com/r3labs/sse/v2/README.md delete mode 100644 vendor/github.com/r3labs/sse/v2/client.go delete mode 100644 vendor/github.com/r3labs/sse/v2/event.go delete mode 100644 vendor/github.com/r3labs/sse/v2/event_log.go delete mode 100644 vendor/github.com/r3labs/sse/v2/http.go delete mode 100644 vendor/github.com/r3labs/sse/v2/server.go delete mode 100644 vendor/github.com/r3labs/sse/v2/stream.go delete mode 100644 vendor/github.com/r3labs/sse/v2/subscriber.go create mode 100644 vendor/github.com/tmaxmax/go-sse/.gitignore create mode 100644 vendor/github.com/tmaxmax/go-sse/.golangci.yml create mode 100644 vendor/github.com/tmaxmax/go-sse/CHANGELOG.md create mode 100644 vendor/github.com/tmaxmax/go-sse/LICENSE create mode 100644 vendor/github.com/tmaxmax/go-sse/README.md create mode 100644 vendor/github.com/tmaxmax/go-sse/client.go create mode 100644 vendor/github.com/tmaxmax/go-sse/client_connection.go create mode 100644 vendor/github.com/tmaxmax/go-sse/codecov.yml create mode 100644 vendor/github.com/tmaxmax/go-sse/event.go create mode 100644 vendor/github.com/tmaxmax/go-sse/internal/parser/chunk.go create mode 100644 vendor/github.com/tmaxmax/go-sse/internal/parser/field.go create mode 100644 vendor/github.com/tmaxmax/go-sse/internal/parser/field_parser.go create mode 100644 vendor/github.com/tmaxmax/go-sse/internal/parser/parser.go create mode 100644 vendor/github.com/tmaxmax/go-sse/joe.go create mode 100644 vendor/github.com/tmaxmax/go-sse/message.go create mode 100644 vendor/github.com/tmaxmax/go-sse/message_fields.go create mode 100644 vendor/github.com/tmaxmax/go-sse/replay.go create mode 100644 vendor/github.com/tmaxmax/go-sse/server.go create mode 100644 vendor/github.com/tmaxmax/go-sse/session.go delete mode 100644 vendor/gopkg.in/cenkalti/backoff.v1/.gitignore delete mode 100644 vendor/gopkg.in/cenkalti/backoff.v1/.travis.yml delete mode 100644 vendor/gopkg.in/cenkalti/backoff.v1/LICENSE delete mode 100644 vendor/gopkg.in/cenkalti/backoff.v1/README.md delete mode 100644 vendor/gopkg.in/cenkalti/backoff.v1/backoff.go delete mode 100644 vendor/gopkg.in/cenkalti/backoff.v1/context.go delete mode 100644 vendor/gopkg.in/cenkalti/backoff.v1/exponential.go delete mode 100644 vendor/gopkg.in/cenkalti/backoff.v1/retry.go delete mode 100644 vendor/gopkg.in/cenkalti/backoff.v1/ticker.go delete mode 100644 vendor/gopkg.in/cenkalti/backoff.v1/tries.go diff --git a/go.mod b/go.mod index a05593aa71..e28d00e8ef 100644 --- a/go.mod +++ b/go.mod @@ -71,7 +71,6 @@ require ( github.com/pkg/errors v0.9.1 github.com/pkg/xattr v0.4.12 github.com/prometheus/client_golang v1.23.2 - github.com/r3labs/sse/v2 v2.10.0 github.com/riandyrn/otelchi v0.12.2 github.com/rogpeppe/go-internal v1.14.1 github.com/rs/cors v1.11.1 @@ -89,6 +88,7 @@ require ( github.com/thejerf/suture/v4 v4.0.6 github.com/tidwall/gjson v1.18.0 github.com/tidwall/sjson v1.2.5 + github.com/tmaxmax/go-sse v0.11.0 github.com/tus/tusd/v2 v2.9.2 github.com/unrolled/secure v1.16.0 github.com/vmihailenco/msgpack/v5 v5.4.1 @@ -394,7 +394,6 @@ require ( golang.org/x/tools v0.42.0 // indirect google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d // indirect - gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect sigs.k8s.io/yaml v1.6.0 // indirect diff --git a/go.sum b/go.sum index e9b7ca6f99..37cab7ca7a 100644 --- a/go.sum +++ b/go.sum @@ -1059,8 +1059,6 @@ github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9 github.com/prometheus/statsd_exporter v0.22.8 h1:Qo2D9ZzaQG+id9i5NYNGmbf1aa/KxKbB9aKfMS+Yib0= github.com/prometheus/statsd_exporter v0.22.8/go.mod h1:/DzwbTEaFTE0Ojz5PqcSk6+PFHOPWGxdXVr6yC8eFOM= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/r3labs/sse/v2 v2.10.0 h1:hFEkLLFY4LDifoHdiCN/LlGBAdVJYsANaLqNYa1l/v0= -github.com/r3labs/sse/v2 v2.10.0/go.mod h1:Igau6Whc+F17QUgML1fYe1VPZzTV6EMCnYktEmkNJ7I= github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg= @@ -1217,6 +1215,8 @@ github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYI github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= +github.com/tmaxmax/go-sse v0.11.0 h1:nogmJM6rJUoOLoAwEKeQe5XlVpt9l7N82SS1jI7lWFg= +github.com/tmaxmax/go-sse v0.11.0/go.mod h1:u/2kZQR1tyngo1lKaNCj1mJmhXGZWS1Zs5yiSOD+Eg8= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 h1:PM5hJF7HVfNWmCjMdEfbuOBNXSVF2cMFGgQTPdKCbwM= github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208/go.mod h1:BzWtXXrXzZUvMacR0oF/fbDDgUPO8L36tDMmRAf14ns= @@ -1420,7 +1420,6 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1747,8 +1746,6 @@ google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y= -gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UDAkHu8BrjI= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/services/sse/pkg/server/http/server.go b/services/sse/pkg/server/http/server.go index 72e99e86f1..cee45c051f 100644 --- a/services/sse/pkg/server/http/server.go +++ b/services/sse/pkg/server/http/server.go @@ -2,12 +2,16 @@ package http import ( "fmt" - stdhttp "net/http" "github.com/go-chi/chi/v5" chimiddleware "github.com/go-chi/chi/v5/middleware" "github.com/google/uuid" + "github.com/riandyrn/otelchi" + "go-micro.dev/v4" + + "github.com/opencloud-eu/reva/v2/pkg/events" + "github.com/opencloud-eu/opencloud/pkg/account" "github.com/opencloud-eu/opencloud/pkg/cors" "github.com/opencloud-eu/opencloud/pkg/middleware" @@ -15,9 +19,6 @@ import ( "github.com/opencloud-eu/opencloud/pkg/tracing" "github.com/opencloud-eu/opencloud/pkg/version" svc "github.com/opencloud-eu/opencloud/services/sse/pkg/service" - "github.com/opencloud-eu/reva/v2/pkg/events" - "github.com/riandyrn/otelchi" - "go-micro.dev/v4" ) // Service is the service interface @@ -83,12 +84,17 @@ func Server(opts ...Option) (http.Service, error) { return http.Service{}, err } - handle, err := svc.NewSSE(options.Config, options.Logger, ch, mux) + sseHandler, err := svc.NewSSEHandler(options.Context, options.Config, options.Logger, ch) + if err != nil { + return http.Service{}, err + } + + svcHandler, err := svc.New(mux, sseHandler) if err != nil { return http.Service{}, err } - if err := micro.RegisterHandler(service.Server(), handle); err != nil { + if err := micro.RegisterHandler(service.Server(), svcHandler); err != nil { return http.Service{}, err } diff --git a/services/sse/pkg/service/service.go b/services/sse/pkg/service/service.go index 97a211e215..1657aabec2 100644 --- a/services/sse/pkg/service/service.go +++ b/services/sse/pkg/service/service.go @@ -2,102 +2,22 @@ package service import ( "net/http" - "time" "github.com/go-chi/chi/v5" - "github.com/r3labs/sse/v2" - - revactx "github.com/opencloud-eu/reva/v2/pkg/ctx" - "github.com/opencloud-eu/reva/v2/pkg/events" - - "github.com/opencloud-eu/opencloud/pkg/log" - "github.com/opencloud-eu/opencloud/services/sse/pkg/config" ) -// SSE defines implements the business logic for Service. -type SSE struct { - c *config.Config - l log.Logger - m *chi.Mux - sse *sse.Server - evChannel <-chan events.Event -} - -// NewSSE returns a service implementation for Service. -func NewSSE(c *config.Config, l log.Logger, ch <-chan events.Event, mux *chi.Mux) (SSE, error) { - s := SSE{ - c: c, - l: l, - m: mux, - sse: sse.New(), - evChannel: ch, - } - mux.Route("/ocs/v2.php/apps/notifications/api/v1/notifications", func(r chi.Router) { - r.Get("/sse", s.HandleSSE) - }) - - go s.ListenForEvents() - - return s, nil +type Service struct { + handler http.Handler } -// ServeHTTP fulfills Handler interface -func (s SSE) ServeHTTP(w http.ResponseWriter, r *http.Request) { - s.m.ServeHTTP(w, r) -} +func New(mux *chi.Mux, sseHandler http.Handler) (Service, error) { + mux.Get("/ocs/v2.php/apps/notifications/api/v1/notifications/sse", sseHandler.ServeHTTP) -// ListenForEvents listens for events -func (s SSE) ListenForEvents() { - for e := range s.evChannel { - switch ev := e.Event.(type) { - default: - s.l.Error().Interface("event", ev).Msg("unhandled event") - case events.SendSSE: - for _, uid := range ev.UserIDs { - s.sse.Publish(uid, &sse.Event{ - Event: []byte(ev.Type), - Data: ev.Message, - }) - } - } - } + return Service{ + handler: mux, + }, nil } -// HandleSSE is the GET handler for events -func (s SSE) HandleSSE(w http.ResponseWriter, r *http.Request) { - u, ok := revactx.ContextGetUser(r.Context()) - if !ok { - s.l.Error().Msg("sse: no user in context") - w.WriteHeader(http.StatusInternalServerError) - return - } - - uid := u.GetId().GetOpaqueId() - if uid == "" { - s.l.Error().Msg("sse: user in context is broken") - w.WriteHeader(http.StatusInternalServerError) - return - } - - stream := s.sse.CreateStream(uid) - stream.AutoReplay = false - - if s.c.KeepAliveInterval != 0 { - ticker := time.NewTicker(s.c.KeepAliveInterval) - defer ticker.Stop() - go func() { - for range ticker.C { - s.sse.Publish(uid, &sse.Event{ - Comment: []byte("keepalive"), - }) - } - }() - } - - // add stream to URL - q := r.URL.Query() - q.Set("stream", uid) - r.URL.RawQuery = q.Encode() - - s.sse.ServeHTTP(w, r) +func (s Service) ServeHTTP(w http.ResponseWriter, r *http.Request) { + s.handler.ServeHTTP(w, r) } diff --git a/services/sse/pkg/service/sse.go b/services/sse/pkg/service/sse.go new file mode 100644 index 0000000000..ae4c6b351f --- /dev/null +++ b/services/sse/pkg/service/sse.go @@ -0,0 +1,113 @@ +package service + +import ( + "context" + "net/http" + "time" + + "github.com/tmaxmax/go-sse" + + revactx "github.com/opencloud-eu/reva/v2/pkg/ctx" + "github.com/opencloud-eu/reva/v2/pkg/events" + + "github.com/opencloud-eu/opencloud/pkg/log" + "github.com/opencloud-eu/opencloud/services/sse/pkg/config" +) + +const ( + SSETopicAllUsers = "all" +) + +// SSEHandler defines implements the business logic for Service. +type SSEHandler struct { + conf *config.Config + logger log.Logger + server *sse.Server + channel <-chan events.Event +} + +// NewSSEHandler returns a service implementation for Service. +func NewSSEHandler(ctx context.Context, conf *config.Config, logger log.Logger, ch <-chan events.Event) (SSEHandler, error) { + handler := SSEHandler{ + conf: conf, + logger: logger, + channel: ch, + } + + handler.server = &sse.Server{ + OnSession: func(_ http.ResponseWriter, r *http.Request) (topics []string, allowed bool) { + return handler.topics(r) + }, + } + + go func() { + select { + case <-ctx.Done(): + if err := handler.server.Shutdown(ctx); err != nil { + logger.Error().Err(err).Msg("failed to shutdown SSE handler") + } + return + } + }() + + go handler.listen() + + return handler, nil +} + +// ServeHTTP fulfills Handler interface +func (h SSEHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + topics, ok := h.topics(r) + if !ok { + h.logger.Error().Msg("sse: failed to get topics") + w.WriteHeader(http.StatusInternalServerError) + return + } + + if h.conf.KeepAliveInterval != 0 { + ticker := time.NewTicker(h.conf.KeepAliveInterval) + defer ticker.Stop() + go func() { + for range ticker.C { + m := &sse.Message{} + m.AppendData("keep-alive") + if err := h.server.Publish(m, topics...); err != nil { + h.logger.Error().Err(err).Msg("sse: failed to publish message") + } + } + }() + } + h.server.ServeHTTP(w, r) +} + +// ListenForEvents listens for events +func (h SSEHandler) listen() { + for e := range h.channel { + switch ev := e.Event.(type) { + default: + h.logger.Error().Interface("event", ev).Msg("unhandled event") + case events.SendSSE: + m := &sse.Message{ + Type: sse.Type(ev.Type), + } + m.AppendData(string(ev.Message)) + if err := h.server.Publish(m, ev.UserIDs...); err != nil { + h.logger.Error().Err(err).Msg("sse: failed to publish message") + } + } + } +} + +func (h SSEHandler) topics(r *http.Request) ([]string, bool) { + u, ok := revactx.ContextGetUser(r.Context()) + if !ok { + return nil, false + } + + uid := u.GetId().GetOpaqueId() + if uid == "" { + return nil, false + } + + return append([]string{SSETopicAllUsers}, uid), true +} diff --git a/vendor/github.com/r3labs/sse/v2/.gitignore b/vendor/github.com/r3labs/sse/v2/.gitignore deleted file mode 100644 index d48c759d6c..0000000000 --- a/vendor/github.com/r3labs/sse/v2/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.idea -.vscode \ No newline at end of file diff --git a/vendor/github.com/r3labs/sse/v2/.golangci.yml b/vendor/github.com/r3labs/sse/v2/.golangci.yml deleted file mode 100644 index 5a76e9a0fd..0000000000 --- a/vendor/github.com/r3labs/sse/v2/.golangci.yml +++ /dev/null @@ -1,15 +0,0 @@ -linters: - enable-all: true - disable: - - gofmt - - gofumpt - - goimports - - golint # deprecated - - interfacer # deprecated - - maligned # deprecated - - scopelint # deprecated - - varnamelen - -linters-settings: - govet: - enable-all: true diff --git a/vendor/github.com/r3labs/sse/v2/CONTRIBUTING.md b/vendor/github.com/r3labs/sse/v2/CONTRIBUTING.md deleted file mode 100644 index b9c7859d3c..0000000000 --- a/vendor/github.com/r3labs/sse/v2/CONTRIBUTING.md +++ /dev/null @@ -1,80 +0,0 @@ -# Contributing guidelines - -Looking to contribute something to this project? Here's how you can help: - -Please take a moment to review this document in order to make the contribution process easy and effective for everyone involved. - -Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue or assessing patches and features. - -We also have a [code of conduct](https://ernest.io/conduct). - -## Using the issue tracker - -The issue tracker is the preferred channel for [bug reports](#bug-reports), [features requests](#feature-requests) and [submitting pull requests](#pull-requests), but please respect the following restrictions: - -* Please **do not** use the issue tracker for personal support requests. - -* Please **do not** derail issues. Keep the discussion on topic and - respect the opinions of others. - - -## Bug reports - -A bug is a _demonstrable problem_ that is caused by the code in the repository. -Good bug reports are extremely helpful - thank you! - -Guidelines for bug reports: - -1. **Use the GitHub issue search** — check if the issue has already been - reported. - -2. **Check if the issue has been fixed** — try to reproduce it using the - latest `master` or `develop` branch in the repository. - -3. **Isolate the problem** — create a reduced test case and a live example. - -A good bug report shouldn't leave others needing to chase you up for more -information. Please try to be as detailed as possible in your report. What is -your environment? What steps will reproduce the issue? Which environment experience the problem? What would you expect to be the outcome? All these -details will help people to fix any potential bugs. - -Example: - -> Short and descriptive example bug report title -> -> A summary of the issue and the environment in which it occurs. If -> suitable, include the steps required to reproduce the bug. -> -> 1. This is the first step -> 2. This is the second step -> 3. Further steps, etc. -> -> `` - a link to the reduced test case -> -> Any other information you want to share that is relevant to the issue being -> reported. This might include the lines of code that you have identified as -> causing the bug, and potential solutions (and your opinions on their -> merits). - - -## Feature requests - -Feature requests are welcome. But take a moment to find out whether your idea -fits with the scope and aims of the project. It's up to *you* to make a strong -case to convince the project's developers of the merits of this feature. Please -provide as much detail and context as possible. - - -## Pull requests - -Good pull requests - patches, improvements, new features - are a fantastic -help. They should remain focused in scope and avoid containing unrelated -commits. - -[**Please ask first**](https://ernest.io/community) before embarking on any significant pull request (e.g. -implementing features, refactoring code, porting to a different language), -otherwise you risk spending a lot of time working on something that the -project's developers might not want to merge into the project. - -Please adhere to the coding conventions used throughout a project (indentation, -accurate comments, etc.) and any other requirements (such as test coverage). diff --git a/vendor/github.com/r3labs/sse/v2/LICENSE b/vendor/github.com/r3labs/sse/v2/LICENSE deleted file mode 100644 index a612ad9813..0000000000 --- a/vendor/github.com/r3labs/sse/v2/LICENSE +++ /dev/null @@ -1,373 +0,0 @@ -Mozilla Public License Version 2.0 -================================== - -1. Definitions --------------- - -1.1. "Contributor" - means each individual or legal entity that creates, contributes to - the creation of, or owns Covered Software. - -1.2. "Contributor Version" - means the combination of the Contributions of others (if any) used - by a Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - means Source Code Form to which the initial Contributor has attached - the notice in Exhibit A, the Executable Form of such Source Code - Form, and Modifications of such Source Code Form, in each case - including portions thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - (a) that the initial Contributor has attached the notice described - in Exhibit B to the Covered Software; or - - (b) that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the - terms of a Secondary License. - -1.6. "Executable Form" - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - means a work that combines Covered Software with other material, in - a separate file or files, that is not Covered Software. - -1.8. "License" - means this document. - -1.9. "Licensable" - means having the right to grant, to the maximum extent possible, - whether at the time of the initial grant or subsequently, any and - all of the rights conveyed by this License. - -1.10. "Modifications" - means any of the following: - - (a) any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered - Software; or - - (b) any new file in Source Code Form that contains any Covered - Software. - -1.11. "Patent Claims" of a Contributor - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the - License, by the making, using, selling, offering for sale, having - made, import, or transfer of either its Contributions or its - Contributor Version. - -1.12. "Secondary License" - means either the GNU General Public License, Version 2.0, the GNU - Lesser General Public License, Version 2.1, the GNU Affero General - Public License, Version 3.0, or any later versions of those - licenses. - -1.13. "Source Code Form" - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that - controls, is controlled by, or is under common control with You. For - purposes of this definition, "control" means (a) the power, direct - or indirect, to cause the direction or management of such entity, - whether by contract or otherwise, or (b) ownership of more than - fifty percent (50%) of the outstanding shares or beneficial - ownership of such entity. - -2. License Grants and Conditions --------------------------------- - -2.1. Grants - -Each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: - -(a) under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - -(b) under Patent Claims of such Contributor to make, use, sell, offer - for sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -(a) for any code that a Contributor has removed from Covered Software; - or - -(b) for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - -(c) under Patent Claims infringed by Covered Software in the absence of - its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -3. Responsibilities -------------------- - -3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -(a) such Covered Software must also be made available in Source Code - Form, as described in Section 3.1, and You must inform recipients of - the Executable Form how they can obtain a copy of such Source Code - Form by reasonable means in a timely manner, at a charge no more - than the cost of distribution to the recipient; and - -(b) You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter - the recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - -You may choose to offer, and to charge a fee for, warranty, support, -indemnity or liability obligations to one or more recipients of Covered -Software. However, You may do so only on Your own behalf, and not on -behalf of any Contributor. You must make it absolutely clear that any -such warranty, support, indemnity, or liability obligation is offered by -You alone, and You hereby agree to indemnify every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -4. Inability to Comply Due to Statute or Regulation ---------------------------------------------------- - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -5. Termination --------------- - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - -************************************************************************ -* * -* 6. Disclaimer of Warranty * -* ------------------------- * -* * -* Covered Software is provided under this License on an "as is" * -* basis, without warranty of any kind, either expressed, implied, or * -* statutory, including, without limitation, warranties that the * -* Covered Software is free of defects, merchantable, fit for a * -* particular purpose or non-infringing. The entire risk as to the * -* quality and performance of the Covered Software is with You. * -* Should any Covered Software prove defective in any respect, You * -* (not any Contributor) assume the cost of any necessary servicing, * -* repair, or correction. This disclaimer of warranty constitutes an * -* essential part of this License. No use of any Covered Software is * -* authorized under this License except under this disclaimer. * -* * -************************************************************************ - -************************************************************************ -* * -* 7. Limitation of Liability * -* -------------------------- * -* * -* Under no circumstances and under no legal theory, whether tort * -* (including negligence), contract, or otherwise, shall any * -* Contributor, or anyone who distributes Covered Software as * -* permitted above, be liable to You for any direct, indirect, * -* special, incidental, or consequential damages of any character * -* including, without limitation, damages for lost profits, loss of * -* goodwill, work stoppage, computer failure or malfunction, or any * -* and all other commercial damages or losses, even if such party * -* shall have been informed of the possibility of such damages. This * -* limitation of liability shall not apply to liability for death or * -* personal injury resulting from such party's negligence to the * -* extent applicable law prohibits such limitation. Some * -* jurisdictions do not allow the exclusion or limitation of * -* incidental or consequential damages, so this exclusion and * -* limitation may not apply to You. * -* * -************************************************************************ - -8. Litigation -------------- - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -9. Miscellaneous ----------------- - -This License represents the complete agreement concerning the subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -10. Versions of the License ---------------------------- - -10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice -------------------------------------------- - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice ---------------------------------------------------------- - - This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the Mozilla Public License, v. 2.0. diff --git a/vendor/github.com/r3labs/sse/v2/Makefile b/vendor/github.com/r3labs/sse/v2/Makefile deleted file mode 100644 index a63b7001e0..0000000000 --- a/vendor/github.com/r3labs/sse/v2/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -install: - go install -v - -build: - go build -v ./... - -lint: - golint ./... - go vet ./... - -test: - go test -v ./... --cover - -deps: - go get -u gopkg.in/cenkalti/backoff.v1 - go get -u github.com/golang/lint/golint - go get -u github.com/stretchr/testify - -clean: - go clean diff --git a/vendor/github.com/r3labs/sse/v2/README.md b/vendor/github.com/r3labs/sse/v2/README.md deleted file mode 100644 index c2201be698..0000000000 --- a/vendor/github.com/r3labs/sse/v2/README.md +++ /dev/null @@ -1,191 +0,0 @@ -# SSE - Server Sent Events Client/Server Library for Go - -## Synopsis - -SSE is a client/server implementation for Server Sent Events for Golang. - -## Build status - -* Master: [![CircleCI Master](https://circleci.com/gh/r3labs/sse.svg?style=svg)](https://circleci.com/gh/r3labs/sse) - -## Quick start - -To install: -``` -go get github.com/r3labs/sse/v2 -``` - -To Test: - -```sh -$ make deps -$ make test -``` - -#### Example Server - -There are two parts of the server. It is comprised of the message scheduler and a http handler function. -The messaging system is started when running: - -```go -func main() { - server := sse.New() -} -``` - -To add a stream to this handler: - -```go -func main() { - server := sse.New() - server.CreateStream("messages") -} -``` - -This creates a new stream inside of the scheduler. Seeing as there are no consumers, publishing a message to this channel will do nothing. -Clients can connect to this stream once the http handler is started by specifying _stream_ as a url parameter, like so: - -``` -http://server/events?stream=messages -``` - - -In order to start the http server: - -```go -func main() { - server := sse.New() - - // Create a new Mux and set the handler - mux := http.NewServeMux() - mux.HandleFunc("/events", server.ServeHTTP) - - http.ListenAndServe(":8080", mux) -} -``` - -To publish messages to a stream: - -```go -func main() { - server := sse.New() - - // Publish a payload to the stream - server.Publish("messages", &sse.Event{ - Data: []byte("ping"), - }) -} -``` - -Please note there must be a stream with the name you specify and there must be subscribers to that stream - -A way to detect disconnected clients: - -```go -func main() { - server := sse.New() - - mux := http.NewServeMux() - mux.HandleFunc("/events", func(w http.ResponseWriter, r *http.Request) { - go func() { - // Received Browser Disconnection - <-r.Context().Done() - println("The client is disconnected here") - return - }() - - server.ServeHTTP(w, r) - }) - - http.ListenAndServe(":8080", mux) -} -``` - -#### Example Client - -The client exposes a way to connect to an SSE server. The client can also handle multiple events under the same url. - -To create a new client: - -```go -func main() { - client := sse.NewClient("http://server/events") -} -``` - -To subscribe to an event stream, please use the Subscribe function. This accepts the name of the stream and a handler function: - -```go -func main() { - client := sse.NewClient("http://server/events") - - client.Subscribe("messages", func(msg *sse.Event) { - // Got some data! - fmt.Println(msg.Data) - }) -} -``` - -Please note that this function will block the current thread. You can run this function in a go routine. - -If you wish to have events sent to a channel, you can use SubscribeChan: - -```go -func main() { - events := make(chan *sse.Event) - - client := sse.NewClient("http://server/events") - client.SubscribeChan("messages", events) -} -``` - -#### HTTP client parameters - -To add additional parameters to the http client, such as disabling ssl verification for self signed certs, you can override the http client or update its options: - -```go -func main() { - client := sse.NewClient("http://server/events") - client.Connection.Transport = &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } -} -``` - -#### URL query parameters - -To set custom query parameters on the client or disable the stream parameter altogether: - -```go -func main() { - client := sse.NewClient("http://server/events?search=example") - - client.SubscribeRaw(func(msg *sse.Event) { - // Got some data! - fmt.Println(msg.Data) - }) -} -``` - - -## Contributing - -Please read through our -[contributing guidelines](CONTRIBUTING.md). -Included are directions for opening issues, coding standards, and notes on -development. - -Moreover, if your pull request contains patches or features, you must include -relevant unit tests. - -## Versioning - -For transparency into our release cycle and in striving to maintain backward -compatibility, this project is maintained under [the Semantic Versioning guidelines](http://semver.org/). - -## Copyright and License - -Code and documentation copyright since 2015 r3labs.io authors. - -Code released under -[the Mozilla Public License Version 2.0](LICENSE). diff --git a/vendor/github.com/r3labs/sse/v2/client.go b/vendor/github.com/r3labs/sse/v2/client.go deleted file mode 100644 index 61772b624d..0000000000 --- a/vendor/github.com/r3labs/sse/v2/client.go +++ /dev/null @@ -1,390 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package sse - -import ( - "bytes" - "context" - "encoding/base64" - "errors" - "fmt" - "io" - "net/http" - "sync" - "sync/atomic" - "time" - - "gopkg.in/cenkalti/backoff.v1" -) - -var ( - headerID = []byte("id:") - headerData = []byte("data:") - headerEvent = []byte("event:") - headerRetry = []byte("retry:") -) - -func ClientMaxBufferSize(s int) func(c *Client) { - return func(c *Client) { - c.maxBufferSize = s - } -} - -// ConnCallback defines a function to be called on a particular connection event -type ConnCallback func(c *Client) - -// ResponseValidator validates a response -type ResponseValidator func(c *Client, resp *http.Response) error - -// Client handles an incoming server stream -type Client struct { - Retry time.Time - ReconnectStrategy backoff.BackOff - disconnectcb ConnCallback - connectedcb ConnCallback - subscribed map[chan *Event]chan struct{} - Headers map[string]string - ReconnectNotify backoff.Notify - ResponseValidator ResponseValidator - Connection *http.Client - URL string - LastEventID atomic.Value // []byte - maxBufferSize int - mu sync.Mutex - EncodingBase64 bool - Connected bool -} - -// NewClient creates a new client -func NewClient(url string, opts ...func(c *Client)) *Client { - c := &Client{ - URL: url, - Connection: &http.Client{}, - Headers: make(map[string]string), - subscribed: make(map[chan *Event]chan struct{}), - maxBufferSize: 1 << 16, - } - - for _, opt := range opts { - opt(c) - } - - return c -} - -// Subscribe to a data stream -func (c *Client) Subscribe(stream string, handler func(msg *Event)) error { - return c.SubscribeWithContext(context.Background(), stream, handler) -} - -// SubscribeWithContext to a data stream with context -func (c *Client) SubscribeWithContext(ctx context.Context, stream string, handler func(msg *Event)) error { - operation := func() error { - resp, err := c.request(ctx, stream) - if err != nil { - return err - } - if validator := c.ResponseValidator; validator != nil { - err = validator(c, resp) - if err != nil { - return err - } - } else if resp.StatusCode != 200 { - resp.Body.Close() - return fmt.Errorf("could not connect to stream: %s", http.StatusText(resp.StatusCode)) - } - defer resp.Body.Close() - - reader := NewEventStreamReader(resp.Body, c.maxBufferSize) - eventChan, errorChan := c.startReadLoop(reader) - - for { - select { - case err = <-errorChan: - return err - case msg := <-eventChan: - handler(msg) - } - } - } - - // Apply user specified reconnection strategy or default to standard NewExponentialBackOff() reconnection method - var err error - if c.ReconnectStrategy != nil { - err = backoff.RetryNotify(operation, c.ReconnectStrategy, c.ReconnectNotify) - } else { - err = backoff.RetryNotify(operation, backoff.NewExponentialBackOff(), c.ReconnectNotify) - } - return err -} - -// SubscribeChan sends all events to the provided channel -func (c *Client) SubscribeChan(stream string, ch chan *Event) error { - return c.SubscribeChanWithContext(context.Background(), stream, ch) -} - -// SubscribeChanWithContext sends all events to the provided channel with context -func (c *Client) SubscribeChanWithContext(ctx context.Context, stream string, ch chan *Event) error { - var connected bool - errch := make(chan error) - c.mu.Lock() - c.subscribed[ch] = make(chan struct{}) - c.mu.Unlock() - - operation := func() error { - resp, err := c.request(ctx, stream) - if err != nil { - return err - } - if validator := c.ResponseValidator; validator != nil { - err = validator(c, resp) - if err != nil { - return err - } - } else if resp.StatusCode != 200 { - resp.Body.Close() - return fmt.Errorf("could not connect to stream: %s", http.StatusText(resp.StatusCode)) - } - defer resp.Body.Close() - - if !connected { - // Notify connect - errch <- nil - connected = true - } - - reader := NewEventStreamReader(resp.Body, c.maxBufferSize) - eventChan, errorChan := c.startReadLoop(reader) - - for { - var msg *Event - // Wait for message to arrive or exit - select { - case <-c.subscribed[ch]: - return nil - case err = <-errorChan: - return err - case msg = <-eventChan: - } - - // Wait for message to be sent or exit - if msg != nil { - select { - case <-c.subscribed[ch]: - return nil - case ch <- msg: - // message sent - } - } - } - } - - go func() { - defer c.cleanup(ch) - // Apply user specified reconnection strategy or default to standard NewExponentialBackOff() reconnection method - var err error - if c.ReconnectStrategy != nil { - err = backoff.RetryNotify(operation, c.ReconnectStrategy, c.ReconnectNotify) - } else { - err = backoff.RetryNotify(operation, backoff.NewExponentialBackOff(), c.ReconnectNotify) - } - - // channel closed once connected - if err != nil && !connected { - errch <- err - } - }() - err := <-errch - close(errch) - return err -} - -func (c *Client) startReadLoop(reader *EventStreamReader) (chan *Event, chan error) { - outCh := make(chan *Event) - erChan := make(chan error) - go c.readLoop(reader, outCh, erChan) - return outCh, erChan -} - -func (c *Client) readLoop(reader *EventStreamReader, outCh chan *Event, erChan chan error) { - for { - // Read each new line and process the type of event - event, err := reader.ReadEvent() - if err != nil { - if err == io.EOF { - erChan <- nil - return - } - // run user specified disconnect function - if c.disconnectcb != nil { - c.Connected = false - c.disconnectcb(c) - } - erChan <- err - return - } - - if !c.Connected && c.connectedcb != nil { - c.Connected = true - c.connectedcb(c) - } - - // If we get an error, ignore it. - var msg *Event - if msg, err = c.processEvent(event); err == nil { - if len(msg.ID) > 0 { - c.LastEventID.Store(msg.ID) - } else { - msg.ID, _ = c.LastEventID.Load().([]byte) - } - - // Send downstream if the event has something useful - if msg.hasContent() { - outCh <- msg - } - } - } -} - -// SubscribeRaw to an sse endpoint -func (c *Client) SubscribeRaw(handler func(msg *Event)) error { - return c.Subscribe("", handler) -} - -// SubscribeRawWithContext to an sse endpoint with context -func (c *Client) SubscribeRawWithContext(ctx context.Context, handler func(msg *Event)) error { - return c.SubscribeWithContext(ctx, "", handler) -} - -// SubscribeChanRaw sends all events to the provided channel -func (c *Client) SubscribeChanRaw(ch chan *Event) error { - return c.SubscribeChan("", ch) -} - -// SubscribeChanRawWithContext sends all events to the provided channel with context -func (c *Client) SubscribeChanRawWithContext(ctx context.Context, ch chan *Event) error { - return c.SubscribeChanWithContext(ctx, "", ch) -} - -// Unsubscribe unsubscribes a channel -func (c *Client) Unsubscribe(ch chan *Event) { - c.mu.Lock() - defer c.mu.Unlock() - - if c.subscribed[ch] != nil { - c.subscribed[ch] <- struct{}{} - } -} - -// OnDisconnect specifies the function to run when the connection disconnects -func (c *Client) OnDisconnect(fn ConnCallback) { - c.disconnectcb = fn -} - -// OnConnect specifies the function to run when the connection is successful -func (c *Client) OnConnect(fn ConnCallback) { - c.connectedcb = fn -} - -func (c *Client) request(ctx context.Context, stream string) (*http.Response, error) { - req, err := http.NewRequest("GET", c.URL, nil) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - - // Setup request, specify stream to connect to - if stream != "" { - query := req.URL.Query() - query.Add("stream", stream) - req.URL.RawQuery = query.Encode() - } - - req.Header.Set("Cache-Control", "no-cache") - req.Header.Set("Accept", "text/event-stream") - req.Header.Set("Connection", "keep-alive") - - lastID, exists := c.LastEventID.Load().([]byte) - if exists && lastID != nil { - req.Header.Set("Last-Event-ID", string(lastID)) - } - - // Add user specified headers - for k, v := range c.Headers { - req.Header.Set(k, v) - } - - return c.Connection.Do(req) -} - -func (c *Client) processEvent(msg []byte) (event *Event, err error) { - var e Event - - if len(msg) < 1 { - return nil, errors.New("event message was empty") - } - - // Normalize the crlf to lf to make it easier to split the lines. - // Split the line by "\n" or "\r", per the spec. - for _, line := range bytes.FieldsFunc(msg, func(r rune) bool { return r == '\n' || r == '\r' }) { - switch { - case bytes.HasPrefix(line, headerID): - e.ID = append([]byte(nil), trimHeader(len(headerID), line)...) - case bytes.HasPrefix(line, headerData): - // The spec allows for multiple data fields per event, concatenated them with "\n". - e.Data = append(e.Data[:], append(trimHeader(len(headerData), line), byte('\n'))...) - // The spec says that a line that simply contains the string "data" should be treated as a data field with an empty body. - case bytes.Equal(line, bytes.TrimSuffix(headerData, []byte(":"))): - e.Data = append(e.Data, byte('\n')) - case bytes.HasPrefix(line, headerEvent): - e.Event = append([]byte(nil), trimHeader(len(headerEvent), line)...) - case bytes.HasPrefix(line, headerRetry): - e.Retry = append([]byte(nil), trimHeader(len(headerRetry), line)...) - default: - // Ignore any garbage that doesn't match what we're looking for. - } - } - - // Trim the last "\n" per the spec. - e.Data = bytes.TrimSuffix(e.Data, []byte("\n")) - - if c.EncodingBase64 { - buf := make([]byte, base64.StdEncoding.DecodedLen(len(e.Data))) - - n, err := base64.StdEncoding.Decode(buf, e.Data) - if err != nil { - err = fmt.Errorf("failed to decode event message: %s", err) - } - e.Data = buf[:n] - } - return &e, err -} - -func (c *Client) cleanup(ch chan *Event) { - c.mu.Lock() - defer c.mu.Unlock() - - if c.subscribed[ch] != nil { - close(c.subscribed[ch]) - delete(c.subscribed, ch) - } -} - -func trimHeader(size int, data []byte) []byte { - if data == nil || len(data) < size { - return data - } - - data = data[size:] - // Remove optional leading whitespace - if len(data) > 0 && data[0] == 32 { - data = data[1:] - } - // Remove trailing new line - if len(data) > 0 && data[len(data)-1] == 10 { - data = data[:len(data)-1] - } - return data -} diff --git a/vendor/github.com/r3labs/sse/v2/event.go b/vendor/github.com/r3labs/sse/v2/event.go deleted file mode 100644 index 1258038786..0000000000 --- a/vendor/github.com/r3labs/sse/v2/event.go +++ /dev/null @@ -1,114 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package sse - -import ( - "bufio" - "bytes" - "context" - "io" - "time" -) - -// Event holds all of the event source fields -type Event struct { - timestamp time.Time - ID []byte - Data []byte - Event []byte - Retry []byte - Comment []byte -} - -func (e *Event) hasContent() bool { - return len(e.ID) > 0 || len(e.Data) > 0 || len(e.Event) > 0 || len(e.Retry) > 0 -} - -// EventStreamReader scans an io.Reader looking for EventStream messages. -type EventStreamReader struct { - scanner *bufio.Scanner -} - -// NewEventStreamReader creates an instance of EventStreamReader. -func NewEventStreamReader(eventStream io.Reader, maxBufferSize int) *EventStreamReader { - scanner := bufio.NewScanner(eventStream) - initBufferSize := minPosInt(4096, maxBufferSize) - scanner.Buffer(make([]byte, initBufferSize), maxBufferSize) - - split := func(data []byte, atEOF bool) (int, []byte, error) { - if atEOF && len(data) == 0 { - return 0, nil, nil - } - - // We have a full event payload to parse. - if i, nlen := containsDoubleNewline(data); i >= 0 { - return i + nlen, data[0:i], nil - } - // If we're at EOF, we have all of the data. - if atEOF { - return len(data), data, nil - } - // Request more data. - return 0, nil, nil - } - // Set the split function for the scanning operation. - scanner.Split(split) - - return &EventStreamReader{ - scanner: scanner, - } -} - -// Returns a tuple containing the index of a double newline, and the number of bytes -// represented by that sequence. If no double newline is present, the first value -// will be negative. -func containsDoubleNewline(data []byte) (int, int) { - // Search for each potentially valid sequence of newline characters - crcr := bytes.Index(data, []byte("\r\r")) - lflf := bytes.Index(data, []byte("\n\n")) - crlflf := bytes.Index(data, []byte("\r\n\n")) - lfcrlf := bytes.Index(data, []byte("\n\r\n")) - crlfcrlf := bytes.Index(data, []byte("\r\n\r\n")) - // Find the earliest position of a double newline combination - minPos := minPosInt(crcr, minPosInt(lflf, minPosInt(crlflf, minPosInt(lfcrlf, crlfcrlf)))) - // Detemine the length of the sequence - nlen := 2 - if minPos == crlfcrlf { - nlen = 4 - } else if minPos == crlflf || minPos == lfcrlf { - nlen = 3 - } - return minPos, nlen -} - -// Returns the minimum non-negative value out of the two values. If both -// are negative, a negative value is returned. -func minPosInt(a, b int) int { - if a < 0 { - return b - } - if b < 0 { - return a - } - if a > b { - return b - } - return a -} - -// ReadEvent scans the EventStream for events. -func (e *EventStreamReader) ReadEvent() ([]byte, error) { - if e.scanner.Scan() { - event := e.scanner.Bytes() - return event, nil - } - if err := e.scanner.Err(); err != nil { - if err == context.Canceled { - return nil, io.EOF - } - return nil, err - } - return nil, io.EOF -} diff --git a/vendor/github.com/r3labs/sse/v2/event_log.go b/vendor/github.com/r3labs/sse/v2/event_log.go deleted file mode 100644 index aa17dad058..0000000000 --- a/vendor/github.com/r3labs/sse/v2/event_log.go +++ /dev/null @@ -1,43 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package sse - -import ( - "strconv" - "time" -) - -// EventLog holds all of previous events -type EventLog []*Event - -// Add event to eventlog -func (e *EventLog) Add(ev *Event) { - if !ev.hasContent() { - return - } - - ev.ID = []byte(e.currentindex()) - ev.timestamp = time.Now() - *e = append(*e, ev) -} - -// Clear events from eventlog -func (e *EventLog) Clear() { - *e = nil -} - -// Replay events to a subscriber -func (e *EventLog) Replay(s *Subscriber) { - for i := 0; i < len(*e); i++ { - id, _ := strconv.Atoi(string((*e)[i].ID)) - if id >= s.eventid { - s.connection <- (*e)[i] - } - } -} - -func (e *EventLog) currentindex() string { - return strconv.Itoa(len(*e)) -} diff --git a/vendor/github.com/r3labs/sse/v2/http.go b/vendor/github.com/r3labs/sse/v2/http.go deleted file mode 100644 index c7a2b434a9..0000000000 --- a/vendor/github.com/r3labs/sse/v2/http.go +++ /dev/null @@ -1,120 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package sse - -import ( - "bytes" - "fmt" - "net/http" - "strconv" - "time" -) - -// ServeHTTP serves new connections with events for a given stream ... -func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { - flusher, err := w.(http.Flusher) - if !err { - http.Error(w, "Streaming unsupported!", http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "text/event-stream") - w.Header().Set("Cache-Control", "no-cache") - w.Header().Set("Connection", "keep-alive") - - for k, v := range s.Headers { - w.Header().Set(k, v) - } - - // Get the StreamID from the URL - streamID := r.URL.Query().Get("stream") - if streamID == "" { - http.Error(w, "Please specify a stream!", http.StatusInternalServerError) - return - } - - stream := s.getStream(streamID) - - if stream == nil { - if !s.AutoStream { - http.Error(w, "Stream not found!", http.StatusInternalServerError) - return - } - - stream = s.CreateStream(streamID) - } - - eventid := 0 - if id := r.Header.Get("Last-Event-ID"); id != "" { - var err error - eventid, err = strconv.Atoi(id) - if err != nil { - http.Error(w, "Last-Event-ID must be a number!", http.StatusBadRequest) - return - } - } - - // Create the stream subscriber - sub := stream.addSubscriber(eventid, r.URL) - - go func() { - <-r.Context().Done() - - sub.close() - - if s.AutoStream && !s.AutoReplay && stream.getSubscriberCount() == 0 { - s.RemoveStream(streamID) - } - }() - - w.WriteHeader(http.StatusOK) - flusher.Flush() - - // Push events to client - for ev := range sub.connection { - // If the data buffer is an empty string abort. - if len(ev.Data) == 0 && len(ev.Comment) == 0 { - break - } - - // if the event has expired, dont send it - if s.EventTTL != 0 && time.Now().After(ev.timestamp.Add(s.EventTTL)) { - continue - } - - if len(ev.Data) > 0 { - fmt.Fprintf(w, "id: %s\n", ev.ID) - - if s.SplitData { - sd := bytes.Split(ev.Data, []byte("\n")) - for i := range sd { - fmt.Fprintf(w, "data: %s\n", sd[i]) - } - } else { - if bytes.HasPrefix(ev.Data, []byte(":")) { - fmt.Fprintf(w, "%s\n", ev.Data) - } else { - fmt.Fprintf(w, "data: %s\n", ev.Data) - } - } - - if len(ev.Event) > 0 { - fmt.Fprintf(w, "event: %s\n", ev.Event) - } - - if len(ev.Retry) > 0 { - fmt.Fprintf(w, "retry: %s\n", ev.Retry) - } - } - - if len(ev.Comment) > 0 { - fmt.Fprintf(w, ": %s\n", ev.Comment) - } - - fmt.Fprint(w, "\n") - - flusher.Flush() - } -} diff --git a/vendor/github.com/r3labs/sse/v2/server.go b/vendor/github.com/r3labs/sse/v2/server.go deleted file mode 100644 index d1b27af325..0000000000 --- a/vendor/github.com/r3labs/sse/v2/server.go +++ /dev/null @@ -1,156 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package sse - -import ( - "encoding/base64" - "sync" - "time" -) - -// DefaultBufferSize size of the queue that holds the streams messages. -const DefaultBufferSize = 1024 - -// Server Is our main struct -type Server struct { - // Extra headers adding to the HTTP response to each client - Headers map[string]string - // Sets a ttl that prevents old events from being transmitted - EventTTL time.Duration - // Specifies the size of the message buffer for each stream - BufferSize int - // Encodes all data as base64 - EncodeBase64 bool - // Splits an events data into multiple data: entries - SplitData bool - // Enables creation of a stream when a client connects - AutoStream bool - // Enables automatic replay for each new subscriber that connects - AutoReplay bool - - // Specifies the function to run when client subscribe or un-subscribe - OnSubscribe func(streamID string, sub *Subscriber) - OnUnsubscribe func(streamID string, sub *Subscriber) - - streams map[string]*Stream - muStreams sync.RWMutex -} - -// New will create a server and setup defaults -func New() *Server { - return &Server{ - BufferSize: DefaultBufferSize, - AutoStream: false, - AutoReplay: true, - streams: make(map[string]*Stream), - Headers: map[string]string{}, - } -} - -// NewWithCallback will create a server and setup defaults with callback function -func NewWithCallback(onSubscribe, onUnsubscribe func(streamID string, sub *Subscriber)) *Server { - return &Server{ - BufferSize: DefaultBufferSize, - AutoStream: false, - AutoReplay: true, - streams: make(map[string]*Stream), - Headers: map[string]string{}, - OnSubscribe: onSubscribe, - OnUnsubscribe: onUnsubscribe, - } -} - -// Close shuts down the server, closes all of the streams and connections -func (s *Server) Close() { - s.muStreams.Lock() - defer s.muStreams.Unlock() - - for id := range s.streams { - s.streams[id].close() - delete(s.streams, id) - } -} - -// CreateStream will create a new stream and register it -func (s *Server) CreateStream(id string) *Stream { - s.muStreams.Lock() - defer s.muStreams.Unlock() - - if s.streams[id] != nil { - return s.streams[id] - } - - str := newStream(id, s.BufferSize, s.AutoReplay, s.AutoStream, s.OnSubscribe, s.OnUnsubscribe) - str.run() - - s.streams[id] = str - - return str -} - -// RemoveStream will remove a stream -func (s *Server) RemoveStream(id string) { - s.muStreams.Lock() - defer s.muStreams.Unlock() - - if s.streams[id] != nil { - s.streams[id].close() - delete(s.streams, id) - } -} - -// StreamExists checks whether a stream by a given id exists -func (s *Server) StreamExists(id string) bool { - return s.getStream(id) != nil -} - -// Publish sends a mesage to every client in a streamID. -// If the stream's buffer is full, it blocks until the message is sent out to -// all subscribers (but not necessarily arrived the clients), or when the -// stream is closed. -func (s *Server) Publish(id string, event *Event) { - stream := s.getStream(id) - if stream == nil { - return - } - - select { - case <-stream.quit: - case stream.event <- s.process(event): - } -} - -// TryPublish is the same as Publish except that when the operation would cause -// the call to be blocked, it simply drops the message and returns false. -// Together with a small BufferSize, it can be useful when publishing the -// latest message ASAP is more important than reliable delivery. -func (s *Server) TryPublish(id string, event *Event) bool { - stream := s.getStream(id) - if stream == nil { - return false - } - - select { - case stream.event <- s.process(event): - return true - default: - return false - } -} - -func (s *Server) getStream(id string) *Stream { - s.muStreams.RLock() - defer s.muStreams.RUnlock() - return s.streams[id] -} - -func (s *Server) process(event *Event) *Event { - if s.EncodeBase64 { - output := make([]byte, base64.StdEncoding.EncodedLen(len(event.Data))) - base64.StdEncoding.Encode(output, event.Data) - event.Data = output - } - return event -} diff --git a/vendor/github.com/r3labs/sse/v2/stream.go b/vendor/github.com/r3labs/sse/v2/stream.go deleted file mode 100644 index bfbcb9b523..0000000000 --- a/vendor/github.com/r3labs/sse/v2/stream.go +++ /dev/null @@ -1,153 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package sse - -import ( - "net/url" - "sync" - "sync/atomic" -) - -// Stream ... -type Stream struct { - ID string - event chan *Event - quit chan struct{} - quitOnce sync.Once - register chan *Subscriber - deregister chan *Subscriber - subscribers []*Subscriber - Eventlog EventLog - subscriberCount int32 - // Enables replaying of eventlog to newly added subscribers - AutoReplay bool - isAutoStream bool - - // Specifies the function to run when client subscribe or un-subscribe - OnSubscribe func(streamID string, sub *Subscriber) - OnUnsubscribe func(streamID string, sub *Subscriber) -} - -// newStream returns a new stream -func newStream(id string, buffSize int, replay, isAutoStream bool, onSubscribe, onUnsubscribe func(string, *Subscriber)) *Stream { - return &Stream{ - ID: id, - AutoReplay: replay, - subscribers: make([]*Subscriber, 0), - isAutoStream: isAutoStream, - register: make(chan *Subscriber), - deregister: make(chan *Subscriber), - event: make(chan *Event, buffSize), - quit: make(chan struct{}), - Eventlog: make(EventLog, 0), - OnSubscribe: onSubscribe, - OnUnsubscribe: onUnsubscribe, - } -} - -func (str *Stream) run() { - go func(str *Stream) { - for { - select { - // Add new subscriber - case subscriber := <-str.register: - str.subscribers = append(str.subscribers, subscriber) - if str.AutoReplay { - str.Eventlog.Replay(subscriber) - } - - // Remove closed subscriber - case subscriber := <-str.deregister: - i := str.getSubIndex(subscriber) - if i != -1 { - str.removeSubscriber(i) - } - - if str.OnUnsubscribe != nil { - go str.OnUnsubscribe(str.ID, subscriber) - } - - // Publish event to subscribers - case event := <-str.event: - if str.AutoReplay { - str.Eventlog.Add(event) - } - for i := range str.subscribers { - str.subscribers[i].connection <- event - } - - // Shutdown if the server closes - case <-str.quit: - // remove connections - str.removeAllSubscribers() - return - } - } - }(str) -} - -func (str *Stream) close() { - str.quitOnce.Do(func() { - close(str.quit) - }) -} - -func (str *Stream) getSubIndex(sub *Subscriber) int { - for i := range str.subscribers { - if str.subscribers[i] == sub { - return i - } - } - return -1 -} - -// addSubscriber will create a new subscriber on a stream -func (str *Stream) addSubscriber(eventid int, url *url.URL) *Subscriber { - atomic.AddInt32(&str.subscriberCount, 1) - sub := &Subscriber{ - eventid: eventid, - quit: str.deregister, - connection: make(chan *Event, 64), - URL: url, - } - - if str.isAutoStream { - sub.removed = make(chan struct{}, 1) - } - - str.register <- sub - - if str.OnSubscribe != nil { - go str.OnSubscribe(str.ID, sub) - } - - return sub -} - -func (str *Stream) removeSubscriber(i int) { - atomic.AddInt32(&str.subscriberCount, -1) - close(str.subscribers[i].connection) - if str.subscribers[i].removed != nil { - str.subscribers[i].removed <- struct{}{} - close(str.subscribers[i].removed) - } - str.subscribers = append(str.subscribers[:i], str.subscribers[i+1:]...) -} - -func (str *Stream) removeAllSubscribers() { - for i := 0; i < len(str.subscribers); i++ { - close(str.subscribers[i].connection) - if str.subscribers[i].removed != nil { - str.subscribers[i].removed <- struct{}{} - close(str.subscribers[i].removed) - } - } - atomic.StoreInt32(&str.subscriberCount, 0) - str.subscribers = str.subscribers[:0] -} - -func (str *Stream) getSubscriberCount() int { - return int(atomic.LoadInt32(&str.subscriberCount)) -} diff --git a/vendor/github.com/r3labs/sse/v2/subscriber.go b/vendor/github.com/r3labs/sse/v2/subscriber.go deleted file mode 100644 index 4b54c204f3..0000000000 --- a/vendor/github.com/r3labs/sse/v2/subscriber.go +++ /dev/null @@ -1,24 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package sse - -import "net/url" - -// Subscriber ... -type Subscriber struct { - quit chan *Subscriber - connection chan *Event - removed chan struct{} - eventid int - URL *url.URL -} - -// Close will let the stream know that the clients connection has terminated -func (s *Subscriber) close() { - s.quit <- s - if s.removed != nil { - <-s.removed - } -} diff --git a/vendor/github.com/tmaxmax/go-sse/.gitignore b/vendor/github.com/tmaxmax/go-sse/.gitignore new file mode 100644 index 0000000000..485dee64bc --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/vendor/github.com/tmaxmax/go-sse/.golangci.yml b/vendor/github.com/tmaxmax/go-sse/.golangci.yml new file mode 100644 index 0000000000..2ce58bbd38 --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/.golangci.yml @@ -0,0 +1,58 @@ +linters: + enable: + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + - typecheck + - dogsled + - dupl + - errorlint + - exhaustive + - nestif + - goconst + - gocritic + - gocyclo + - godot + - godox + - gofmt + - gofumpt + - goheader + - goimports + - gomoddirectives + - gomodguard + - gosec + - importas + - makezero + - misspell + - prealloc + - promlinter + - predeclared + - nolintlint + - revive + - stylecheck + - tagliatelle + - thelper + - unparam + - unused + - whitespace +linters-settings: + gosec: + excludes: + - G404 + gocritic: + disabled-checks: + - ifElseChain + - unnamedResult + - hugeParam + enabled-tags: + - performance + - diagnostic + - experimental + - opinionated + nestif: + min-complexity: 8 + govet: + enable: + - fieldalignment diff --git a/vendor/github.com/tmaxmax/go-sse/CHANGELOG.md b/vendor/github.com/tmaxmax/go-sse/CHANGELOG.md new file mode 100644 index 0000000000..2880426f19 --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/CHANGELOG.md @@ -0,0 +1,323 @@ +# Changelog + +This file tracks changes to this project. It follows the [Keep a Changelog format](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.11.0] - 2025-05-14 + +The `sse.Server` logging and session handling were revamped to have more familiar, more flexible and less error prone interfaces for users. + +### Removed + +- `Logger` and `LogLevel` enum have been removed. `Server.Logger` has transitioned to the standard `slog` library for better compatibility with the ecosystem + +### Changed + +- `Server.Logger` is now of type `func(r *http.Request) *slog.Logger` instead of `sse.Logger` – it is possible to customize the logger on a per-request basis, by for example retrieving it from the context. +- `Server.OnSession` signature changed from `func(s *Session) (Subscription, bool)` to `func(w http.ResponseWriter, r *http.Request) (topics []string, accepted bool)` – its initial role was to essentially just provide the topics, so the need to fiddle with `Session` and `Subscription` was redundant anyway +- `Joe.Subscribe` now always returns `ErrProviderClosed` when a `Joe` instance is closed while subscriptions are active. Previously it would return it only if `Joe` was already shut down before subscribing. +- `Joe` will print a stack trace for `Replayer` panics. + +### Fixed + +- `sse.Session` doesn't write the header explicitly anymore. This would cause a `http: superfluous response.WriteHeader call` warning being logged when `sse.Server.OnSession` writes a response code itself when accepting a session. The change was initially introduced to remove the warning for users of certain external libraries (see #41) but this is the issue of the external library, not of `go-sse`. If you encounter this warning when using an external library, write the response code yourself in the HTTP handler before subscribing the `sse.Session`, as described in the linked discussion. +- An insidious synchronization issue in `Joe` causing a channel double close in an edge case scenario (see #50, see code for details) + +## [0.10.0] - 2024-12-29 + +If you're working with LLMs in Go this update will make you happy! `sse.Read` is now a thing – it just parses all events from an `io.Reader`. Use it with your response bodies and forget about any `sse.Client` configuration. It also makes use of the new Go 1.23 iterators to keep your code neat and tidy. + +### Added + +- `Read` and `ReadConfig` + +## [0.9.0] - 2024-12-26 + +This is the replayer update. Oh, what is a "replayer"? It's how we call replay providers starting with this version! Anyway, besides renaming, this update removes many replaying bugs, improves performance, robustness and error handling and better defines expected behavior for `ReplayProviders`... err, `Replayers`. + +More such overhauls are planned. I'm leaving it up to you to guess which comes next – the server or the client? ;) + +### Removed + +- `FiniteReplayer.{Count, AutoIDs}` – use the constructor instead. +- `ValidReplayer.{TTL, AutoIDs}` – use the constructor instead. + +### Changed + +- The `ReplayProvider` and related entities are renamed to just `Replayer`. `go-sse` strives to have a minimal and expressive API, and minimal and expressive names are an important step in that direction. The changelog will use the new names onwards. +- Due to a change in the internal implementation, the `FiniteReplayer` is now able to replay events only if the event with the LastEventID provided by the client is still buffered. Previously if the LastEventID was that of the latest removed event, events would still be replayed. This detail added complexity to the implementation without an apparent significant win, so it was dropped. +- `FiniteReplayer.GCInterval` should be set to `0` now in order to disable GC. +- Automatic ID generation for both replayers does not overwrite already existing message IDs and errors instead. Ensure that your events do not have IDs when using replayers configured to generate IDs. +- `Replayer.Put` now returns an error instead of being required to panic. Read the method documentation for more info. `Joe` also propagates this error through `Joe.Publish`. +- Replayers are now required to not overwrite message IDs and return errors instead. Sending unsupported messages to replayers is a bug which should not go unnoticed. Both replayers in this library now implement this behavior. +- `Joe` does not log replayer panics to the console anymore. Handle these panics inside the replay provider itself. + +### Added + +- `NewFiniteReplayer` constructor +- `NewValidReplayer` constructor +- `Connection.Buffer` + +### Fixed + +- `FiniteReplayer` doesn't leak memory anymore and respects the stored messages count it was given. Previously when a new message was put after the messages count was reached and some other messages were removed, the total messages count would grow unexpectedly and `FiniteReplayer` would store and replay more events than it was configured to. +- `ValidReplayer` was also susceptible to a similar memory leak, which is also fixed now. +- #41 – `sse.Session` now writes the header explicitly when upgrading. + +## [0.8.0] - 2024-01-30 + +This version removes all external dependencies of `go-sse`. All our bugs are belong to us! It also does some API and documentation cleanups. + +### Removed + +- `Client.DefaultReconnectionTime`, `Client.MaxRetries` have been replaced with the new `Client.Backoff` configuration field. See the Added section for more info. +- `ErrReplayFailed` is removed from the public API. +- `ReplayProviderWithGC` and `Joe.ReplayGCInterval` are no more. The responsibility for garbage collection is assigned to the replay providers. + +### Changed + +- `Server.Logger` is now of a new type: the `Logger` interface. The dependency on x/exp/slog is removed. This opens up the possibility to adapt any existing logger to be usable with `Server`. +- The default backoff behavior has changed. The _previous_ defaults map to the new `Backoff` configuration as follows: +```go +sse.Backoff{ + InitialInterval: 5 * time.Second, // currently 500ms + Multiplier: 1.5, // currently the same + Jitter: 0.5, // currently the same + MaxInterval: 60 * time.Second, // currently unbounded + MaxElapsedDuration: 15 * time.Minute, // currently unbounded + MaxRetries: -1, // previously no retries by default, currently unbounded +} +``` +- `Joe` now accepts new subscriptions even if replay providers panic (previously `ErrReplayFailed` would be returned). +- `Server.ServeHTTP` panics if a custom `OnSession` handler returns a `Subscription` with 0 topics + +### Added + +- The `Logger` interface, `LogLevel` type, and `LogLevel(Info|Warn|Error)` values. +- `Backoff` and `Client.Backoff` – the backoff strategy is now fully configurable. See the code documentation for info. +- `ValidReplayProvider.GCInterval`, to configure at which interval expired events should be cleaned up. + +## [0.7.0] - 2023-11-19 + +This version overhauls connection retry and fixes the connection event dispatch order issue. Some internal changes to Joe were also made, which makes it faster and more resilient. + +### Removed + +- `ConnectionError.Temporary` +- `ConnectionError.Timeout` + +### Changed + +- Go's `Timeout` and `Temporary` interfaces are not used anymore – the client makes no assumptions and retries on every network or response read error. The only cases when `Connection.Connect` returns now are either when there are no more retries left (when the number is not infinite), or when the request context was cancelled. +- `*url.Error`s that occur on the HTTP request are now unwrapped and their cause is put inside a `ConnectionError`. +- `Connection.Connect` doesn't suppress any errors anymore: the request context errors are returned as is, all other errors are wrapped inside `ConnectionError`. +- On reconnection attempt, the response reset error is now wrapped inside `ConnectionError`. With this change, all errors other than the context errors are wrapped inside `ConnectionError`. +- Subscription callbacks are no longer called in individual goroutines. This caused messages to be received in an indeterminate order. Make sure that your callbacks do not block for too long! + +### Changed + +- If a `ReplayProvider` method panics when called by `Joe`, instead of closing itself completely it just stops replaying, putting or GC-ing messages to upcoming clients. `Joe` continues to function as if no replay provider was given. A stack trace is printed to stderr when such a panic occurs. + +## [0.6.0] - 2023-07-22 + +This version brings a number of refactors to the server-side tooling the library offers. Constructors and construction related types are removed, for ease of use and reduced API size, concerns regarding topics and expiry were separated from `Message`, logging of the `Server` is upgraded to structured logging and messages can be now published to multiple topics at once. Request upgrading has also been refactored to provide a more functional API, and the `Server` logic can now be customized without having to create a distinct handler. + +### Removed + +- `Message.ExpiresAt` is no more. +- `Message.Topic` is no more. See the changes to `Server`, `Provider` and `ReplayProvider` for handling topics – you can now publish a message to multiple topics at once. +- `Message.Writer` is no more. The API was redundant – one can achieve the same using `strings.Builder` and `Message.AppendData`. See the `MessageWriter` example for more. +- `NewValidReplayProvider` is no more. +- `NewFiniteReplayProvider` is no more. +- `NewJoe` is no more. +- `JoeConfig` is no more. +- `Server.Subscribe` is no more – it never made sense. +- `Server.Provider` is no more. +- `NewServer`, `ServerOption` and friends are no more. +- The `Logger` interface and the capability of the `Server` to use types that implement `Logger` as logging systems is removed. +- `SubscriptionCallback` is no more (see the change to the `Subscription` type in the "Changed" section). + +### Added + +- Because the `ValidReplayProvider` constructor was removed, the fields `ValidReplayProvider.{TTL,AutoIDs}` were added for configuration. +- Because the `FiniteReplayProvider` constructor was removed, the fields `FiniteReplayProvider.{Count,AutoIDs}` were added for configuration. +- Because the `Joe` constructor was removed, the fields `Joe.{ReplayProvider,ReplayGCInterval}` were added for configuration. +- Because the `Server` constructor was removed, the field `Server.Provider` was added for configuration. +- New `MessageWriter` interface; used by providers to send messages and implemented by `Session` (previously named `Request`). +- New `ResponseWriter` interface, which is a `http.ResponseWriter` augmented with a `Flush` method. +- `ValidReplayProvider` has a new field `Now` which allows providing a custom current time getter, like `time.Now`, to the provider. Enables deterministic testing of dependents on `ValidReplayProvider`. +- New `Server.OnSession` field, which enables customization of `Server`'s response and subscriptions. +- New `Server.Logger` field, which enables structured logging with logger retrieved from the request and customizable config of logged information. + +### Changed + +- `ReplayProvider.Put` takes a simple `*Message` and returns a `*Message`, instead of changing the `*Message` to which the `**Message` parameter points. + It also takes a slice of topics, given that the `Message` doesn't hold the topic itself anymore. If the Message cannot be put, the method must now panic – see documentation for info. +- Because `Message.ExpiresAt` is removed, the `ValidReplayProvider` sets the expiry itself. +- `Server.Publish` now takes a list of topics. +- `Provider.Publish` now takes a non-empty slice of topics. +- `ReplayProvider.Put` now takes a non-empty slice of topics. +- `Provider.Stop` is now `Provider.Shutdown` and takes now a `context.Context` as a parameter. +- `Server.Shutdown` takes now a `context.Context` as a parameter. +- `Request` is now named `Session` and exposes the HTTP request, response writer, and the last event ID of the request. +- A new method `Flush` is added to `Session`; messages are no longer flushed by default, which allows providers, replay providers to batch send messages. +- `Upgrade` now takes an `*http.Request` as its second parameter. +- `Subscription` now has a `Client` field of type `MessageWriter` instead of a `Callback`. +- Given the `Subscription` change, `Provider.Subscribe` and `ReplayProvider.Replay` now report message sending errors. + + +## [0.5.2] - 2023-07-12 + +### Added + +- The new `Message.Writer` – write to the `Message` as if it is an `io.Writer`. + +### Fixed + +- `Message.UnmarshalText` now strips the leading Unicode BOM, if it exists, as per the specification. +- When parsing events client-side, BOM removal was attempted on each event input. Now the BOM is correctly removed only when parsing is started. + +## [0.5.1] - 2023-07-12 + +### Fixed + +- `Message.WriteTo` now writes nothing if `Message` is empty. +- `Message.WriteTo` does not attempt to write the `retry` field if `Message.Retry` is not at least 1ms. +- `NewType` error message is updated to say "event type", not "event name". + +## [0.5.0] - 2023-07-11 + +This version comes with a series of internal refactorings that improve code readability and performance. It also replaces usage of `[]byte` for event data with `string` – SSE is a UTF-8 encoded text-based protocol, so raw bytes never made sense. This migration improves code safety (less `unsafe` usage and less worry about ownership) and reduces the memory footprint of some objects. + +Creating events on the server is also revised – fields that required getters and setters, apart from `data` and comments, are now simple public fields on the `sse.Message` struct. + +Across the codebase, to refer to the value of the `event` field the name "event type" is used, which is the nomenclature used in the SSE specification. + +Documentation and examples were also fixed and improved. + +### Added + +- New `sse.EventName` type, which holds valid values for the `event` field, together with constructors (`sse.Name` and `sse.NewName`). + +### Removed + +- `sse.Message`: `AppendText` was removed, as part of the migration from byte slices to strings. SSE is a UTF-8 encoded text-based protocol – raw bytes never made sense. + +### Changed + +- Minimum supported Go version was bumped from 1.16 to 1.19. From now on, the latest two major Go versions will be supported. +- `sse.Message`: `AppendData` takes `string`s instead of `[]byte`. +- `sse.Message`: `Comment` is now named `AppendComment`, for consistency with `AppendData`. +- `sse.Message`: The message's expiration is not reset anymore by `UnmarshalText`. +- `sse.Message`: `UnmarshalText` now unmarshals comments as well. +- `sse.Message`: `WriteTo` (and `MarshalText` and `String` as a result) replaces all newline sequences in data with LF. +- `sse.Message`: The `Expiry` getter and `SetExpiresAt`, `SetTTL` setters are replaced by the public field `ExpiresAt`. +- `sse.Message`: Event ID getter and setter are replaced by the public `ID` field. +- `sse.Message`: Event type (previously named `Name`) getter and setter are replaced by the public `Type` field. +- `sse.Message`: The `retry` field value is now a public field on the struct. As a byproduct, `WriteTo` will now make 1 allocation when writing events with the `retry` field set. +- `sse.NewEventID` is now `sse.NewID`, and `sse.MustEventID` is `sse.ID`. +- `sse.Event`: The `Data` field is now of type `string`, not `[]byte`. +- `sse.Event`: The `Name` field is now named `Type`. + +### Fixed + +- `sse.Message`: `Clone` now copies the topic of the message to the new value. +- `sse.Message`: ID fields that contain NUL characters are now ignored, as required by the spec, in `UnmarshalText`. + +## [0.4.3] - 2023-07-08 + +### Fixed + +- Messages longer than 4096 bytes are no longer being dropped ([#2], thanks [@aldld]) +- Event parsing no longer panics on empty field with colon after name, see [test case](https://github.com/tmaxmax/go-sse/blob/4938f99db3bf7a8f057cb3e21ca88df57db3c0e0/internal/parser/field_parser_test.go#L37-L45) for example ([#5]) + +## [0.4.2] - 2021-10-17 + +### Added + +- Get the event name of a Message + +## [0.4.1] - 2021-10-15 + +### Added + +- Set a custom logger for Server + +## [0.4.0] - 2021-10-15 + +### Changed + +- Server does not set any other headers besides `Content-Type`. +- UpgradedRequest does not return a SendError anymore when Write errors. +- Providers don't handle callback errors anymore. Callbacks return a flag that indicates whether the provider should keep calling it for new messages instead. + +### Fixed + +- Client's default response validator now ignores `Content-Type` parameters when checking if the response's content type is `text/event-stream`. +- Various optimizations + +## [0.3.0] - 2021-09-18 + +### Added + +- ReplayProviderWithGC interface, which must be satisfied by replay providers that must be cleaned up periodically. + +### Changed + +- Subscriptions now take a callback function instead of a channel. +- Server response headers are now sent on the first Send call, not when Upgrade is called. +- Providers are not required to add the default topic anymore. Callers of Subscribe should ensure at least a topic is specified. +- Providers' Subscribe method now blocks until the subscriber is removed. +- Server's Subscribe method automatically adds the default topic if no topic is specified. +- ReplayProvider does not require for GC to be implemented. +- Client connections take callback functions instead of channels as event listeners. +- Client connections' Unsubscribe methods are replaced by functions returned by their Subscribe counterparts. + +### Fixed + +- Fix replay providers not replaying the oldest message if the ID provided is of the one before that one. +- Fix replay providers hanging the caller's goroutine when a write error occurs using the default ServeHTTP implementation. +- Fix providers hanging when a write error occurs using the default ServeHTTP implementation. + +## [0.2.0] - 2021-09-13 + +### Added + +- Text/JSON marshalers and unmarshalers, and SQL scanners and valuers for the EventID type (previously event.ID). +- Check for http.NoBody before resetting the request body on client reconnect. + +### Changed + +- Package structure. The module is now refactored into a single package with an idiomatic name. This has resulted in various name changes: + - `client.Error` - `sse.ConnectionError` + - `event.Event` - `sse.Message` (previous `server.Message` is removed, see next change) + - `event.ID` - `sse.EventID` + - `event.NewID` - `sse.NewEventID` + - `event.MustID` - `sse.MustEventID` + - `server.Connection` - `sse.UpgradedRequest` + - `server.NewConnection` - `sse.Upgrade` + - `server.ErrUnsupported` - `sse.ErrUpgradeUnsupported` + - `server.New` - `sse.NewServer`. +- `event.Event` is merged with `server.Message`, becoming `sse.Message`. This affects the `sse.Server.Publish` function, which doesn't take a `topic` parameter anymore. +- The server's constructor doesn't take an `Provider` as a parameter. It instead takes multiple optional `ServerOptions`. The `WithProvider` option is now used to pass custom providers to the server. +- The `ReplayProvider` interface's `Put` method now takes a `**Message` instead of a `*Message`. This change also affects the replay providers in this package: `ValidReplayProvider` and `FiniteReplayProvider`. +- The `Provider` interface's `Publish` method now takes a `*Message` instead of a `Message`. This change also affects `Joe`, the provider in this package. +- The `UpgradedRequest`'s `Send` now method takes a `*Message` as parameter. + +## [0.1.0] - 2021-09-11 First release + +[@aldld]: https://github.com/aldld + +[#5]: https://github.com/tmaxmax/go-sse/pull/5 +[#2]: https://github.com/tmaxmax/go-sse/pull/2 + +[0.6.0]: https://github.com/tmaxmax/go-sse/releases/tag/v0.6.0 +[0.5.2]: https://github.com/tmaxmax/go-sse/releases/tag/v0.5.2 +[0.5.1]: https://github.com/tmaxmax/go-sse/releases/tag/v0.5.1 +[0.5.0]: https://github.com/tmaxmax/go-sse/releases/tag/v0.5.0 +[0.4.3]: https://github.com/tmaxmax/go-sse/releases/tag/v0.4.3 +[0.4.2]: https://github.com/tmaxmax/go-sse/releases/tag/v0.4.2 +[0.4.1]: https://github.com/tmaxmax/go-sse/releases/tag/v0.4.1 +[0.4.0]: https://github.com/tmaxmax/go-sse/releases/tag/v0.4.0 +[0.3.0]: https://github.com/tmaxmax/go-sse/releases/tag/v0.3.0 +[0.2.0]: https://github.com/tmaxmax/go-sse/releases/tag/v0.2.0 +[0.1.0]: https://github.com/tmaxmax/go-sse/releases/tag/v0.1.0 diff --git a/vendor/github.com/tmaxmax/go-sse/LICENSE b/vendor/github.com/tmaxmax/go-sse/LICENSE new file mode 100644 index 0000000000..4652fd0b4d --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Teodor Maxim + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/tmaxmax/go-sse/README.md b/vendor/github.com/tmaxmax/go-sse/README.md new file mode 100644 index 0000000000..35f92e5df9 --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/README.md @@ -0,0 +1,485 @@ +# go-sse + +[![Go Reference](https://pkg.go.dev/badge/github.com/tmaxmax/go-sse.svg)](https://pkg.go.dev/github.com/tmaxmax/go-sse) +![CI](https://github.com/tmaxmax/go-sse/actions/workflows/go.yml/badge.svg) +[![codecov](https://codecov.io/gh/tmaxmax/go-sse/branch/master/graph/badge.svg?token=EP52XJI4RO)](https://codecov.io/gh/tmaxmax/go-sse) +[![Go Report Card](https://goreportcard.com/badge/github.com/tmaxmax/go-sse)](https://goreportcard.com/report/github.com/tmaxmax/go-sse) + +Lightweight, fully spec-compliant HTML5 server-sent events library. + +## Table of contents + +- [go-sse](#go-sse) + - [Table of contents](#table-of-contents) + - [Installation and usage](#installation-and-usage) + - [Cut to the chase – how do I read my LLM's response?](#cut-to-the-chase--how-do-i-read-my-llms-response) + - [Implementing a server](#implementing-a-server) + - [Providers and why they are vital](#providers-and-why-they-are-vital) + - [Meet Joe, the default provider](#meet-joe-the-default-provider) + - [Publish your first event](#publish-your-first-event) + - [The server-side "Hello world"](#the-server-side-hello-world) + - [Using the client](#using-the-client) + - [Creating a client](#creating-a-client) + - [Initiating a connection](#initiating-a-connection) + - [Subscribing to events](#subscribing-to-events) + - [Establishing the connection](#establishing-the-connection) + - [Connection lost?](#connection-lost) + - [The "Hello world" server's client](#the-hello-world-servers-client) + - [License](#license) + - [Contributing](#contributing) + +## Installation and usage + +Install the package using `go get`: + +```sh +go get -u github.com/tmaxmax/go-sse +``` + +It is strongly recommended to use tagged versions of `go-sse` in your projects. The `master` branch has tested but unreleased and maybe undocumented changes, which may break backwards compatibility - use with caution. + +The library provides both server-side and client-side implementations of the protocol. The implementations are completely decoupled and unopinionated: you can connect to a server created using `go-sse` from the browser and you can connect to any server that emits events using the client! + +If you are not familiar with the protocol or not sure how it works, read [MDN's guide for using server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events). [The spec](https://html.spec.whatwg.org/multipage/server-sent-events.html) is also useful read! + +`go-sse` promises to support the [Go versions supported by the Go team](https://go.dev/doc/devel/release#policy) – that is, the 2 most recent major releases. + +## Cut to the chase – how do I read my LLM's response? + +If you're here just to read ChatGPT's, Claude's or whichever LLM's response stream, you're in the right place! Let's take a look at [`sse.Read`](https://pkg.go.dev/github.com/tmaxmax/go-sse#Read): you just make your HTTP request the same way you'd do for any other API and call it on the request body. Here's some code: + +```go +req, _ := http.NewRequestWithContext(ctx, http.MethodPost, "https://api.yourllm.com/v1/chat/completions", payload) +req.Header.Set("Content-Type", "application/json") +req.Header.Set("Authorization", "Bearer "+yourKey) + +res, err := http.DefaultClient.Do(req) +if err != nil { + // handle error +} +defer res.Body.Close() // don't forget!! + +for ev, err := range sse.Read(res.Body, nil) { + if err != nil { + // handle read error + break // can end the loop as Read stops on first error anyway + } + // Do something with the events, parse the JSON or whatever. +} +``` + +See the [LLM example](cmd/llm/main.go) for a fully working Go program. + +Go 1.23 iterators (officially ["range-over-func"](https://go.dev/blog/range-functions)) are used for this feature. If you are still on Go 1.22 use the `GOEXPERIMENT=rangefunc` environment variable (e.g. `GOEXPERIMENT=rangefunc go run main.go`) or use the iterator without the syntactic sugar: +```go +events(func(ev Event) bool { + // do something with event + return true // or false to stop iteration +}) +``` + +`sse.Read` is also useful if you're implementing an LLM SDK: call it in your code and spare yourself time and maintenance burden by not reimplementing event stream parsing. + +## Implementing a server + +### Providers and why they are vital + +First, a server instance has to be created: + +```go +import "github.com/tmaxmax/go-sse" + +s := &sse.Server{} // zero value ready to use! +``` + +The `sse.Server` type also implements the `http.Handler` interface, but a server is framework-agnostic: See the [`ServeHTTP` implementation](https://github.com/tmaxmax/go-sse/blob/master/server/server.go#L156) to learn how to implement your own custom logic. It also has some additional configuration options: + +```go +s := &sse.Server{ + Provider: /* what goes here? find out next! */, + OnSession: /* see Go docs for this one */, + Logger: /* see Go docs for this one, too */, +} +``` + +What is this "provider"? A provider is an implementation of the publish-subscribe messaging system: + +```go +type Provider interface { + // Publish a message to all subscribers of the given topics. + Publish(msg *Message, topics []string) error + // Add a new subscriber that is unsubscribed when the context is done. + Subscribe(ctx context.Context, sub Subscription) error + // Cleanup all resources and stop publishing messages or accepting subscriptions. + Shutdown(ctx context.Context) error +} +``` + +The provider is what dispatches events to clients. When you publish a message (an event), the provider distributes it to all connections (subscribers). It is the central piece of the server: it determines the maximum number of clients your server can handle, the latency between broadcasting events and receiving them client-side and the maximum message throughput supported by your server. As different use cases have different needs, `go-sse` allows to plug in your own system. Some examples of such external systems are: + +- [RabbitMQ streams](https://blog.rabbitmq.com/posts/2021/07/rabbitmq-streams-overview/) +- [Redis pub-sub](https://redis.io/topics/pubsub) +- [Apache Kafka](https://kafka.apache.org/) +- Your own! For example, you can mock providers in testing. + +If an external system is required, an adapter that satisfies the `Provider` interface must be created so it can then be used with `go-sse`. To implement such an adapter, read [the Provider documentation][2] for implementation requirements! And maybe share them with others: `go-sse` is built with reusability in mind! + +But in most cases the power and scalability that these external systems bring is not necessary, so `go-sse` comes with a default provider builtin. Read further! + +### Meet Joe, the default provider + +The server still works by default, without a provider. `go-sse` brings you Joe: the trusty, pure Go pub-sub implementation, who handles all your events by default! Befriend Joe as following: + +```go +import "github.com/tmaxmax/go-sse" + +joe := &sse.Joe{} // the zero value is ready to use! +``` + +and he'll dispatch events all day! By default, he has no memory of what events he has received, but you can help him remember and replay older messages to new clients using a `Replayer`: + +```go +type Replayer interface { + // Put a new event in the provider's buffer. + // If the provider automatically adds IDs as well, + // the returned message will also have the ID set, + // otherwise the input value is returned. + Put(msg *Message, topics []string) (*Message, error) + // Replay valid events to a subscriber. + Replay(sub Subscription) error +} +``` + +`go-sse` provides two replayers by default, which both hold the events in-memory: the `ValidReplayer` and `FiniteReplayer`. The first replays events that are valid, not expired, the second replays a finite number of the most recent events. For example: + +```go +// Let's have events expire after 5 minutes. For this example we don't enable automatic ID generation. +r, err := sse.NewValidReplayer(time.Minute * 5, false) +if err != nil { + // TTL was 0 or negative. + // Useful to have this error if the value comes from a config which happens to be faulty. +} + +joe = &sse.Joe{Replayer: r} +``` + +will tell Joe to replay all valid events! Replayers can do so much more (for example, add IDs to events automatically): read the [docs][3] on how to use the existing ones and how to implement yours. + +You can also implement your own replayers: maybe you need persistent storage for your events? Or event validity is determined based on other criteria than expiry time? And if you think your replayer may be useful to others, you are encouraged to share it! + +`go-sse` created the `Replayer` interface mainly for `Joe`, but it encourages you to integrate it with your own `Provider` implementations, where suitable. + +### Publish your first event + +To publish events from the server, we use the `sse.Message` struct: + +```go +import "github.com/tmaxmax/go-sse" + +m := &sse.Message{} +m.AppendData("Hello world!", "Nice\nto see you.") +``` + +Now let's send it to our clients: + +```go +var s *sse.Server + +s.Publish(m) +``` + +This is how clients will receive our event: + +```txt +data: Hello world! +data: Nice +data: to see you. +``` + +You can also see that `go-sse` takes care of splitting input by lines into new fields, as required by the specification. + +Keep in mind that replayers, such as the `ValidReplayer` used above, will give an error for and won't replay the events without an ID (unless, of course, they give the IDs themselves). To have our event expire, as configured, we must set an ID for the event: + +```go +m.ID = sse.ID("unique") +``` + +This is how the event will look: + +```txt +id: unique +data: Hello world! +data: Nice +data: to see you. +``` + +Now that it has an ID, the event will be considered expired 5 minutes after it's been published – it won't be replayed to clients after it expires! + +`sse.ID` is a function that returns an `EventID` – a special type that denotes an event's ID. An ID must not have newlines, so we must use special functions which validate the value beforehand. The `ID` constructor function we've used above panics (it is useful when creating IDs from static strings), but there's also `NewID`, which returns an error indicating whether the value was successfully converted to an ID or not: + +```go +id, err := sse.NewID("invalid\nID") +``` + +Here, `err` will be non-nil and `id` will be an unset value: no `id` field will be sent to clients if you set an event's ID using that value! + +Setting the event's type (the `event` field) is equally easy: + +```go +m.Type = sse.Type("The event's name") +``` + +Like IDs, types cannot have newlines. You are provided with constructors that follow the same convention: `Type` panics, `NewType` returns an error. Read the [docs][4] to find out more about messages and how to use them! + +### The server-side "Hello world" + +Now, let's put everything that we've learned together! We'll create a server that sends a "Hello world!" message every second to all its clients, with Joe's help: + +```go +package main + +import ( + "log" + "net/http" + "time" + + "github.com/tmaxmax/go-sse" +) + +func main() { + s := &sse.Server{} + + go func() { + m := &sse.Message{} + m.AppendData("Hello world") + + for range time.Tick(time.Second) { + _ = s.Publish(m) + } + }() + + if err := http.ListenAndServe(":8000", s); err != nil { + log.Fatalln(err) + } +} +``` + +Joe is our default provider here, as no provider is given to the server constructor. The server is already an `http.Handler` so we can use it directly with `http.ListenAndServe`. + +[Also see a more complex example!](cmd/complex/main.go) + +This is by far a complete presentation, make sure to read the docs in order to use `go-sse` to its full potential! + +## Using the client + +### Creating a client + +We will use the `sse.Client` type for connecting to event streams: + +```go +type Client struct { + HTTPClient *http.Client + OnRetry backoff.Notify + ResponseValidator ResponseValidator + MaxRetries int + DefaultReconnectionTime time.Duration +} +``` + +As you can see, it uses a `net/http` client. It also uses the [cenkalti/backoff][1] library for implementing auto-reconnect when a connection to a server is lost. Read the [client docs][5] and the Backoff library's docs to find out how to configure the client. We'll use the default client the package provides for further examples. + +### Initiating a connection + +We must first create an `http.Request` - yup, a fully customizable request: + +```go +req, err := http.NewRequestWithContext(ctx, http.MethodGet, "host", http.NoBody) +``` + +Any kind of request is valid as long as your server handler supports it: you can do a GET, a POST, send a body; do whatever! The context is used as always for cancellation - to stop receiving events you will have to cancel the context. +Let's initiate a connection with this request: + +```go +import "github.com/tmaxmax/go-sse" + +conn := sse.DefaultClient.NewConnection(req) +// you can also do sse.NewConnection(req) +// it is an utility function that calls the +// NewConnection method on the default client +``` + +### Subscribing to events + +Great! Let's imagine the event stream looks as following: + +```txt +data: some unnamed event + +event: I have a name +data: some data + +event: Another name +data: some data +``` + +To receive the unnamed events, we subscribe to them as following: + +```go +unsubscribe := conn.SubscribeMessages(func (event sse.Event) { + // do something with the event +}) +``` + +To receive the events named "I have a name": + +```go +unsubscribe := conn.SubscribeEvent("I have a name", func (event sse.Event) { + // do something with the event +}) +``` + +If you want to subscribe to all events, regardless of their name: + +```go +unsubscribe := conn.SubscribeToAll(func (event sse.Event) { + // do something with the event +}) +``` + +All `Subscribe` methods return a function that when called tells the connection to stop calling the corresponding callback. + +In order to work with events, the `sse.Event` type has some fields and methods exposed: + +```go +type Event struct { + LastEventID string + Name string + Data string +} +``` + +Pretty self-explanatory, but make sure to read the [docs][6]! + +Now, with this knowledge, let's subscribe to all unnamed events and, when the connection is established, print their data: + +```go +unsubscribe := conn.SubscribeMessages(func(event sse.Event) { + fmt.Printf("Received an unnamed event: %s\n", event.Data) +}) +``` + +### Establishing the connection + +Great, we are subscribed now! Let's start receiving events: + +```go +err := conn.Connect() +``` + +By calling `Connect`, the request created above will be sent to the server, and if successful, the subscribed callbacks will be called when new events are received. `Connect` returns only after all callbacks have finished executing. +To stop calling a certain callback, call the unsubscribe function returned when subscribing. You can also subscribe new callbacks after calling Connect from a different goroutine. +When using a `context.Context` to stop the connection, the error returned will be the context error – be it `context.Canceled`, `context.DeadlineExceeded` or a custom cause (when using `context.WithCancelCause`). In other words, a successfully closed `Connection` will always return an error – if the context error is not relevant, you can ignore it. For example: + +```go +if err := conn.Connect(); !errors.Is(err, context.Canceled) { + // handle error +} +``` + +A context created with `context.WithCancel`, or one with `context.WithCancelCause` and cancelled with the error `context.Canceled` is assumed above. + +There may be situations where the connection does not have to live for indeterminately long – for example when using the OpenAI API. In those situations, configure the client to not retry the connection and ignore `io.EOF` on return: + +```go +client := sse.Client{ + Backoff: sse.Backoff{ + MaxRetries: -1, + }, + // other settings... +} + +req, _ := http.NewRequest(http.MethodPost, "https://api.openai.com/...", body) +conn := client.NewConnection(req) + +conn.SubscribeMessages(/* callback */) + +if err := conn.Connect(); !errors.Is(err, io.EOF) { + // handle error +} +``` + +### Connection lost? + +Either way, after receiving so many events, something went wrong and the server is temporarily down. Oh no! As a last hope, it has sent us the following event: + +```text +retry: 60000 +: that's a minute in milliseconds and this +: is a comment which is ignored by the client +``` + +Not a sweat, though! The connection will automatically be reattempted after a minute, when we'll hope the server's back up again. Canceling the request's context will cancel any reconnection attempt, too. + +If the server doesn't set a retry time, the client's `DefaultReconnectionTime` is used. + +### The "Hello world" server's client + +Let's use what we know to create a client for the previous server example: + +```go +package main + +import ( + "fmt" + "net/http" + "os" + + "github.com/tmaxmax/go-sse" +) + +func main() { + r, _ := http.NewRequest(http.MethodGet, "http://localhost:8000", nil) + conn := sse.NewConnection(r) + + conn.SubscribeMessages(func(ev sse.Event) { + fmt.Printf("%s\n\n", ev.Data) + }) + + if err := conn.Connect(); err != nil { + fmt.Fprintln(os.Stderr, err) + } +} +``` + +Yup, this is it! We are using the default client to receive all the unnamed events from the server. The output will look like this, when both programs are run in parallel: + +```txt +Hello world! + +Hello world! + +Hello world! + +Hello world! + +... +``` + +[See the complex example's client too!](cmd/complex_client/main.go) + +## License + +This project is licensed under the [MIT license](LICENSE). + +## Contributing + +The library's in its early stages, so contributions are vital - I'm so glad you wish to improve `go-sse`! Maybe start by opening an issue first, to describe the intended modifications and further discuss how to integrate them. Open PRs to the `master` branch and wait for CI to complete. If all is clear, your changes will soon be merged! Also, make sure your changes come with an extensive set of tests and the code is formatted. + +Thank you for contributing! + +[1]: https://github.com/cenkalti/backoff +[2]: https://pkg.go.dev/github.com/tmaxmax/go-sse#Provider +[3]: https://pkg.go.dev/github.com/tmaxmax/go-sse#Replayer +[4]: https://pkg.go.dev/github.com/tmaxmax/go-sse#Message +[5]: https://pkg.go.dev/github.com/tmaxmax/go-sse#Client +[6]: https://pkg.go.dev/github.com/tmaxmax/go-sse#Event diff --git a/vendor/github.com/tmaxmax/go-sse/client.go b/vendor/github.com/tmaxmax/go-sse/client.go new file mode 100644 index 0000000000..a0a576be22 --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/client.go @@ -0,0 +1,229 @@ +package sse + +import ( + "fmt" + "math/rand" + "net/http" + "strings" + "time" + "unicode" +) + +// The ResponseValidator type defines the type of the function +// that checks whether server responses are valid, before starting +// to read events from them. See the Client's documentation for more info. +// +// These errors are considered permanent and thus if the client is configured +// to retry on error no retry is attempted and the error is returned. +type ResponseValidator func(*http.Response) error + +// The Client struct is used to initialize new connections to different servers. +// It is safe for concurrent use. +// +// After connections are created, the Connect method must be called to start +// receiving events. +type Client struct { + // The HTTP client to be used. Defaults to http.DefaultClient. + HTTPClient *http.Client + // A callback that's executed whenever a reconnection attempt starts. + // It receives the error that caused the retry and the reconnection time. + OnRetry func(error, time.Duration) + // A function to check if the response from the server is valid. + // Defaults to a function that checks the response's status code is 200 + // and the content type is text/event-stream. + // + // If the error type returned has a Temporary or a Timeout method, + // they will be used to determine whether to reattempt the connection. + // Otherwise, the error will be considered permanent and no reconnections + // will be attempted. + ResponseValidator ResponseValidator + // Backoff configures the backoff strategy. See the documentation of + // each field for more information. + Backoff Backoff +} + +// Backoff configures the reconnection strategy of a Connection. +type Backoff struct { + // The initial wait time before a reconnection is attempted. + // Must be >0. Defaults to 500ms. + InitialInterval time.Duration + // How much should the reconnection time grow on subsequent attempts. + // Must be >=1; 1 = constant interval. Defaults to 1.5. + Multiplier float64 + // How much does the reconnection time vary relative to the base value. + // This is useful to prevent multiple clients to reconnect at the exact + // same time, as it makes the wait times distinct. + // Must be in range (0, 1); -1 = no randomization. Defaults to 0.5. + Jitter float64 + // How much can the wait time grow. + // If <=0 = the wait time can infinitely grow. Defaults to infinite growth. + MaxInterval time.Duration + // How much time can retries be attempted. + // For example, if this is 5 seconds, after 5 seconds the client + // will stop retrying. + // If <=0 = no limit. Defaults to no limit. + MaxElapsedTime time.Duration + // How many retries are allowed. + // <0 = no retries, 0 = infinite. Defaults to infinite retries. + MaxRetries int +} + +// NewConnection initializes and configures a connection. On connect, the given +// request is sent and if successful the connection starts receiving messages. +// Use the request's context to stop the connection. +// +// If the request has a body, it is necessary to provide a GetBody function in order +// for the connection to be reattempted, in case of an error. Using readers +// such as bytes.Reader, strings.Reader or bytes.Buffer when creating a request +// using http.NewRequestWithContext will ensure this function is present on the request. +func (c *Client) NewConnection(r *http.Request) *Connection { + if r == nil { + panic("go-sse.client.NewConnection: request cannot be nil") + } + + mergeDefaults(c) + + conn := &Connection{ + client: *c, // we clone the client so the config cannot be modified from outside + request: r.Clone(r.Context()), // we clone the request so its fields cannot be modified from outside + callbacks: map[string]map[int]EventCallback{}, + callbacksAll: map[int]EventCallback{}, + } + + return conn +} + +// DefaultValidator is the default client response validation function. As per the spec, +// It checks the content type to be text/event-stream and the response status code to be 200 OK. +// +// If this validator fails, errors are considered permanent. No retry attempts are made. +// +// See https://html.spec.whatwg.org/multipage/server-sent-events.html#sse-processing-model. +var DefaultValidator ResponseValidator = func(r *http.Response) error { + if r.StatusCode != http.StatusOK { + return fmt.Errorf("expected status code %d %s, received %d %s", http.StatusOK, http.StatusText(http.StatusOK), r.StatusCode, http.StatusText(r.StatusCode)) + } + cts := r.Header.Get("Content-Type") + ct := contentType(cts) + if expected := "text/event-stream"; ct != expected { + return fmt.Errorf("expected content type to have %q, received %q", expected, cts) + } + return nil +} + +// NoopValidator is a client response validator function that treats all responses as valid. +var NoopValidator ResponseValidator = func(_ *http.Response) error { + return nil +} + +// DefaultClient is the client that is used when creating a new connection using the NewConnection function. +// Unset properties on new clients are replaced with the ones set for the default client. +var DefaultClient = &Client{ + HTTPClient: http.DefaultClient, + ResponseValidator: DefaultValidator, + Backoff: Backoff{ + InitialInterval: time.Millisecond * 500, + Multiplier: 1.5, + Jitter: 0.5, + }, +} + +// NewConnection creates a connection using the default client. +func NewConnection(r *http.Request) *Connection { + return DefaultClient.NewConnection(r) +} + +func mergeDefaults(c *Client) { + if c.HTTPClient == nil { + c.HTTPClient = DefaultClient.HTTPClient + } + if c.Backoff.InitialInterval <= 0 { + c.Backoff.InitialInterval = DefaultClient.Backoff.InitialInterval + } + if c.Backoff.Multiplier < 1 { + c.Backoff.Multiplier = DefaultClient.Backoff.Multiplier + } + if c.Backoff.Jitter <= 0 || c.Backoff.Jitter >= 1 { + c.Backoff.Jitter = DefaultClient.Backoff.Jitter + } + if c.ResponseValidator == nil { + c.ResponseValidator = DefaultClient.ResponseValidator + } +} + +func contentType(header string) string { + cts := strings.FieldsFunc(header, func(r rune) bool { + return unicode.IsSpace(r) || r == ';' || r == ',' + }) + if len(cts) == 0 { + return "" + } + return strings.ToLower(cts[0]) +} + +type backoffController struct { + start time.Time + rng *rand.Rand + b *Backoff + interval time.Duration + numRetries int +} + +func (b *Backoff) new() backoffController { + now := time.Now() + return backoffController{ + start: now, + rng: rand.New(rand.NewSource(now.UnixNano())), + b: b, + interval: b.InitialInterval, + numRetries: 0, + } +} + +// reset the backoff to the initial state, i.e. as if no retries have occurred. +// If newInterval is greater than 0, the initial interval is changed to it. +func (c *backoffController) reset(newInterval time.Duration) { + if newInterval > 0 { + c.interval = newInterval + } else { + c.interval = c.b.InitialInterval + } + c.numRetries = 0 + c.start = time.Now() +} + +func (c *backoffController) next() (interval time.Duration, shouldRetry bool) { + if c.b.MaxRetries < 0 || (c.b.MaxRetries > 0 && c.numRetries == c.b.MaxRetries) { + return 0, false + } + + c.numRetries++ + elapsed := time.Since(c.start) + next := nextInterval(c.b.Jitter, c.rng, c.interval) + c.interval = growInterval(c.interval, c.b.MaxInterval, c.b.Multiplier) + + if c.b.MaxElapsedTime > 0 && elapsed+next > c.b.MaxElapsedTime { + return 0, false + } + + return next, true +} + +func nextInterval(jitter float64, rng *rand.Rand, current time.Duration) time.Duration { + if jitter == -1 { + return current + } + + delta := jitter * float64(current) + minInterval := float64(current) - delta + maxInterval := float64(current) + delta + + return time.Duration(minInterval + (rng.Float64() * (maxInterval - minInterval + 1))) +} + +func growInterval(current, maxInterval time.Duration, mul float64) time.Duration { + if maxInterval > 0 && float64(current) >= float64(maxInterval)/mul { + return maxInterval + } + return time.Duration(float64(current) * mul) +} diff --git a/vendor/github.com/tmaxmax/go-sse/client_connection.go b/vendor/github.com/tmaxmax/go-sse/client_connection.go new file mode 100644 index 0000000000..a901952509 --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/client_connection.go @@ -0,0 +1,277 @@ +package sse + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "sync" + "time" + + "github.com/tmaxmax/go-sse/internal/parser" +) + +// EventCallback is a function that is used to receive events from a Connection. +type EventCallback func(Event) + +// EventCallbackRemover is a function that removes an already registered callback +// from a connection. Calling it multiple times is a no-op. +type EventCallbackRemover func() + +// Connection is a connection to an events stream. Created using the Client struct, +// a Connection processes the incoming events and calls the subscribed event callbacks. +// If the connection to the server temporarily fails, the connection will be reattempted. +// Retry values received from servers will be taken into account. +// +// Connections must not be copied after they are created. +type Connection struct { //nolint:govet // The current order aids readability. + mu sync.RWMutex + request *http.Request + callbacks map[string]map[int]EventCallback + callbacksAll map[int]EventCallback + lastEventID string + client Client + buf []byte + bufMaxSize int + callbackID int + isRetry bool +} + +// SubscribeMessages subscribes the given callback to all events without type (without or with empty `event` field). +// Remove the callback by calling the returned function. +func (c *Connection) SubscribeMessages(cb EventCallback) EventCallbackRemover { + return c.SubscribeEvent("", cb) +} + +// SubscribeEvent subscribes the given callback to all the events with the provided type +// (the `event` field has the value given here). +// Remove the callback by calling the returned function. +func (c *Connection) SubscribeEvent(typ string, cb EventCallback) EventCallbackRemover { + return c.addSubscriber(typ, cb) +} + +// SubscribeToAll subscribes the given callback to all events, with or without type. +// Remove the callback by calling the returned function. +func (c *Connection) SubscribeToAll(cb EventCallback) EventCallbackRemover { + return c.addSubscriberToAll(cb) +} + +func (c *Connection) addSubscriberToAll(cb EventCallback) EventCallbackRemover { + c.mu.Lock() + defer c.mu.Unlock() + + id := c.callbackID + c.callbacksAll[id] = cb + c.callbackID++ + + return func() { + c.mu.Lock() + defer c.mu.Unlock() + + delete(c.callbacksAll, id) + } +} + +func (c *Connection) addSubscriber(event string, cb EventCallback) EventCallbackRemover { + c.mu.Lock() + defer c.mu.Unlock() + + if _, ok := c.callbacks[event]; !ok { + c.callbacks[event] = map[int]EventCallback{} + } + + id := c.callbackID + c.callbacks[event][id] = cb + c.callbackID++ + + return func() { + c.mu.Lock() + defer c.mu.Unlock() + + delete(c.callbacks[event], id) + if len(c.callbacks[event]) == 0 { + delete(c.callbacks, event) + } + } +} + +// Buffer sets the underlying buffer to be used when scanning events. +// Use this if you need to read very large events (bigger than the default +// of 65K bytes). +// +// Read the documentation of bufio.Scanner.Buffer for more information. +func (c *Connection) Buffer(buf []byte, maxSize int) { + c.buf = buf + c.bufMaxSize = maxSize +} + +// ConnectionError is the type that wraps all the connection errors that occur. +type ConnectionError struct { + // The request for which the connection failed. + Req *http.Request + // The reason the operation failed. + Err error + // The reason why the request failed. + Reason string +} + +func (e *ConnectionError) Error() string { + return fmt.Sprintf("request failed: %s: %v", e.Reason, e.Err) +} + +func (e *ConnectionError) Unwrap() error { + return e.Err +} + +func (c *Connection) resetRequest() error { + if !c.isRetry { + c.isRetry = true + return nil + } + if err := resetRequestBody(c.request); err != nil { + return err + } + if c.lastEventID == "" { + c.request.Header.Del("Last-Event-ID") + } else { + c.request.Header.Set("Last-Event-ID", c.lastEventID) + } + return nil +} + +func (c *Connection) dispatch(ev Event) { + c.mu.RLock() + defer c.mu.RUnlock() + + cbs := c.callbacks[ev.Type] + cbCount := len(cbs) + len(c.callbacksAll) + if cbCount == 0 { + return + } + + for _, cb := range c.callbacks[ev.Type] { + cb(ev) + } + for _, cb := range c.callbacksAll { + cb(ev) + } +} + +func (c *Connection) read(r io.Reader, setRetry func(time.Duration)) error { + pf := func() *parser.Parser { + p := parser.New(r) + if c.buf != nil || c.bufMaxSize > 0 { + p.Buffer(c.buf, c.bufMaxSize) + } + return p + } + + var readErr error + read(pf, c.lastEventID, func(r int64) { setRetry(time.Duration(r) * time.Millisecond) }, false)(func(e Event, err error) bool { + if err != nil { + readErr = err + return false + } + c.lastEventID = e.LastEventID + c.dispatch(e) + return true + }) + + return readErr +} + +// Connect sends the request the connection was created with to the server +// and, if successful, it starts receiving events. The caller goroutine +// is blocked until the request's context is done or an error occurs. +// +// If the request's context is cancelled, Connect returns its error. +// Otherwise, if the maximum number of retries is made, the last error +// that occurred is returned. Connect never returns otherwise – either +// the context is cancelled, or it's done retrying. +// +// All errors returned other than the context errors will be wrapped +// inside a *ConnectionError. +func (c *Connection) Connect() error { + ctx := c.request.Context() + backoff := c.client.Backoff.new() + + c.request.Header.Set("Accept", "text/event-stream") + c.request.Header.Set("Connection", "keep-alive") + c.request.Header.Set("Cache", "no-cache") + + t := time.NewTimer(0) + defer t.Stop() + + for { + select { + case <-t.C: + shouldRetry, err := c.doConnect(ctx, backoff.reset) + if !shouldRetry { + return err + } + + next, shouldRetry := backoff.next() + if !shouldRetry { + return err + } + + if c.client.OnRetry != nil { + c.client.OnRetry(err, next) + } + + t.Reset(next) + case <-ctx.Done(): + return ctx.Err() + } + } +} + +func (c *Connection) doConnect(ctx context.Context, setRetry func(time.Duration)) (shouldRetry bool, err error) { + if err := c.resetRequest(); err != nil { + return false, &ConnectionError{Req: c.request, Reason: "request reset failed", Err: err} + } + + res, err := c.client.HTTPClient.Do(c.request) + if err != nil { + concrete := err.(*url.Error) //nolint:errorlint // We know the concrete type here + if errors.Is(err, ctx.Err()) { + return false, concrete.Err + } + return true, &ConnectionError{Req: c.request, Reason: "connection to server failed", Err: concrete.Err} + } + defer res.Body.Close() + + if err := c.client.ResponseValidator(res); err != nil { + return false, &ConnectionError{Req: c.request, Reason: "response validation failed", Err: err} + } + + setRetry(0) + + err = c.read(res.Body, setRetry) + if errors.Is(err, ctx.Err()) { + return false, err + } + + return true, &ConnectionError{Req: c.request, Reason: "connection to server lost", Err: err} +} + +// ErrNoGetBody is a sentinel error returned when the connection cannot be reattempted +// due to GetBody not existing on the original request. +var ErrNoGetBody = errors.New("the GetBody function doesn't exist on the request") + +func resetRequestBody(r *http.Request) error { + if r.Body == nil || r.Body == http.NoBody { + return nil + } + if r.GetBody == nil { + return ErrNoGetBody + } + body, err := r.GetBody() + if err != nil { + return err + } + r.Body = body + return nil +} diff --git a/vendor/github.com/tmaxmax/go-sse/codecov.yml b/vendor/github.com/tmaxmax/go-sse/codecov.yml new file mode 100644 index 0000000000..fd35a5fac2 --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/codecov.yml @@ -0,0 +1,3 @@ +ignore: + - cmd + - internal/tests \ No newline at end of file diff --git a/vendor/github.com/tmaxmax/go-sse/event.go b/vendor/github.com/tmaxmax/go-sse/event.go new file mode 100644 index 0000000000..989bb9dd8b --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/event.go @@ -0,0 +1,133 @@ +package sse + +import ( + "io" + "strconv" + "strings" + + "github.com/tmaxmax/go-sse/internal/parser" +) + +// The Event struct represents an event sent to the client by a server. +type Event struct { + // The last non-empty ID of all the events received. This may not be + // the ID of the latest event! + LastEventID string + // The event's type. It is empty if the event is unnamed. + Type string + // The event's payload. + Data string +} + +// ReadConfig is used to configure how Read behaves. +type ReadConfig struct { + // MaxEventSize is the maximum expected length of the byte sequence + // representing a single event. Parsing events longer than that + // will result in an error. + // + // By default this limit is 64KB. You don't need to set this if it + // is enough for your needs (e.g. the events you receive don't contain + // larger amounts of data). + MaxEventSize int +} + +// Read parses an SSE stream and yields all incoming events, +// On any encountered errors iteration stops and no further events are parsed – +// the loop can safely be ended on error. If EOF is reached, the Read operation +// is considered successful and no error is returned. An Event will never +// be yielded together with an error. +// +// Read is especially useful for parsing responses from services which +// communicate using SSE but not over long-lived connections – for example, +// LLM APIs. +// +// Read handles the Event.LastEventID value just as the browser SSE client +// (EventSource) would – for every event, the last encountered event ID will be given, +// even if the ID is not the current event's ID. Read, unlike EventSource, does +// not set Event.Type to "message" if no "event" field is received, leaving +// it blank. +// +// Read provides no way to handle the "retry" field and doesn't handle retrying. +// Use a Client and a Connection if you need to retry requests. +func Read(r io.Reader, cfg *ReadConfig) func(func(Event, error) bool) { + pf := func() *parser.Parser { + p := parser.New(r) + if cfg != nil && cfg.MaxEventSize > 0 { + // NOTE(tmaxmax): we don't allow setting the buffer at the moment. + // ReadConfig objects might be shared between Read calls executed in + // different goroutines and having an actual []byte in it seems dangerous. + // If there is demand it can be added. + p.Buffer(nil, cfg.MaxEventSize) + } + return p + } + + // We take a factory function for the parser so that Read can be inlined by the compiler. + return read(pf, "", nil, true) +} + +func read(pf func() *parser.Parser, lastEventID string, onRetry func(int64), ignoreEOF bool) func(func(Event, error) bool) { + return func(yield func(Event, error) bool) { + p := pf() + + typ, sb, dirty := "", strings.Builder{}, false + doYield := func(data string) bool { + if data != "" { + data = data[:len(data)-1] + } + return yield(Event{LastEventID: lastEventID, Data: data, Type: typ}, nil) + } + + for f := (parser.Field{}); p.Next(&f); { + switch f.Name { //nolint:exhaustive // Comment fields are not parsed. + case parser.FieldNameData: + sb.WriteString(f.Value) + sb.WriteByte('\n') + dirty = true + case parser.FieldNameEvent: + typ = f.Value + dirty = true + case parser.FieldNameID: + // empty IDs are valid, only IDs that contain the null byte must be ignored: + // https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation + if strings.IndexByte(f.Value, 0) != -1 { + break + } + + lastEventID = f.Value + dirty = true + case parser.FieldNameRetry: + n, err := strconv.ParseInt(f.Value, 10, 64) + if err != nil { + break + } + if n >= 0 && onRetry != nil { + onRetry(n) + dirty = true + } + default: + if dirty { + if !doYield(sb.String()) { + return + } + sb.Reset() + typ = "" + dirty = false + } + } + } + + err := p.Err() + isEOF := err == io.EOF //nolint:errorlint // Our scanner returns io.EOF unwrapped + + if dirty && isEOF { + if !doYield(sb.String()) { + return + } + } + + if err != nil && !(ignoreEOF && isEOF) { + yield(Event{}, err) + } + } +} diff --git a/vendor/github.com/tmaxmax/go-sse/internal/parser/chunk.go b/vendor/github.com/tmaxmax/go-sse/internal/parser/chunk.go new file mode 100644 index 0000000000..449b715bee --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/internal/parser/chunk.go @@ -0,0 +1,41 @@ +package parser + +// isNewlineChar returns whether the given character is '\n' or '\r'. +func isNewlineChar(b byte) bool { + return b == '\n' || b == '\r' +} + +// NewlineIndex returns the index of the first occurrence of a newline sequence (\n, \r, or \r\n). +// It also returns the sequence's length. If no sequence is found, index is equal to len(s) +// and length is 0. +// +// The newline is defined in the Event Stream standard's documentation: +// https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events +func NewlineIndex(s string) (index, length int) { + for l := len(s); index < l; index++ { + b := s[index] + + if isNewlineChar(b) { + length++ + if b == '\r' && index < l-1 && s[index+1] == '\n' { + length++ + } + + break + } + } + + return +} + +// NextChunk retrieves the next chunk of data from the given string +// along with the data remaining after the returned chunk. +// A chunk is a string of data delimited by a newline. +// If the returned chunk is the last one, len(remaining) will be 0. +// +// The newline is defined in the Event Stream standard's documentation: +// https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events +func NextChunk(s string) (chunk, remaining string, hasNewline bool) { + index, endlineLen := NewlineIndex(s) + return s[:index], s[index+endlineLen:], endlineLen != 0 +} diff --git a/vendor/github.com/tmaxmax/go-sse/internal/parser/field.go b/vendor/github.com/tmaxmax/go-sse/internal/parser/field.go new file mode 100644 index 0000000000..0262b16a2d --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/internal/parser/field.go @@ -0,0 +1,44 @@ +package parser + +// FieldName is the name of the field. +type FieldName string + +// A Field represents an unprocessed field of a single event. The Name is the field's identifier, which is used to +// process the fields afterwards. +// +// As a special case, if a parser (FieldParser or Parser) returns a field without a name, +// it means that a whole event was parsed. In other words, all the fields before the one without a name +// and after another such field are part of the same event. +type Field struct { + Name FieldName + Value string +} + +// Valid field names. +const ( + FieldNameData = FieldName("data") + FieldNameEvent = FieldName("event") + FieldNameRetry = FieldName("retry") + FieldNameID = FieldName("id") + // FieldNameComment is a sentinel value that indicates + // comment fields. It is not a valid field name that should + // be written to an SSE stream. + FieldNameComment = FieldName(":") + + maxFieldNameLength = 5 +) + +func getFieldName(b string) (FieldName, bool) { + switch FieldName(b) { //nolint:exhaustive // Cannot have Comment here + case FieldNameData: + return FieldNameData, true + case FieldNameEvent: + return FieldNameEvent, true + case FieldNameRetry: + return FieldNameRetry, true + case FieldNameID: + return FieldNameID, true + default: + return "", false + } +} diff --git a/vendor/github.com/tmaxmax/go-sse/internal/parser/field_parser.go b/vendor/github.com/tmaxmax/go-sse/internal/parser/field_parser.go new file mode 100644 index 0000000000..431cf2f4e0 --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/internal/parser/field_parser.go @@ -0,0 +1,129 @@ +package parser + +import ( + "errors" + "strings" +) + +// FieldParser extracts fields from a byte slice. +type FieldParser struct { + err error + data string + + started bool + + keepComments bool + removeBOM bool +} + +func trimFirstSpace(c string) string { + if c != "" && c[0] == ' ' { + return c[1:] + } + return c +} + +func (f *FieldParser) scanSegment(chunk string, out *Field) bool { + colonPos, l := strings.IndexByte(chunk, ':'), len(chunk) + if colonPos > maxFieldNameLength { + return false + } + if colonPos == -1 { + colonPos = l + } + + name, ok := getFieldName(chunk[:colonPos]) + if ok { + out.Name = name + out.Value = trimFirstSpace(chunk[min(colonPos+1, l):]) + return true + } else if chunk == "" { + // scanSegment is called only with chunks which end with a newline in the input. + // If chunk is empty, it means that this is a blank line which ends the event, + // so an empty Field needs to be returned. + out.Name = "" + out.Value = "" + return true + } else if colonPos == 0 && f.keepComments { + out.Name = FieldNameComment + out.Value = trimFirstSpace(chunk[min(1, l):]) + return true + } + + return false +} + +// ErrUnexpectedEOF is returned when the input is completely parsed but no complete field was found at the end. +var ErrUnexpectedEOF = errors.New("go-sse: unexpected end of input") + +// Next parses the next available field in the remaining buffer. +// It returns false if there are no more fields to parse. +func (f *FieldParser) Next(r *Field) bool { + for f.data != "" { + f.started = true + + chunk, rem, hasNewline := NextChunk(f.data) + if !hasNewline { + f.err = ErrUnexpectedEOF + return false + } + + f.data = rem + + if !f.scanSegment(chunk, r) { + continue + } + + return true + } + + return false +} + +// Reset changes the buffer from which fields are parsed. +func (f *FieldParser) Reset(data string) { + f.data = data + f.err = nil + f.started = false + f.doRemoveBOM() +} + +// Err returns the last error encountered by the parser. It is either nil or ErrUnexpectedEOF. +func (f *FieldParser) Err() error { + return f.err +} + +// Started tells whether parsing has started (a call to Next which consumed input was made +// or the BOM was removed, if it existed). Started will be true if the FieldParser has advanced +// through the data. +func (f *FieldParser) Started() bool { + return f.started +} + +// KeepComments configures the FieldParser to parse/ignore comment fields. +// By default comment fields are ignored. +func (f *FieldParser) KeepComments(shouldKeep bool) { + f.keepComments = shouldKeep +} + +// RemoveBOM configures the FieldParser to try and remove the Unicode BOM +// when parsing the first field, if it exists. +// If, at the time this option is set, the input is untouched (no fields were parsed), +// it will also be attempted to remove the BOM. +func (f *FieldParser) RemoveBOM(shouldRemove bool) { + f.removeBOM = shouldRemove + f.doRemoveBOM() +} + +func (f *FieldParser) doRemoveBOM() { + const bom = "\xEF\xBB\xBF" + if f.removeBOM && !f.started && strings.HasPrefix(f.data, bom) { + f.data = f.data[len(bom):] + f.started = true + } +} + +// NewFieldParser creates a parser that extracts fields from the given string. +func NewFieldParser(data string) *FieldParser { + return &FieldParser{data: data} +} diff --git a/vendor/github.com/tmaxmax/go-sse/internal/parser/parser.go b/vendor/github.com/tmaxmax/go-sse/internal/parser/parser.go new file mode 100644 index 0000000000..8ebf28cafb --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/internal/parser/parser.go @@ -0,0 +1,117 @@ +package parser + +import ( + "bufio" + "io" + "unsafe" +) + +// splitFunc is a split function for a bufio.Scanner that splits a sequence of +// bytes into SSE events. Each event ends with two consecutive newline sequences, +// where a newline sequence is defined as either "\n", "\r", or "\r\n". +func splitFunc(data []byte, atEOF bool) (advance int, token []byte, err error) { + if len(data) == 0 { + return 0, nil, nil + } + + var start int + for { + index, endlineLen := NewlineIndex(unsafe.String(unsafe.SliceData(data), len(data))[advance:]) + advance += index + endlineLen + if index == 0 { + // If it was a blank line, skip it. + start += endlineLen + } + // We've reached the end of data or a second newline follows and the line isn't blank. + // The latter means we have an event. + if advance == len(data) || (isNewlineChar(data[advance]) && index > 0) { + break + } + } + + if l := len(data); advance == l && !atEOF { + // We have reached the end of the buffer but have not yet seen two consecutive + // newline sequences, so we request more data. + return 0, nil, nil + } else if advance < l { + // We have found a newline. Consume the end-of-line sequence. + advance++ + // Consume one more character if end-of-line is "\r\n". + if advance < l && data[advance-1] == '\r' && data[advance] == '\n' { + advance++ + } + } + + token = data[start:advance] + + return advance, token, nil +} + +// Parser extracts fields from a reader. Reading is buffered using a bufio.Scanner. +// The Parser also removes the UTF-8 BOM if it exists. +type Parser struct { + inputScanner *bufio.Scanner + fieldScanner *FieldParser +} + +// Next parses a single field from the reader. It returns false when there are no more fields to parse. +func (r *Parser) Next(f *Field) bool { + if !r.fieldScanner.Next(f) { + if !r.inputScanner.Scan() { + // Do this to signal EOF, which bufio.Scanner suppresses. + if r.inputScanner.Err() == nil { + r.inputScanner = nil + } + return false + } + + if r.fieldScanner.Started() { + // If scanning was started, then an event was already processed at this point and the BOM was + // already removed, if it existed. We don't need to remove it anymore, so disable the option. + r.fieldScanner.RemoveBOM(false) + } + + // The allocation made inside `Text` is not an issue and should even improve performance. + // If the Field returned from `Next` wouldn't own its resources, then the caller would have + // to allocate new memory and copy each field value. This way, not only the caller doesn't + // have to worry about allocations and ownership, but also bigger and less frequent allocations + // are made, compared to the previous usage – allocations are now made per event, not per field value. + r.fieldScanner.Reset(r.inputScanner.Text()) + + return r.fieldScanner.Next(f) + } + + return true +} + +// Err returns the last read error. At the end of input +// it will always be equal to io.EOF. +func (r *Parser) Err() error { + if err := r.fieldScanner.Err(); err != nil { + return err + } + if r.inputScanner == nil { + // Recover the EOF suppressed by bufio.Scanner. + // We need it inside the client, to know when to retry. + return io.EOF + } + return r.inputScanner.Err() +} + +// Buffer sets the buffer used to scan the input. +// For more information, see the documentation on bufio.Scanner.Buffer. +// Do not call this after parsing has started – the method will panic! +func (r *Parser) Buffer(buf []byte, maxSize int) { + r.inputScanner.Buffer(buf, maxSize) +} + +// New returns a Parser that extracts fields from a reader. +func New(r io.Reader) *Parser { + sc := bufio.NewScanner(r) + sc.Split(splitFunc) + + fsc := NewFieldParser("") + fsc.RemoveBOM(true) + + return &Parser{inputScanner: sc, fieldScanner: fsc} +} diff --git a/vendor/github.com/tmaxmax/go-sse/joe.go b/vendor/github.com/tmaxmax/go-sse/joe.go new file mode 100644 index 0000000000..d693774d96 --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/joe.go @@ -0,0 +1,327 @@ +package sse + +import ( + "context" + "runtime/debug" + "sync" +) + +// A Replayer is a type that can replay older published events to new subscribers. +// Replayers use event IDs, the topics the events were published and optionally +// any other criteria to determine which are valid for replay. +// +// While replayers can require events to have IDs beforehand, they can also set the IDs themselves, +// automatically - it's up to the implementation. Replayers should not overwrite or remove any existing +// IDs and return an error instead. +// +// Replayers are not required to be thread-safe - server providers are required to ensure only +// one operation is executed on the replayer at any given time. Server providers may not execute +// replay operation concurrently with other operations, so make sure any action on the replayer +// blocks for as little as possible. If a replayer is thread-safe, some operations may be +// run in a separate goroutine - see the interface's method documentation. +// +// Executing actions that require waiting for a long time on I/O, such as HTTP requests or database +// calls must be handled with great care, so the server provider is not blocked. Reducing them to +// the minimum by using techniques such as caching or by executing them in separate goroutines is +// recommended, as long as the implementation fulfills the requirements. +// +// If not specified otherwise, the errors returned are implementation-specific. +type Replayer interface { + // Put adds a new event to the replay buffer. The Message that is returned may not have the + // same address, if the replayer automatically sets IDs. + // + // Put errors if the message couldn't be queued – if no topics are provided, + // a message without an ID is put into a Replayer which does not + // automatically set IDs, or a message with an ID is put into a Replayer which + // does automatically set IDs. An error should be returned for other failures + // related to the given message. When no topics are provided, ErrNoTopic should be + // returned. + // + // The Put operation may be executed by the replayer in another goroutine only if + // it can ensure that any Replay operation called after the Put goroutine is started + // can replay the new received message. This also requires the replayer implementation + // to be thread-safe. + // + // Replayers are not required to guarantee that immediately after Put returns + // the new messages can be replayed. If an error occurs internally when putting the new message + // and retrying the operation would block for too long, it can be aborted. + // + // To indicate a complete replayer failure (i.e. the replayer won't work after this point) + // a panic should be used instead of an error. + Put(message *Message, topics []string) (*Message, error) + // Replay sends to a new subscriber all the valid events received by the replayer + // since the event with the listener's ID. If the ID the listener provides + // is invalid, the provider should not replay any events. + // + // Replay calls must return only after replaying is done. + // Implementations should not keep references to the subscription client + // after Replay returns. + // + // If an error is returned, then at least some messages weren't successfully replayed. + // The error is nil if there were no messages to replay for the particular subscription + // or if all messages were replayed successfully. + // + // If any messages are replayed, Client.Flush must be called by implementations. + Replay(subscription Subscription) error +} + +type ( + subscriber chan<- error + subscription struct { + done subscriber + Subscription + } + + messageWithTopics struct { + message *Message + topics []string + } + + publishedMessage struct { + replayerErr chan<- error + messageWithTopics + } +) + +// Joe is a basic server provider that synchronously executes operations by queueing them in channels. +// Events are also sent synchronously to subscribers, so if a subscriber's callback blocks, the others +// have to wait. +// +// Joe optionally supports event replaying with the help of a Replayer. +// +// If the replayer panics, the subscription for which it panicked is considered failed +// and an error is returned, and thereafter the replayer is not used anymore – no replays +// will be attempted for future subscriptions. +// If due to some other unexpected scenario something panics internally, Joe will remove all subscribers +// and close itself, so subscribers don't end up blocked. +// +// He serves simple use-cases well, as he's light on resources, and does not require any external +// services. Also, he is the default provider for Servers. +type Joe struct { + message chan publishedMessage + subscription chan subscription + unsubscription chan subscriber + done chan struct{} + closed chan struct{} + subscribers map[subscriber]Subscription + + // An optional replayer that Joe uses to resend older messages to new subscribers. + Replayer Replayer + + initDone sync.Once +} + +// Subscribe tells Joe to send new messages to this subscriber. The subscription +// is automatically removed when the context is done, a client error occurs +// or Joe is stopped. +// +// Subscribe returns without error only when the unsubscription is caused +// by the given context being canceled. +func (j *Joe) Subscribe(ctx context.Context, sub Subscription) error { + j.init() + + // Without a buffered channel we risk a deadlock when Subscribe + // stops receiving from this channel on done context and Joe + // encounters an error when sending messages or replaying. + done := make(chan error, 1) + + select { + case <-j.done: + return ErrProviderClosed + case j.subscription <- subscription{done: done, Subscription: sub}: + } + + select { + case err := <-done: + return err + case <-j.closed: + return ErrProviderClosed + case <-ctx.Done(): + } + + select { + case <-j.done: + return ErrProviderClosed + case j.unsubscription <- done: + // NOTE(tmaxmax): should we return ctx.Err() instead? + return nil + } +} + +// Publish tells Joe to send the given message to the subscribers. +// When a message is published to multiple topics, Joe makes sure to +// not send the Message multiple times to clients that are subscribed +// to more than one topic that receive the given Message. Every client +// receives each unique message once, regardless of how many topics it +// is subscribed to or to how many topics the message is published. +// +// It returns ErrNoTopic if no topics are provided, eventual Replayer.Put +// errors or ErrProviderClosed. If the replayer returns an error the +// message will still be sent but most probably it won't be replayed to +// new subscribers, depending on how the error is handled by the replay provider. +func (j *Joe) Publish(msg *Message, topics []string) error { + if len(topics) == 0 { + return ErrNoTopic + } + + j.init() + + // Buffered to prevent a deadlock when Publish doesn't + // receive from errs due to Joe being shut down and the + // message published causes an error after the shutdown. + errs := make(chan error, 1) + + pub := publishedMessage{replayerErr: errs} + pub.message = msg + pub.topics = topics + + // Waiting on done ensures Publish doesn't block the caller goroutine + // when Joe is stopped and implements the required Provider behavior. + select { + case j.message <- pub: + return <-errs + case <-j.done: + return ErrProviderClosed + } +} + +// Shutdown signals Joe to close all subscribers and stop receiving messages. +// It returns when all the subscribers are closed. +// +// Further calls to Stop will return ErrProviderClosed. +func (j *Joe) Shutdown(ctx context.Context) (err error) { + j.init() + + defer func() { + if r := recover(); r != nil { + err = ErrProviderClosed + } + }() + + close(j.done) + + select { + case <-j.closed: + case <-ctx.Done(): + err = ctx.Err() + } + + return +} + +func (j *Joe) removeSubscriber(sub subscriber) { + l := len(j.subscribers) + delete(j.subscribers, sub) + // We check that an element was deleted as removeSubscriber is called twice + // in the following edge case: the subscriber context is done before a + // published message is sent/flushed, and the send/flush returns an error. + if l != len(j.subscribers) { + close(sub) + } +} + +func (j *Joe) start(replay Replayer) { + defer close(j.closed) + + for { + select { + case msg := <-j.message: + if replay != nil { + m, err := tryPut(msg.messageWithTopics, &replay) + if _, isPanic := err.(replayPanic); err != nil && !isPanic { //nolint:errorlint // it's our error + // NOTE(tmaxmax): We could return panic errors here but we'd have to expose + // the error type in order for this error to be handled. Let's not change + // the public errors for now. See also the other note below. + msg.replayerErr <- err + } else if m != nil { + msg.message = m + } + } + close(msg.replayerErr) + + for done, sub := range j.subscribers { + if topicsIntersect(sub.Topics, msg.topics) { + err := sub.Client.Send(msg.message) + if err == nil { + err = sub.Client.Flush() + } + + if err != nil { + done <- err + // Technically it would be possible to just send the error, + // as Subscribe would send an unsubscription signal. The problem + // is that if the j.message channel is ready together with j.unsubscription + // and j.message is picked we might send again to this now unsubscribed + // subscriber, which will cause issues (e.g. deadlock on done). + // This line here is the reason why we need to verify we actually + // have this subscriber in removeSubscriber above. + j.removeSubscriber(done) + } + } + } + case sub := <-j.subscription: + var err error + if replay != nil { + err = tryReplay(sub.Subscription, &replay) + } + + // NOTE(tmaxmax): We can't meaningfully handle replay panics in any way + // other than disabling replay altogether. This ensures uptime + // in the face of unexpected – returning the panic as an error + // to the subscriber doesn't make sense, as it's probably not the subscriber's fault. + if _, isPanic := err.(replayPanic); err != nil && !isPanic { //nolint:errorlint // it's our error + sub.done <- err + close(sub.done) + } else { + j.subscribers[sub.done] = sub.Subscription + } + case sub := <-j.unsubscription: + j.removeSubscriber(sub) + case <-j.done: + return + } + } +} + +func tryReplay(sub Subscription, replay *Replayer) (err error) { //nolint:gocritic // intended + defer handleReplayerPanic(replay, &err) + + return (*replay).Replay(sub) +} + +func tryPut(msg messageWithTopics, replay *Replayer) (m *Message, err error) { //nolint:gocritic // intended + defer handleReplayerPanic(replay, &err) + + return (*replay).Put(msg.message, msg.topics) +} + +type replayPanic struct{} + +func (replayPanic) Error() string { return "replay provider panicked" } + +func handleReplayerPanic(replay *Replayer, errp *error) { //nolint:gocritic // intended + if r := recover(); r != nil { + *replay = nil + *errp = replayPanic{} + // NOTE(tmaxmax): At least print a stacktrace. It's annoying when libraries recover from panics + // and make them untraceable. Should we provide a way to handle these in a custom manner? + debug.PrintStack() + } +} + +func (j *Joe) init() { + j.initDone.Do(func() { + j.message = make(chan publishedMessage) + j.subscription = make(chan subscription) + j.unsubscription = make(chan subscriber) + j.done = make(chan struct{}) + j.closed = make(chan struct{}) + j.subscribers = map[subscriber]Subscription{} + + replay := j.Replayer + if replay == nil { + replay = noopReplayer{} + } + go j.start(replay) + }) +} diff --git a/vendor/github.com/tmaxmax/go-sse/message.go b/vendor/github.com/tmaxmax/go-sse/message.go new file mode 100644 index 0000000000..c39b190f5c --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/message.go @@ -0,0 +1,354 @@ +package sse + +import ( + "bytes" + "fmt" + "io" + "strconv" + "strings" + "time" + "unicode/utf8" + "unsafe" + + "github.com/tmaxmax/go-sse/internal/parser" +) + +func isSingleLine(p string) bool { + _, newlineLen := parser.NewlineIndex(p) + return newlineLen == 0 +} + +// fieldBytes holds the byte representation of each field type along with a colon at the end. +var ( + fieldBytesData = []byte(parser.FieldNameData + ": ") + fieldBytesEvent = []byte(parser.FieldNameEvent + ": ") + fieldBytesRetry = []byte(parser.FieldNameRetry + ": ") + fieldBytesID = []byte(parser.FieldNameID + ": ") + fieldBytesComment = []byte(": ") +) + +type chunk struct { + content string + isComment bool +} + +var newline = []byte{'\n'} + +func (c *chunk) WriteTo(w io.Writer) (int64, error) { + name := fieldBytesData + if c.isComment { + name = fieldBytesComment + } + n, err := w.Write(name) + if err != nil { + return int64(n), err + } + m, err := writeString(w, c.content) + n += m + if err != nil { + return int64(n), err + } + m, err = w.Write(newline) + return int64(n + m), err +} + +// Message is the representation of an event sent from the server to its clients. +type Message struct { + chunks []chunk + + ID EventID + Type EventType + Retry time.Duration +} + +func (e *Message) appendText(isComment bool, chunks ...string) { + for _, c := range chunks { + var content string + + for c != "" { + content, c, _ = parser.NextChunk(c) + e.chunks = append(e.chunks, chunk{content: content, isComment: isComment}) + } + } +} + +// AppendData adds multiple data fields on the message's event from the given strings. +// Each string will be a distinct data field, and if the strings themselves span multiple lines +// they will be broken into multiple fields. +// +// Server-sent events are not suited for binary data: the event fields are delimited by newlines, +// where a newline can be a LF, CR or CRLF sequence. When the client interprets the fields, +// it joins multiple data fields using LF, so information is altered. Here's an example: +// +// initial payload: This is a\r\nmultiline\rtext.\nIt has multiple\nnewline\r\nvariations. +// data sent over the wire: +// data: This is a +// data: multiline +// data: text. +// data: It has multiple +// data: newline +// data: variations +// data received by client: This is a\nmultiline\ntext.\nIt has multiple\nnewline\nvariations. +// +// Each line prepended with "data:" is a field; multiple data fields are joined together using LF as the delimiter. +// If you attempted to send the same payload without prepending the "data:" prefix, like so: +// +// data: This is a +// multiline +// text. +// It has multiple +// newline +// variations +// +// there would be only one data field (the first one). The rest would be different fields, named "multiline", "text.", +// "It has multiple" etc., which are invalid fields according to the protocol. +// +// Besides, the protocol explicitly states that event streams must always be UTF-8 encoded: +// https://html.spec.whatwg.org/multipage/server-sent-events.html#parsing-an-event-stream. +// +// If you need to send binary data, you can use a Base64 encoder or any other encoder that does not output +// any newline characters (\r or \n) and then append the resulted data. +// +// Given that clients treat all newlines the same and replace the original newlines with LF, +// for internal code simplicity AppendData replaces them as well. +func (e *Message) AppendData(chunks ...string) { + e.appendText(false, chunks...) +} + +// AppendComment adds comment fields to the message's event. +// If the comments span multiple lines, they are broken into multiple comment fields. +func (e *Message) AppendComment(comments ...string) { + e.appendText(true, comments...) +} + +func (e *Message) writeMessageField(w io.Writer, f messageField, fieldBytes []byte) (int64, error) { + if !f.IsSet() { + return 0, nil + } + + n, err := w.Write(fieldBytes) + if err != nil { + return int64(n), err + } + m, err := writeString(w, f.String()) + n += m + if err != nil { + return int64(n), err + } + m, err = w.Write(newline) + return int64(n + m), err +} + +func (e *Message) writeID(w io.Writer) (int64, error) { + return e.writeMessageField(w, e.ID.messageField, fieldBytesID) +} + +func (e *Message) writeType(w io.Writer) (int64, error) { + return e.writeMessageField(w, e.Type.messageField, fieldBytesEvent) +} + +func (e *Message) writeRetry(w io.Writer) (int64, error) { + millis := e.Retry.Milliseconds() + if millis <= 0 { + return 0, nil + } + + n, err := w.Write(fieldBytesRetry) + if err != nil { + return int64(n), err + } + + var buf [13]byte // log10(INT64_MAX / 1e6) ~= 13 + + i := len(buf) - 1 + for millis != 0 { + buf[i] = '0' + byte(millis%10) + i-- + millis /= 10 + } + + m, err := w.Write(buf[i+1:]) + n += m + if err != nil { + return int64(n), err + } + m, err = w.Write(newline) + return int64(n + m), err +} + +// WriteTo writes the standard textual representation of the message's event to an io.Writer. +// This operation is heavily optimized, so it is strongly preferred over MarshalText or String. +func (e *Message) WriteTo(w io.Writer) (int64, error) { + n, err := e.writeID(w) + if err != nil { + return n, err + } + m, err := e.writeType(w) + n += m + if err != nil { + return n, err + } + m, err = e.writeRetry(w) + n += m + if err != nil { + return n, err + } + for i := range e.chunks { + m, err = e.chunks[i].WriteTo(w) + n += m + if err != nil { + return n, err + } + } + if n == 0 { + return 0, nil + } + o, err := w.Write(newline) + return int64(o) + n, err +} + +// MarshalText writes the standard textual representation of the message's event. Marshalling and unmarshalling will +// result in a message with an event that has the same fields; topic will be lost. +// +// If you want to preserve everything, create your own custom marshalling logic. +// For an example using encoding/json, see the top-level MessageCustomJSONMarshal example. +// +// Use the WriteTo method if you don't need the byte representation. +// +// The representation is written to a bytes.Buffer, which means the error is always nil. +// If the buffer grows to a size bigger than the maximum allowed, MarshalText will panic. +// See the bytes.Buffer documentation for more info. +func (e *Message) MarshalText() ([]byte, error) { + b := bytes.Buffer{} + _, err := e.WriteTo(&b) + return b.Bytes(), err +} + +// String writes the message's event standard textual representation to a strings.Builder and returns the resulted string. +// It may panic if the representation is too long to be buffered. +// +// Use the WriteTo method if you don't actually need the string representation. +func (e *Message) String() string { + s := strings.Builder{} + _, _ = e.WriteTo(&s) + return s.String() +} + +// UnmarshalError is the error returned by the Message's UnmarshalText method. +// If the error is related to a specific field, FieldName will be a non-empty string. +// If no fields were found in the target text or any other errors occurred, only +// a Reason will be provided. Reason is always present. +type UnmarshalError struct { + Reason error + FieldName string + // The value of the invalid field. + FieldValue string +} + +func (u *UnmarshalError) Error() string { + if u.FieldName == "" { + return fmt.Sprintf("unmarshal event error: %s", u.Reason.Error()) + } + return fmt.Sprintf("unmarshal event error, %s field invalid: %s. contents: %s", u.FieldName, u.Reason.Error(), u.FieldValue) +} + +func (u *UnmarshalError) Unwrap() error { + return u.Reason +} + +// ErrUnexpectedEOF is returned when unmarshaling a Message from an input that doesn't end in a newline. +// +// If it returned from a Connection, it means that the data from the server has reached EOF +// in the middle of an incomplete event and retries are disabled (normally the client retries +// the connection in this situation). +var ErrUnexpectedEOF = parser.ErrUnexpectedEOF + +func (e *Message) reset() { + e.chunks = nil + e.Type = EventType{} + e.ID = EventID{} + e.Retry = 0 +} + +// UnmarshalText extracts the first event found in the given byte slice into the +// receiver. The input is expected to be a wire format event, as defined by the spec. +// Therefore, previous fields present on the Message will be overwritten +// (i.e. event, ID, comments, data, retry). +// +// Unmarshaling ignores fields with invalid names. If no valid fields are found, +// an error is returned. For a field to be valid it must end in a newline - if the last +// field of the event doesn't end in one, an error is returned. +// +// All returned errors are of type UnmarshalError. +func (e *Message) UnmarshalText(p []byte) error { + e.reset() + + s := parser.NewFieldParser(string(p)) + s.KeepComments(true) + s.RemoveBOM(true) + +loop: + for f := (parser.Field{}); s.Next(&f); { + switch f.Name { + case parser.FieldNameRetry: + if i := strings.IndexFunc(f.Value, func(r rune) bool { + return r < '0' || r > '9' + }); i != -1 { + r, _ := utf8.DecodeRuneInString(f.Value[i:]) + + return &UnmarshalError{ + FieldName: string(f.Name), + FieldValue: f.Value, + Reason: fmt.Errorf("contains character %q, which is not an ASCII digit", r), + } + } + + milli, err := strconv.ParseInt(f.Value, 10, 64) + if err != nil { + return &UnmarshalError{ + FieldName: string(f.Name), + FieldValue: f.Value, + Reason: fmt.Errorf("invalid retry value: %w", err), + } + } + + e.Retry = time.Duration(milli) * time.Millisecond + case parser.FieldNameData, parser.FieldNameComment: + e.chunks = append(e.chunks, chunk{content: f.Value, isComment: f.Name == parser.FieldNameComment}) + case parser.FieldNameEvent: + e.Type.value = f.Value + e.Type.set = true + case parser.FieldNameID: + if strings.IndexByte(f.Value, 0) != -1 { + break + } + + e.ID.value = f.Value + e.ID.set = true + default: // event end + break loop + } + } + + if len(e.chunks) == 0 && !e.Type.IsSet() && e.Retry == 0 && !e.ID.IsSet() || s.Err() != nil { + e.reset() + return &UnmarshalError{Reason: ErrUnexpectedEOF} + } + return nil +} + +// Clone returns a copy of the message. +func (e *Message) Clone() *Message { + return &Message{ + // The first AppendData will trigger a reallocation. + // Already appended chunks cannot be modified/removed, so this is safe. + chunks: e.chunks[:len(e.chunks):len(e.chunks)], + Retry: e.Retry, + Type: e.Type, + ID: e.ID, + } +} + +func writeString(w io.Writer, s string) (int, error) { + return w.Write(unsafe.Slice(unsafe.StringData(s), len(s))) +} diff --git a/vendor/github.com/tmaxmax/go-sse/message_fields.go b/vendor/github.com/tmaxmax/go-sse/message_fields.go new file mode 100644 index 0000000000..4fb60420ff --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/message_fields.go @@ -0,0 +1,181 @@ +package sse + +import ( + "database/sql/driver" + "encoding/json" + "errors" + "fmt" +) + +// EventID is a value of the "id" field. +// It must have a single line. +type EventID struct { + messageField +} + +// NewID creates an event ID value. A valid ID must not have any newlines. +// If the input is not valid, an unset (invalid) ID is returned. +func NewID(value string) (EventID, error) { + f, err := newMessageField(value) + if err != nil { + return EventID{}, fmt.Errorf("invalid event ID: %w", err) + } + + return EventID{f}, nil +} + +// ID creates an event ID and assumes it is valid. +// If it is not valid, it panics. +func ID(value string) EventID { + return must(NewID(value)) +} + +// EventType is a value of the "event" field. +// It must have a single line. +type EventType struct { + messageField +} + +// NewType creates a value for the "event" field. +// It is valid if it does not have any newlines. +// If the input is not valid, an unset (invalid) ID is returned. +func NewType(value string) (EventType, error) { + f, err := newMessageField(value) + if err != nil { + return EventType{}, fmt.Errorf("invalid event type: %w", err) + } + + return EventType{f}, nil +} + +// Type creates an EventType and assumes it is valid. +// If it is not valid, it panics. +func Type(value string) EventType { + return must(NewType(value)) +} + +func must[T any](v T, err error) T { + if err != nil { + panic(err) + } + return v +} + +// The messageField struct represents any valid field value +// i.e. single line strings. +// Must be passed by value and are comparable. +type messageField struct { + value string + set bool +} + +func newMessageField(value string) (messageField, error) { + if !isSingleLine(value) { + return messageField{}, errors.New("input is multiline") + } + return messageField{value: value, set: true}, nil +} + +// IsSet returns true if the receiver is a valid (set) value. +func (i messageField) IsSet() bool { + return i.set +} + +// String returns the underlying value. The value may be an empty string, +// make sure to check if the value is set before using it. +func (i messageField) String() string { + return i.value +} + +// UnmarshalText sets the underlying value to the given string, if valid. +// If the input is invalid, no changes are made to the receiver. +func (i *messageField) UnmarshalText(data []byte) error { + *i = messageField{} + + id, err := newMessageField(string(data)) + if err != nil { + return err + } + + *i = id + + return nil +} + +// UnmarshalJSON sets the underlying value to the given JSON value +// if the value is a string. The previous value is discarded if the operation fails. +func (i *messageField) UnmarshalJSON(data []byte) error { + *i = messageField{} + + if string(data) == "null" { + return nil + } + + var input string + + if err := json.Unmarshal(data, &input); err != nil { + return err + } + + id, err := newMessageField(input) + if err != nil { + return err + } + + *i = id + + return nil +} + +// MarshalText returns a copy of the underlying value if it is set. +// It returns an error when trying to marshal an unset value. +func (i *messageField) MarshalText() ([]byte, error) { + if i.IsSet() { + return []byte(i.String()), nil + } + + return nil, fmt.Errorf("can't marshal unset string to text") +} + +// MarshalJSON returns a JSON representation of the underlying value if it is set. +// It otherwise returns the representation of the JSON null value. +func (i *messageField) MarshalJSON() ([]byte, error) { + if i.IsSet() { + return json.Marshal(i.String()) + } + + return json.Marshal(nil) +} + +// Scan implements the sql.Scanner interface. Values can be scanned from: +// - nil interfaces (result: unset value) +// - byte slice +// - string +func (i *messageField) Scan(src interface{}) error { + *i = messageField{} + + if src == nil { + return nil + } + + switch v := src.(type) { + case []byte: + i.value = string(v) + case string: + i.value = string([]byte(v)) + default: + return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, *i) + } + + i.set = true + + return nil +} + +// Value implements the driver.Valuer interface. +func (i messageField) Value() (driver.Value, error) { + if i.IsSet() { + return i.String(), nil + } + return nil, nil +} diff --git a/vendor/github.com/tmaxmax/go-sse/replay.go b/vendor/github.com/tmaxmax/go-sse/replay.go new file mode 100644 index 0000000000..6cbe015462 --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/replay.go @@ -0,0 +1,388 @@ +package sse + +import ( + "errors" + "strconv" + "time" +) + +// NewFiniteReplayer creates a finite replay provider with the given max +// count and auto ID behaviour. +// +// Count is the maximum number of events FiniteReplayer should hold as +// valid. It must be greater than zero. +// +// AutoIDs configures FiniteReplayer to automatically set the IDs of +// events. +func NewFiniteReplayer( + count int, autoIDs bool, +) (*FiniteReplayer, error) { + if count < 2 { + return nil, errors.New("count must be at least 2") + } + + r := &FiniteReplayer{} + r.buf.buf = make([]messageWithTopics, count) + if autoIDs { + r.currentID = new(uint64) + } + + return r, nil +} + +// FiniteReplayer is a replayer that replays at maximum a certain number of events. +// The events must have an ID unless the replayer is configured to set IDs automatically. +type FiniteReplayer struct { + currentID *uint64 + buf queue[messageWithTopics] +} + +// Put puts a message into the replayer's buffer. If there are more messages than the maximum +// number, the oldest message is removed. +func (f *FiniteReplayer) Put(message *Message, topics []string) (*Message, error) { + if len(topics) == 0 { + return nil, ErrNoTopic + } + + message, err := ensureID(message, f.currentID) + if err != nil { + return nil, err + } + + f.buf.enqueue(messageWithTopics{message: message, topics: topics}) + + return message, nil +} + +// Replay replays the stored messages to the listener. +func (f *FiniteReplayer) Replay(subscription Subscription) error { + i := findIDInQueue(&f.buf, subscription.LastEventID, f.currentID != nil) + if i < 0 { + return nil + } + + var err error + f.buf.each(i)(func(_ int, m messageWithTopics) bool { + if topicsIntersect(subscription.Topics, m.topics) { + if err = subscription.Client.Send(m.message); err != nil { + return false + } + } + return true + }) + if err != nil { + return err + } + + return subscription.Client.Flush() +} + +// ValidReplayer is a Replayer that replays all the buffered non-expired events. +// +// The replayer removes any expired events when a new event is put and after at least +// a GCInterval period passed. +// +// The events must have an ID unless the replayer is configured to set IDs automatically. +type ValidReplayer struct { + lastGC time.Time + + // The function used to retrieve the current time. Defaults to time.Now. + // Useful when testing. + Now func() time.Time + + currentID *uint64 + messages queue[messageWithTopicsAndExpiry] + + ttl time.Duration + // After how long the replayer should attempt to clean up expired events. + // By default cleanup is done after a fourth of the TTL has passed; this means + // that messages may be stored for a duration equal to 5/4*TTL. If this is not + // desired, set the GC interval to a value sensible for your use case or set + // it to 0 – this disables automatic cleanup, enabling you to do it manually + // using the GC method. + GCInterval time.Duration +} + +// NewValidReplayer creates a ValidReplayer with the given message +// lifetime duration (time-to-live) and auto ID behavior. +// +// The TTL must be a positive duration. It is technically possible to use a very +// big duration in order to store and replay every message put for the lifetime +// of the program; this is not recommended, as memory usage becomes effectively +// unbounded which might lead to a crash. +func NewValidReplayer(ttl time.Duration, autoIDs bool) (*ValidReplayer, error) { + if ttl <= 0 { + return nil, errors.New("event TTL must be greater than zero") + } + + r := &ValidReplayer{ + Now: time.Now, + GCInterval: ttl / 4, + ttl: ttl, + } + + if autoIDs { + r.currentID = new(uint64) + } + + return r, nil +} + +// Put puts the message into the replayer's buffer. +func (v *ValidReplayer) Put(message *Message, topics []string) (*Message, error) { + if len(topics) == 0 { + return nil, ErrNoTopic + } + + now := v.Now() + if v.lastGC.IsZero() { + v.lastGC = now + } + + if v.shouldGC(now) { + v.doGC(now) + v.lastGC = now + } + + message, err := ensureID(message, v.currentID) + if err != nil { + return nil, err + } + + if v.messages.count == len(v.messages.buf) { + newCap := len(v.messages.buf) * 2 + if minCap := 4; newCap < minCap { + newCap = minCap + } + v.messages.resize(newCap) + } + + v.messages.enqueue(messageWithTopicsAndExpiry{messageWithTopics: messageWithTopics{message: message, topics: topics}, exp: now.Add(v.ttl)}) + + return message, nil +} + +func (v *ValidReplayer) shouldGC(now time.Time) bool { + return v.GCInterval > 0 && now.Sub(v.lastGC) >= v.GCInterval +} + +// GC removes all the expired messages from the replayer's buffer. +func (v *ValidReplayer) GC() { + v.doGC(v.Now()) +} + +func (v *ValidReplayer) doGC(now time.Time) { + for v.messages.count > 0 { + e := v.messages.buf[v.messages.head] + if e.exp.After(now) { + break + } + + v.messages.dequeue() + } + + if v.messages.count <= len(v.messages.buf)/4 { + newCap := len(v.messages.buf) / 2 + if minCap := 4; newCap < minCap { + newCap = minCap + } + v.messages.resize(newCap) + } +} + +// Replay replays all the valid messages to the listener. +func (v *ValidReplayer) Replay(subscription Subscription) error { + i := findIDInQueue(&v.messages, subscription.LastEventID, v.currentID != nil) + if i < 0 { + return nil + } + + now := v.Now() + + var err error + v.messages.each(i)(func(_ int, m messageWithTopicsAndExpiry) bool { + if m.exp.After(now) && topicsIntersect(subscription.Topics, m.topics) { + if err = subscription.Client.Send(m.message); err != nil { + return false + } + } + return true + }) + if err != nil { + return err + } + + return subscription.Client.Flush() +} + +// topicsIntersect returns true if the given topic slices have at least one topic in common. +func topicsIntersect(a, b []string) bool { + for _, at := range a { + for _, bt := range b { + if at == bt { + return true + } + } + } + + return false +} + +func ensureID(m *Message, currentID *uint64) (*Message, error) { + if currentID == nil { + if !m.ID.IsSet() { + return nil, errors.New("message has no ID") + } + + return m, nil + } + + if m.ID.IsSet() { + return nil, errors.New("message already has an ID, can't use generated ID") + } + + m = m.Clone() + m.ID = ID(strconv.FormatUint(*currentID, 10)) + + (*currentID)++ + + return m, nil +} + +type queue[T any] struct { + buf []T + head, tail, count int +} + +func (q *queue[T]) each(startAt int) func(func(int, T) bool) { + return func(yield func(int, T) bool) { + if startAt < q.tail { + for i := startAt; i < q.tail; i++ { + if !yield(i, q.buf[i]) { + return + } + } + } else { + for i := startAt; i < len(q.buf); i++ { + if !yield(i, q.buf[i]) { + return + } + } + for i := 0; i < q.tail; i++ { + if !yield(i, q.buf[i]) { + return + } + } + } + } +} + +func (q *queue[T]) enqueue(v T) { + q.buf[q.tail] = v + + q.tail++ + + overwritten := false + if q.tail > q.head && q.count == len(q.buf) { + q.head = q.tail + overwritten = true + } else { + q.count++ + } + + if q.tail == len(q.buf) { + q.tail = 0 + if overwritten { + q.head = 0 + } + } +} + +func (q *queue[T]) dequeue() { + q.buf[q.head] = *new(T) + + q.head++ + if q.head == len(q.buf) { + q.head = 0 + } + + q.count-- +} + +func (q *queue[T]) resize(newSize int) { + buf := make([]T, newSize) + if q.head < q.tail { + copy(buf, q.buf[q.head:q.tail]) + } else { + n := copy(buf, q.buf[q.head:]) + copy(buf[n:], q.buf[:q.tail]) + } + + q.head = 0 + q.tail = q.count + q.buf = buf +} + +func findIDInQueue[M interface{ ID() EventID }](q *queue[M], id EventID, autoID bool) int { + if q.count == 0 { + return -1 + } + + if autoID { + id, err := strconv.ParseUint(id.String(), 10, 64) + if err != nil { + return -1 + } + + firstID, _ := strconv.ParseUint(q.buf[q.head].ID().String(), 10, 64) + + pos := -1 + if delta := id - firstID; id >= firstID { + if delta >= uint64(q.count) { //nolint:gosec // int always positive + return -1 + } + pos = int(delta) //nolint:gosec // delta < q.count, which is an int + } + + i := pos + q.head + 1 + if i >= len(q.buf) { + i -= len(q.buf) + } + + return i + } + + i := -1 + q.each(q.head)(func(j int, m M) bool { + if m.ID() == id { + i = j + return false + } + return true + }) + + if i != -1 { + i++ + if i == len(q.buf) { + i = 0 + } else if i == q.tail { + i = -1 + } + } + + return i +} + +func (m messageWithTopics) ID() EventID { return m.message.ID } + +type messageWithTopicsAndExpiry struct { + exp time.Time + messageWithTopics +} + +// noopReplayer is the default replay provider used if none is given. It does nothing. +// It is used to avoid nil checks for the provider each time it is used. +type noopReplayer struct{} + +func (n noopReplayer) Put(m *Message, _ []string) (*Message, error) { return m, nil } +func (n noopReplayer) Replay(_ Subscription) error { return nil } diff --git a/vendor/github.com/tmaxmax/go-sse/server.go b/vendor/github.com/tmaxmax/go-sse/server.go new file mode 100644 index 0000000000..7e27a58a08 --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/server.go @@ -0,0 +1,233 @@ +/* +Package sse provides utilities for creating and consuming fully spec-compliant HTML5 server-sent events streams. + +The central piece of a server's implementation is the Provider interface. A Provider describes a publish-subscribe +system that can be used to implement messaging for the SSE protocol. This package already has an +implementation, called Joe, that is the default provider for any server. Abstracting the messaging +system implementation away allows servers to use any arbitrary provider under the same interface. +The default provider will work for simple use-cases, but where scalability is required, one will +look at a more suitable solution. Adapters that satisfy the Provider interface can easily be created, +and then plugged into the server instance. +Events themselves are represented using the Message type. + +On the client-side, we use the Client struct to create connections to event streams. Using an `http.Request` +we instantiate a Connection. Then we subscribe to incoming events using callback functions, and then +we establish the connection by calling the Connection's Connect method. +*/ +package sse + +import ( + "context" + "errors" + "log/slog" + "net/http" + "sync" +) + +// The Subscription struct is used to subscribe to a given provider. +type Subscription struct { + // The client to which messages are sent. The implementation of the interface does not have to be + // thread-safe – providers will not call methods on it concurrently. + Client MessageWriter + // An optional last event ID indicating the event to resume the stream from. + // The events will replay starting from the first valid event sent after the one with the given ID. + // If the ID is invalid replaying events will be omitted and new events will be sent as normal. + LastEventID EventID + // The topics to receive message from. Must be a non-empty list. + // Topics are orthogonal to event types. They are used to filter what the server sends to each client. + Topics []string +} + +// A Provider is a publish-subscribe system that can be used to implement a HTML5 server-sent events +// protocol. A standard interface is required so HTTP request handlers are agnostic to the provider's implementation. +// +// Providers are required to be thread-safe. +// +// After Shutdown is called, trying to call any method of the provider must return ErrProviderClosed. The providers +// may return other implementation-specific errors too, but the close error is guaranteed to be the same across +// providers. +type Provider interface { + // Subscribe to the provider. The context is used to remove the subscriber automatically + // when it is done. Errors returned by the subscription's callback function must be returned + // by Subscribe. + // + // Providers can assume that the topics list for a subscription has at least one topic. + Subscribe(ctx context.Context, subscription Subscription) error + // Publish a message to all the subscribers that are subscribed to the given topics. + // The topics slice must be non-empty, or ErrNoTopic will be raised. + Publish(message *Message, topics []string) error + // Shutdown stops the provider. Calling Shutdown will clean up all the provider's resources + // and make Subscribe and Publish fail with an error. All the listener channels will be + // closed and any ongoing publishes will be aborted. + // + // If the given context times out before the provider is shut down – shutting it down takes + // longer, the context error is returned. + // + // Calling Shutdown multiple times after it successfully returned the first time + // does nothing but return ErrProviderClosed. + Shutdown(ctx context.Context) error +} + +// ErrProviderClosed is a sentinel error returned by providers when any operation is attempted after the provider is closed. +// A closed provider might also be a result of an unexpected panic inside the provider. +var ErrProviderClosed = errors.New("go-sse.server: provider is closed") + +// ErrNoTopic is a sentinel error returned when a Message is published without any topics. +// It is not an issue to call Server.Publish without topics, because the Server will add the DefaultTopic; +// it is an error to call Provider.Publish or Replayer.Put without any topics, though. +var ErrNoTopic = errors.New("go-sse.server: no topics specified") + +// DefaultTopic is the identifier for the topic that is implied when no topics are specified for a Subscription +// or a Message. +const DefaultTopic = "" + +// A Server is mostly a convenience wrapper around a Provider. +// It implements the http.Handler interface and has some methods +// for calling the underlying provider's methods. +// +// When creating a server, if no provider is specified using the WithProvider +// option, the Joe provider found in this package with no replay provider is used. +type Server struct { + // The provider used to publish and subscribe clients to events. + // Defaults to Joe. + Provider Provider + // A callback that's called when an SSE session is started. + // You can use this to authorize the session, set the topics + // the client should be subscribed to and so on. Using the + // Res field of the Session you can write an error response + // to the client. + // + // The boolean returned indicates whether the given request + // should be accepted or not. If it is true, the Provider will receive + // a new subscription for the connection and events will be sent + // to this client, otherwise the request will be ended. + // + // Note that OnSession can write the HTTP response code itself, if something other + // than the implicit 200 OK is desired. This is especially helpful when refusing sessions – + // if OnSession does not write a response code, clients will receive a confusing 200 OK. + // + // If this is not set, the client will be subscribed to the provider + // using the DefaultTopic. + OnSession func(w http.ResponseWriter, r *http.Request) (topics []string, allowed bool) + // If the Logger function is set and returns a non-nil Logger instance, + // the Server will log various information about the request lifecycle. + Logger func(r *http.Request) *slog.Logger + + provider Provider + initDone sync.Once +} + +// ServeHTTP implements a default HTTP handler for a server. +// +// This handler upgrades the request, subscribes it to the server's provider and +// starts sending incoming events to the client, while logging any errors. +// It also sends the Last-Event-ID header's value, if present. +// +// If the request isn't upgradeable, it writes a message to the client along with +// an 500 Internal Server ConnectionError response code. If on subscribe the provider returns +// an error, it writes the error message to the client and a 500 Internal Server ConnectionError +// response code. +// +// To customize behavior, use the OnSession callback or create your custom handler. +func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + s.init() + // Make sure to keep the ServeHTTP implementation line number in sync with the number in the README! + + var l *slog.Logger + if s.Logger != nil { + l = s.Logger(r) + } + + if l != nil { + l.Info("sse: starting new session") + } + + sess, err := Upgrade(w, r) + if err != nil { + if l != nil { + l.Error("sse: unsupported", "error", err) + } + + http.Error(w, "Server-sent events unsupported", http.StatusInternalServerError) + return + } + + sub, ok := s.getSubscription(sess) + if !ok { + if l != nil { + l.Warn("sse: invalid subscription") + } + + return + } + + if l != nil { + l.Info("sse: subscribing session", "topics", sub.Topics, "lastEventID", sub.LastEventID) + } + + if err = s.provider.Subscribe(r.Context(), sub); err != nil { + if l != nil { + l.Error("sse: subscribe error", "error", err) + } + + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if l != nil { + l.Info("sse: session ended") + } +} + +// Publish sends the event to all subscribes that are subscribed to the topic the event is published to. +// The topics are optional - if none are specified, the event is published to the DefaultTopic. +func (s *Server) Publish(e *Message, topics ...string) error { + s.init() + return s.provider.Publish(e, getTopics(topics)) +} + +// Shutdown closes all the connections and stops the server. Publish operations will fail +// with the error sent by the underlying provider. NewServer requests will be ignored. +// +// Call this method when shutting down the HTTP server using http.Server's RegisterOnShutdown +// method. Not doing this will result in the server never shutting down or connections being +// abruptly stopped. +// +// See the Provider.Shutdown documentation for information on context usage and errors. +func (s *Server) Shutdown(ctx context.Context) error { + s.init() + return s.provider.Shutdown(ctx) +} + +func (s *Server) init() { + s.initDone.Do(func() { + s.provider = s.Provider + if s.provider == nil { + s.provider = &Joe{} + } + }) +} + +func (s *Server) getSubscription(sess *Session) (Subscription, bool) { + sub := Subscription{Client: sess, LastEventID: sess.LastEventID, Topics: defaultTopicSlice} + if s.OnSession != nil { + topics, ok := s.OnSession(sess.Res, sess.Req) + if ok && len(topics) > 0 { + sub.Topics = topics + } + + return sub, ok + } + + return sub, true +} + +var defaultTopicSlice = []string{DefaultTopic} + +func getTopics(initial []string) []string { + if len(initial) == 0 { + return defaultTopicSlice + } + + return initial +} diff --git a/vendor/github.com/tmaxmax/go-sse/session.go b/vendor/github.com/tmaxmax/go-sse/session.go new file mode 100644 index 0000000000..2dcc319184 --- /dev/null +++ b/vendor/github.com/tmaxmax/go-sse/session.go @@ -0,0 +1,160 @@ +package sse + +import ( + "errors" + "net/http" +) + +// ResponseWriter is a http.ResponseWriter augmented with a Flush method. +type ResponseWriter interface { + http.ResponseWriter + Flush() error +} + +// MessageWriter is a special kind of response writer used by providers to +// send Messages to clients. +type MessageWriter interface { + // Send sends the message to the client. + // To make sure it is sent, call Flush. + Send(m *Message) error + // Flush sends any buffered messages to the client. + Flush() error +} + +// A Session is an HTTP request from an SSE client. +// Create one using the Upgrade function. +// +// Using a Session you can also access the initial HTTP request, +// get the last event ID, or write data to the client. +type Session struct { + // The response writer for the request. Can be used to write an error response + // back to the client. Must not be used after the Session was subscribed! + Res ResponseWriter + // The initial HTTP request. Can be used to retrieve authentication data, + // topics, or data from context – a logger, for example. + Req *http.Request + // Last event ID of the client. It is unset if no ID was provided in the Last-Event-Id + // request header. + LastEventID EventID + + didUpgrade bool +} + +// Send sends the given event to the client. It returns any errors that occurred while writing the event. +func (s *Session) Send(e *Message) error { + if err := s.doUpgrade(); err != nil { + return err + } + if _, err := e.WriteTo(s.Res); err != nil { + return err + } + return nil +} + +// Flush sends any buffered messages to the client. +func (s *Session) Flush() error { + prevDidUpgrade := s.didUpgrade + if err := s.doUpgrade(); err != nil { + return err + } + if prevDidUpgrade == s.didUpgrade { + return s.Res.Flush() + } + return nil +} + +func (s *Session) doUpgrade() error { + if !s.didUpgrade { + s.Res.Header()[headerContentType] = headerContentTypeValue + if err := s.Res.Flush(); err != nil { + return err + } + s.didUpgrade = true + } + return nil +} + +// Upgrade upgrades an HTTP request to support server-sent events. +// It returns a Session that's used to send events to the client, or an +// error if the upgrade failed. +// +// The headers required by the SSE protocol are only sent when calling +// the Send method for the first time. If other operations are done before +// sending messages, other headers and status codes can safely be set. +func Upgrade(w http.ResponseWriter, r *http.Request) (*Session, error) { + rw := getResponseWriter(w) + if rw == nil { + return nil, ErrUpgradeUnsupported + } + + id := EventID{} + // Clients must not send empty Last-Event-Id headers: + // https://html.spec.whatwg.org/multipage/server-sent-events.html#sse-processing-model + if h := r.Header[headerLastEventID]; len(h) != 0 && h[0] != "" { + // We ignore the validity flag because if the given ID is invalid then an unset ID will be returned, + // which providers are required to ignore. + id, _ = NewID(h[0]) + } + + return &Session{Req: r, Res: rw, LastEventID: id}, nil +} + +// ErrUpgradeUnsupported is returned when a request can't be upgraded to support server-sent events. +var ErrUpgradeUnsupported = errors.New("go-sse.server: upgrade unsupported") + +// Canonicalized header keys. +const ( + headerLastEventID = "Last-Event-Id" + headerContentType = "Content-Type" +) + +// Pre-allocated header value. +var headerContentTypeValue = []string{"text/event-stream"} + +// Logic below is similar to Go 1.20's ResponseController. +// We can't use that because we need to check if the request supports +// flushing messages before we subscribe it to the event stream. + +type writeFlusher interface { + http.ResponseWriter + http.Flusher +} + +type writeFlusherError interface { + http.ResponseWriter + FlushError() error +} + +type rwUnwrapper interface { + Unwrap() http.ResponseWriter +} + +func getResponseWriter(w http.ResponseWriter) ResponseWriter { + for { + switch v := w.(type) { + case writeFlusherError: + return flusherErrorWrapper{v} + case writeFlusher: + return flusherWrapper{v} + case rwUnwrapper: + w = v.Unwrap() + default: + return nil + } + } +} + +type flusherWrapper struct { + writeFlusher +} + +func (f flusherWrapper) Flush() error { + f.writeFlusher.Flush() + return nil +} + +type flusherErrorWrapper struct { + writeFlusherError +} + +func (f flusherErrorWrapper) Flush() error { return f.FlushError() } diff --git a/vendor/gopkg.in/cenkalti/backoff.v1/.gitignore b/vendor/gopkg.in/cenkalti/backoff.v1/.gitignore deleted file mode 100644 index 00268614f0..0000000000 --- a/vendor/gopkg.in/cenkalti/backoff.v1/.gitignore +++ /dev/null @@ -1,22 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe diff --git a/vendor/gopkg.in/cenkalti/backoff.v1/.travis.yml b/vendor/gopkg.in/cenkalti/backoff.v1/.travis.yml deleted file mode 100644 index 1040404bfb..0000000000 --- a/vendor/gopkg.in/cenkalti/backoff.v1/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: go -go: - - 1.3.3 - - tip -before_install: - - go get github.com/mattn/goveralls - - go get golang.org/x/tools/cmd/cover -script: - - $HOME/gopath/bin/goveralls -service=travis-ci diff --git a/vendor/gopkg.in/cenkalti/backoff.v1/LICENSE b/vendor/gopkg.in/cenkalti/backoff.v1/LICENSE deleted file mode 100644 index 89b8179965..0000000000 --- a/vendor/gopkg.in/cenkalti/backoff.v1/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Cenk Altı - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/gopkg.in/cenkalti/backoff.v1/README.md b/vendor/gopkg.in/cenkalti/backoff.v1/README.md deleted file mode 100644 index 13b347fb95..0000000000 --- a/vendor/gopkg.in/cenkalti/backoff.v1/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Exponential Backoff [![GoDoc][godoc image]][godoc] [![Build Status][travis image]][travis] [![Coverage Status][coveralls image]][coveralls] - -This is a Go port of the exponential backoff algorithm from [Google's HTTP Client Library for Java][google-http-java-client]. - -[Exponential backoff][exponential backoff wiki] -is an algorithm that uses feedback to multiplicatively decrease the rate of some process, -in order to gradually find an acceptable rate. -The retries exponentially increase and stop increasing when a certain threshold is met. - -## Usage - -See https://godoc.org/github.com/cenkalti/backoff#pkg-examples - -## Contributing - -* I would like to keep this library as small as possible. -* Please don't send a PR without opening an issue and discussing it first. -* If proposed change is not a common use case, I will probably not accept it. - -[godoc]: https://godoc.org/github.com/cenkalti/backoff -[godoc image]: https://godoc.org/github.com/cenkalti/backoff?status.png -[travis]: https://travis-ci.org/cenkalti/backoff -[travis image]: https://travis-ci.org/cenkalti/backoff.png?branch=master -[coveralls]: https://coveralls.io/github/cenkalti/backoff?branch=master -[coveralls image]: https://coveralls.io/repos/github/cenkalti/backoff/badge.svg?branch=master - -[google-http-java-client]: https://github.com/google/google-http-java-client -[exponential backoff wiki]: http://en.wikipedia.org/wiki/Exponential_backoff - -[advanced example]: https://godoc.org/github.com/cenkalti/backoff#example_ diff --git a/vendor/gopkg.in/cenkalti/backoff.v1/backoff.go b/vendor/gopkg.in/cenkalti/backoff.v1/backoff.go deleted file mode 100644 index 2102c5f2de..0000000000 --- a/vendor/gopkg.in/cenkalti/backoff.v1/backoff.go +++ /dev/null @@ -1,66 +0,0 @@ -// Package backoff implements backoff algorithms for retrying operations. -// -// Use Retry function for retrying operations that may fail. -// If Retry does not meet your needs, -// copy/paste the function into your project and modify as you wish. -// -// There is also Ticker type similar to time.Ticker. -// You can use it if you need to work with channels. -// -// See Examples section below for usage examples. -package backoff - -import "time" - -// BackOff is a backoff policy for retrying an operation. -type BackOff interface { - // NextBackOff returns the duration to wait before retrying the operation, - // or backoff.Stop to indicate that no more retries should be made. - // - // Example usage: - // - // duration := backoff.NextBackOff(); - // if (duration == backoff.Stop) { - // // Do not retry operation. - // } else { - // // Sleep for duration and retry operation. - // } - // - NextBackOff() time.Duration - - // Reset to initial state. - Reset() -} - -// Stop indicates that no more retries should be made for use in NextBackOff(). -const Stop time.Duration = -1 - -// ZeroBackOff is a fixed backoff policy whose backoff time is always zero, -// meaning that the operation is retried immediately without waiting, indefinitely. -type ZeroBackOff struct{} - -func (b *ZeroBackOff) Reset() {} - -func (b *ZeroBackOff) NextBackOff() time.Duration { return 0 } - -// StopBackOff is a fixed backoff policy that always returns backoff.Stop for -// NextBackOff(), meaning that the operation should never be retried. -type StopBackOff struct{} - -func (b *StopBackOff) Reset() {} - -func (b *StopBackOff) NextBackOff() time.Duration { return Stop } - -// ConstantBackOff is a backoff policy that always returns the same backoff delay. -// This is in contrast to an exponential backoff policy, -// which returns a delay that grows longer as you call NextBackOff() over and over again. -type ConstantBackOff struct { - Interval time.Duration -} - -func (b *ConstantBackOff) Reset() {} -func (b *ConstantBackOff) NextBackOff() time.Duration { return b.Interval } - -func NewConstantBackOff(d time.Duration) *ConstantBackOff { - return &ConstantBackOff{Interval: d} -} diff --git a/vendor/gopkg.in/cenkalti/backoff.v1/context.go b/vendor/gopkg.in/cenkalti/backoff.v1/context.go deleted file mode 100644 index 5d15709254..0000000000 --- a/vendor/gopkg.in/cenkalti/backoff.v1/context.go +++ /dev/null @@ -1,60 +0,0 @@ -package backoff - -import ( - "time" - - "golang.org/x/net/context" -) - -// BackOffContext is a backoff policy that stops retrying after the context -// is canceled. -type BackOffContext interface { - BackOff - Context() context.Context -} - -type backOffContext struct { - BackOff - ctx context.Context -} - -// WithContext returns a BackOffContext with context ctx -// -// ctx must not be nil -func WithContext(b BackOff, ctx context.Context) BackOffContext { - if ctx == nil { - panic("nil context") - } - - if b, ok := b.(*backOffContext); ok { - return &backOffContext{ - BackOff: b.BackOff, - ctx: ctx, - } - } - - return &backOffContext{ - BackOff: b, - ctx: ctx, - } -} - -func ensureContext(b BackOff) BackOffContext { - if cb, ok := b.(BackOffContext); ok { - return cb - } - return WithContext(b, context.Background()) -} - -func (b *backOffContext) Context() context.Context { - return b.ctx -} - -func (b *backOffContext) NextBackOff() time.Duration { - select { - case <-b.Context().Done(): - return Stop - default: - return b.BackOff.NextBackOff() - } -} diff --git a/vendor/gopkg.in/cenkalti/backoff.v1/exponential.go b/vendor/gopkg.in/cenkalti/backoff.v1/exponential.go deleted file mode 100644 index 9a6addf075..0000000000 --- a/vendor/gopkg.in/cenkalti/backoff.v1/exponential.go +++ /dev/null @@ -1,156 +0,0 @@ -package backoff - -import ( - "math/rand" - "time" -) - -/* -ExponentialBackOff is a backoff implementation that increases the backoff -period for each retry attempt using a randomization function that grows exponentially. - -NextBackOff() is calculated using the following formula: - - randomized interval = - RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor]) - -In other words NextBackOff() will range between the randomization factor -percentage below and above the retry interval. - -For example, given the following parameters: - - RetryInterval = 2 - RandomizationFactor = 0.5 - Multiplier = 2 - -the actual backoff period used in the next retry attempt will range between 1 and 3 seconds, -multiplied by the exponential, that is, between 2 and 6 seconds. - -Note: MaxInterval caps the RetryInterval and not the randomized interval. - -If the time elapsed since an ExponentialBackOff instance is created goes past the -MaxElapsedTime, then the method NextBackOff() starts returning backoff.Stop. - -The elapsed time can be reset by calling Reset(). - -Example: Given the following default arguments, for 10 tries the sequence will be, -and assuming we go over the MaxElapsedTime on the 10th try: - - Request # RetryInterval (seconds) Randomized Interval (seconds) - - 1 0.5 [0.25, 0.75] - 2 0.75 [0.375, 1.125] - 3 1.125 [0.562, 1.687] - 4 1.687 [0.8435, 2.53] - 5 2.53 [1.265, 3.795] - 6 3.795 [1.897, 5.692] - 7 5.692 [2.846, 8.538] - 8 8.538 [4.269, 12.807] - 9 12.807 [6.403, 19.210] - 10 19.210 backoff.Stop - -Note: Implementation is not thread-safe. -*/ -type ExponentialBackOff struct { - InitialInterval time.Duration - RandomizationFactor float64 - Multiplier float64 - MaxInterval time.Duration - // After MaxElapsedTime the ExponentialBackOff stops. - // It never stops if MaxElapsedTime == 0. - MaxElapsedTime time.Duration - Clock Clock - - currentInterval time.Duration - startTime time.Time - random *rand.Rand -} - -// Clock is an interface that returns current time for BackOff. -type Clock interface { - Now() time.Time -} - -// Default values for ExponentialBackOff. -const ( - DefaultInitialInterval = 500 * time.Millisecond - DefaultRandomizationFactor = 0.5 - DefaultMultiplier = 1.5 - DefaultMaxInterval = 60 * time.Second - DefaultMaxElapsedTime = 15 * time.Minute -) - -// NewExponentialBackOff creates an instance of ExponentialBackOff using default values. -func NewExponentialBackOff() *ExponentialBackOff { - b := &ExponentialBackOff{ - InitialInterval: DefaultInitialInterval, - RandomizationFactor: DefaultRandomizationFactor, - Multiplier: DefaultMultiplier, - MaxInterval: DefaultMaxInterval, - MaxElapsedTime: DefaultMaxElapsedTime, - Clock: SystemClock, - random: rand.New(rand.NewSource(time.Now().UnixNano())), - } - b.Reset() - return b -} - -type systemClock struct{} - -func (t systemClock) Now() time.Time { - return time.Now() -} - -// SystemClock implements Clock interface that uses time.Now(). -var SystemClock = systemClock{} - -// Reset the interval back to the initial retry interval and restarts the timer. -func (b *ExponentialBackOff) Reset() { - b.currentInterval = b.InitialInterval - b.startTime = b.Clock.Now() -} - -// NextBackOff calculates the next backoff interval using the formula: -// Randomized interval = RetryInterval +/- (RandomizationFactor * RetryInterval) -func (b *ExponentialBackOff) NextBackOff() time.Duration { - // Make sure we have not gone over the maximum elapsed time. - if b.MaxElapsedTime != 0 && b.GetElapsedTime() > b.MaxElapsedTime { - return Stop - } - defer b.incrementCurrentInterval() - if b.random == nil { - b.random = rand.New(rand.NewSource(time.Now().UnixNano())) - } - return getRandomValueFromInterval(b.RandomizationFactor, b.random.Float64(), b.currentInterval) -} - -// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance -// is created and is reset when Reset() is called. -// -// The elapsed time is computed using time.Now().UnixNano(). -func (b *ExponentialBackOff) GetElapsedTime() time.Duration { - return b.Clock.Now().Sub(b.startTime) -} - -// Increments the current interval by multiplying it with the multiplier. -func (b *ExponentialBackOff) incrementCurrentInterval() { - // Check for overflow, if overflow is detected set the current interval to the max interval. - if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier { - b.currentInterval = b.MaxInterval - } else { - b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier) - } -} - -// Returns a random value from the following interval: -// [randomizationFactor * currentInterval, randomizationFactor * currentInterval]. -func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration { - var delta = randomizationFactor * float64(currentInterval) - var minInterval = float64(currentInterval) - delta - var maxInterval = float64(currentInterval) + delta - - // Get a random value from the range [minInterval, maxInterval]. - // The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then - // we want a 33% chance for selecting either 1, 2 or 3. - return time.Duration(minInterval + (random * (maxInterval - minInterval + 1))) -} diff --git a/vendor/gopkg.in/cenkalti/backoff.v1/retry.go b/vendor/gopkg.in/cenkalti/backoff.v1/retry.go deleted file mode 100644 index 5dbd825b5c..0000000000 --- a/vendor/gopkg.in/cenkalti/backoff.v1/retry.go +++ /dev/null @@ -1,78 +0,0 @@ -package backoff - -import "time" - -// An Operation is executing by Retry() or RetryNotify(). -// The operation will be retried using a backoff policy if it returns an error. -type Operation func() error - -// Notify is a notify-on-error function. It receives an operation error and -// backoff delay if the operation failed (with an error). -// -// NOTE that if the backoff policy stated to stop retrying, -// the notify function isn't called. -type Notify func(error, time.Duration) - -// Retry the operation o until it does not return error or BackOff stops. -// o is guaranteed to be run at least once. -// It is the caller's responsibility to reset b after Retry returns. -// -// If o returns a *PermanentError, the operation is not retried, and the -// wrapped error is returned. -// -// Retry sleeps the goroutine for the duration returned by BackOff after a -// failed operation returns. -func Retry(o Operation, b BackOff) error { return RetryNotify(o, b, nil) } - -// RetryNotify calls notify function with the error and wait duration -// for each failed attempt before sleep. -func RetryNotify(operation Operation, b BackOff, notify Notify) error { - var err error - var next time.Duration - - cb := ensureContext(b) - - b.Reset() - for { - if err = operation(); err == nil { - return nil - } - - if permanent, ok := err.(*PermanentError); ok { - return permanent.Err - } - - if next = b.NextBackOff(); next == Stop { - return err - } - - if notify != nil { - notify(err, next) - } - - t := time.NewTimer(next) - - select { - case <-cb.Context().Done(): - t.Stop() - return err - case <-t.C: - } - } -} - -// PermanentError signals that the operation should not be retried. -type PermanentError struct { - Err error -} - -func (e *PermanentError) Error() string { - return e.Err.Error() -} - -// Permanent wraps the given err in a *PermanentError. -func Permanent(err error) *PermanentError { - return &PermanentError{ - Err: err, - } -} diff --git a/vendor/gopkg.in/cenkalti/backoff.v1/ticker.go b/vendor/gopkg.in/cenkalti/backoff.v1/ticker.go deleted file mode 100644 index 49a99718d7..0000000000 --- a/vendor/gopkg.in/cenkalti/backoff.v1/ticker.go +++ /dev/null @@ -1,81 +0,0 @@ -package backoff - -import ( - "runtime" - "sync" - "time" -) - -// Ticker holds a channel that delivers `ticks' of a clock at times reported by a BackOff. -// -// Ticks will continue to arrive when the previous operation is still running, -// so operations that take a while to fail could run in quick succession. -type Ticker struct { - C <-chan time.Time - c chan time.Time - b BackOffContext - stop chan struct{} - stopOnce sync.Once -} - -// NewTicker returns a new Ticker containing a channel that will send the time at times -// specified by the BackOff argument. Ticker is guaranteed to tick at least once. -// The channel is closed when Stop method is called or BackOff stops. -func NewTicker(b BackOff) *Ticker { - c := make(chan time.Time) - t := &Ticker{ - C: c, - c: c, - b: ensureContext(b), - stop: make(chan struct{}), - } - go t.run() - runtime.SetFinalizer(t, (*Ticker).Stop) - return t -} - -// Stop turns off a ticker. After Stop, no more ticks will be sent. -func (t *Ticker) Stop() { - t.stopOnce.Do(func() { close(t.stop) }) -} - -func (t *Ticker) run() { - c := t.c - defer close(c) - t.b.Reset() - - // Ticker is guaranteed to tick at least once. - afterC := t.send(time.Now()) - - for { - if afterC == nil { - return - } - - select { - case tick := <-afterC: - afterC = t.send(tick) - case <-t.stop: - t.c = nil // Prevent future ticks from being sent to the channel. - return - case <-t.b.Context().Done(): - return - } - } -} - -func (t *Ticker) send(tick time.Time) <-chan time.Time { - select { - case t.c <- tick: - case <-t.stop: - return nil - } - - next := t.b.NextBackOff() - if next == Stop { - t.Stop() - return nil - } - - return time.After(next) -} diff --git a/vendor/gopkg.in/cenkalti/backoff.v1/tries.go b/vendor/gopkg.in/cenkalti/backoff.v1/tries.go deleted file mode 100644 index d2da7308b6..0000000000 --- a/vendor/gopkg.in/cenkalti/backoff.v1/tries.go +++ /dev/null @@ -1,35 +0,0 @@ -package backoff - -import "time" - -/* -WithMaxTries creates a wrapper around another BackOff, which will -return Stop if NextBackOff() has been called too many times since -the last time Reset() was called - -Note: Implementation is not thread-safe. -*/ -func WithMaxTries(b BackOff, max uint64) BackOff { - return &backOffTries{delegate: b, maxTries: max} -} - -type backOffTries struct { - delegate BackOff - maxTries uint64 - numTries uint64 -} - -func (b *backOffTries) NextBackOff() time.Duration { - if b.maxTries > 0 { - if b.maxTries <= b.numTries { - return Stop - } - b.numTries++ - } - return b.delegate.NextBackOff() -} - -func (b *backOffTries) Reset() { - b.numTries = 0 - b.delegate.Reset() -} diff --git a/vendor/modules.txt b/vendor/modules.txt index d2311fbe2f..231bbe4975 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1854,9 +1854,6 @@ github.com/prometheus/procfs/internal/util github.com/prometheus/statsd_exporter/pkg/level github.com/prometheus/statsd_exporter/pkg/mapper github.com/prometheus/statsd_exporter/pkg/mapper/fsm -# github.com/r3labs/sse/v2 v2.10.0 -## explicit; go 1.13 -github.com/r3labs/sse/v2 # github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 ## explicit github.com/rcrowley/go-metrics @@ -2114,6 +2111,10 @@ github.com/tklauser/go-sysconf # github.com/tklauser/numcpus v0.11.0 ## explicit; go 1.24.0 github.com/tklauser/numcpus +# github.com/tmaxmax/go-sse v0.11.0 +## explicit; go 1.22 +github.com/tmaxmax/go-sse +github.com/tmaxmax/go-sse/internal/parser # github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 ## explicit github.com/toorop/go-dkim @@ -2702,9 +2703,6 @@ google.golang.org/protobuf/types/known/fieldmaskpb google.golang.org/protobuf/types/known/structpb google.golang.org/protobuf/types/known/timestamppb google.golang.org/protobuf/types/known/wrapperspb -# gopkg.in/cenkalti/backoff.v1 v1.1.0 -## explicit -gopkg.in/cenkalti/backoff.v1 # gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 ## explicit gopkg.in/tomb.v1 From df057e671f22268cbbaa06243181d38ad08ebe99 Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Thu, 9 Apr 2026 11:31:42 +0200 Subject: [PATCH 2/4] feat(web): introduce dynamic theme management via RPC adds support for adding, removing, and checking themes at runtime. replaces static themes with a shared storage-based (cs3 system-storage) solution suitable for distributed systems. --- pkg/x/io/fsx/cs3/metadata/file.go | 89 ++++ pkg/x/io/fsx/cs3/metadata/file_info.go | 59 +++ pkg/x/io/fsx/cs3/metadata/fs.go | 96 ++++ pkg/x/io/fsx/cs3/metadata/metadata.go | 9 + .../gen/opencloud/services/web/v0/web.pb.go | 499 ++++++++++++++++++ .../opencloud/services/web/v0/web.pb.micro.go | 122 +++++ .../services/web/v0/web.swagger.json | 80 +++ protogen/proto/buf.gen.yaml | 1 + .../proto/opencloud/services/web/v0/web.proto | 58 ++ services/web/Makefile | 7 + services/web/pkg/command/server.go | 23 + services/web/pkg/config/config.go | 22 +- .../web/pkg/config/defaults/defaultconfig.go | 28 +- services/web/pkg/config/grpc.go | 11 + services/web/pkg/fs/fs.go | 52 ++ services/web/pkg/server/grpc/option.go | 94 ++++ services/web/pkg/server/grpc/server.go | 67 +++ services/web/pkg/server/http/server.go | 14 +- services/web/pkg/service/grpc/v0/option.go | 68 +++ services/web/pkg/service/grpc/v0/service.go | 67 +++ services/web/pkg/service/v0/service.go | 89 ++-- services/web/pkg/theme/http.go | 72 +++ services/web/pkg/theme/http_test.go | 57 ++ services/web/pkg/theme/kv.go | 64 +-- services/web/pkg/theme/kv_test.go | 137 +---- services/web/pkg/theme/service.go | 186 +++---- services/web/pkg/theme/service_test.go | 51 +- services/web/pkg/theme/theme.go | 6 +- 28 files changed, 1700 insertions(+), 428 deletions(-) create mode 100644 pkg/x/io/fsx/cs3/metadata/file.go create mode 100644 pkg/x/io/fsx/cs3/metadata/file_info.go create mode 100644 pkg/x/io/fsx/cs3/metadata/fs.go create mode 100644 pkg/x/io/fsx/cs3/metadata/metadata.go create mode 100644 protogen/gen/opencloud/services/web/v0/web.pb.go create mode 100644 protogen/gen/opencloud/services/web/v0/web.pb.micro.go create mode 100644 protogen/gen/opencloud/services/web/v0/web.swagger.json create mode 100644 protogen/proto/opencloud/services/web/v0/web.proto create mode 100644 services/web/pkg/config/grpc.go create mode 100644 services/web/pkg/fs/fs.go create mode 100644 services/web/pkg/server/grpc/option.go create mode 100644 services/web/pkg/server/grpc/server.go create mode 100644 services/web/pkg/service/grpc/v0/option.go create mode 100644 services/web/pkg/service/grpc/v0/service.go create mode 100644 services/web/pkg/theme/http.go create mode 100644 services/web/pkg/theme/http_test.go diff --git a/pkg/x/io/fsx/cs3/metadata/file.go b/pkg/x/io/fsx/cs3/metadata/file.go new file mode 100644 index 0000000000..4980058e7b --- /dev/null +++ b/pkg/x/io/fsx/cs3/metadata/file.go @@ -0,0 +1,89 @@ +package metadata + +import ( + "bytes" + "context" + "io" + "io/fs" + "os" +) + +type File struct { + name string + fs *Fs + fileMode os.FileMode + content []byte + resource io.ReadCloser +} + +func newFile(name string, fs *Fs, fileMode os.FileMode, content []byte) (*File, error) { + return &File{ + name: name, + fs: fs, + fileMode: fileMode, + content: content, + resource: io.NopCloser(bytes.NewBuffer(content)), + }, nil +} + +func (f *File) Close() error { + return f.resource.Close() +} + +func (f *File) Read(p []byte) (n int, err error) { + return f.resource.Read(p) +} + +func (f *File) ReadAt(p []byte, off int64) (n int, err error) { + readerAt, ok := f.resource.(io.ReaderAt) + if !ok { + return -1, &fs.PathError{Op: "ReadAt", Path: f.name, Err: ErrNotImplemented} + } + + return readerAt.ReadAt(p, off) +} + +func (f *File) Seek(offset int64, whence int) (int64, error) { + seeker, ok := f.resource.(io.Seeker) + if !ok { + return -1, &fs.PathError{Op: "Seek", Path: f.name, Err: ErrNotImplemented} + } + + return seeker.Seek(offset, whence) +} + +func (f *File) Write(p []byte) (n int, err error) { + return len(p), f.fs.storage.SimpleUpload(context.Background(), f.name, p) +} + +func (f *File) WriteAt(_ []byte, _ int64) (n int, err error) { + return -1, &fs.PathError{Op: "Write", Path: f.name, Err: ErrNotImplemented} +} + +func (f *File) Name() string { + return f.name +} + +func (f *File) Readdir(_ int) ([]os.FileInfo, error) { + return nil, &fs.PathError{Op: "Readdir", Path: f.name, Err: ErrNotImplemented} +} + +func (f *File) Readdirnames(_ int) ([]string, error) { + return nil, &fs.PathError{Op: "Readdirnames", Path: f.name, Err: ErrNotImplemented} +} + +func (f *File) Sync() error { + return nil +} + +func (f *File) Truncate(_ int64) error { + return &fs.PathError{Op: "Truncate", Path: f.name, Err: ErrNotImplemented} +} + +func (f *File) WriteString(_ string) (ret int, err error) { + return -1, &fs.PathError{Op: "WriteString", Path: f.name, Err: ErrNotImplemented} +} + +func (f *File) Stat() (os.FileInfo, error) { + return newFileInfo(f.name, f.fs, f.fileMode) +} diff --git a/pkg/x/io/fsx/cs3/metadata/file_info.go b/pkg/x/io/fsx/cs3/metadata/file_info.go new file mode 100644 index 0000000000..562ca4c193 --- /dev/null +++ b/pkg/x/io/fsx/cs3/metadata/file_info.go @@ -0,0 +1,59 @@ +package metadata + +import ( + "context" + "io/fs" + "os" + "time" + + providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + + "github.com/opencloud-eu/reva/v2/pkg/utils" +) + +type FileInfo struct { + name string + size int64 + modTime time.Time + isDir bool + mode os.FileMode +} + +func newFileInfo(name string, fs *Fs, fileMode os.FileMode) (*FileInfo, error) { + info, err := fs.storage.Stat(context.Background(), name) + if err != nil { + return nil, err + } + + return &FileInfo{ + name: info.GetName(), + size: int64(info.GetSize()), + modTime: utils.TSToTime(info.GetMtime()), + isDir: info.GetType() == providerv1beta1.ResourceType_RESOURCE_TYPE_CONTAINER, + mode: fileMode, + }, nil +} + +func (f *FileInfo) Name() string { + return f.name +} + +func (f *FileInfo) Size() int64 { + return f.size +} + +func (f *FileInfo) ModTime() time.Time { + return f.modTime +} + +func (f *FileInfo) IsDir() bool { + return f.isDir +} + +func (f *FileInfo) Mode() fs.FileMode { + return f.mode +} + +func (f *FileInfo) Sys() any { + return nil +} diff --git a/pkg/x/io/fsx/cs3/metadata/fs.go b/pkg/x/io/fsx/cs3/metadata/fs.go new file mode 100644 index 0000000000..c62337a1c3 --- /dev/null +++ b/pkg/x/io/fsx/cs3/metadata/fs.go @@ -0,0 +1,96 @@ +package metadata + +import ( + "context" + "fmt" + "io/fs" + "os" + "path" + "strings" + "syscall" + "time" + + "github.com/spf13/afero" + + revaMetadata "github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata" + "github.com/opencloud-eu/reva/v2/pkg/storage/utils/metadata" +) + +func NewMetadataFs(storage metadata.Storage) *Fs { + return &Fs{storage: storage} +} + +type Fs struct { + storage metadata.Storage +} + +func (fs *Fs) Create(_ string) (afero.File, error) { + return nil, syscall.EPERM +} + +func (fs *Fs) Mkdir(name string, _ os.FileMode) error { + return fs.storage.MakeDirIfNotExist(context.Background(), name) +} + +func (fs *Fs) MkdirAll(name string, _ os.FileMode) error { + paths := strings.Split(name, string(os.PathSeparator)) + // Create all parent directories if they do not exist + for i := 0; i <= len(paths)-1; i++ { + c := path.Join(paths[:i+1]...) + if err := fs.storage.MakeDirIfNotExist(context.Background(), c); err != nil { + return fmt.Errorf("failed to create directory %s: %w", c, err) + } + } + + return nil +} + +func (fs *Fs) Open(name string) (afero.File, error) { + return fs.OpenFile(name, os.O_RDONLY, 0) +} + +func (fs *Fs) OpenFile(name string, _ int, _ os.FileMode) (afero.File, error) { + res, err := fs.storage.Download(context.Background(), metadata.DownloadRequest{Path: name}) + if err != nil && !revaMetadata.IsNotExist(err) { + return nil, err + } + + var contend []byte + if res != nil { + contend = res.Content + } + + return newFile(name, fs, 0, contend) +} + +func (fs *Fs) Remove(name string) error { + return fs.RemoveAll(name) +} + +func (fs *Fs) RemoveAll(path string) error { + return fs.storage.Delete(context.Background(), path) +} + +func (fs *Fs) Rename(_, _ string) error { + return syscall.EPERM +} + +func (fs *Fs) Stat(name string) (fs.FileInfo, error) { + return newFileInfo(name, fs, 0) +} + +func (fs *Fs) Name() string { + return "MetadataFS" +} + +func (fs *Fs) Chmod(_ string, _ os.FileMode) error { + return syscall.EPERM +} + +func (fs *Fs) Chown(_ string, _, _ int) error { + return syscall.EPERM +} + +func (fs *Fs) Chtimes(_ string, _ time.Time, _ time.Time) error { + return syscall.EPERM +} diff --git a/pkg/x/io/fsx/cs3/metadata/metadata.go b/pkg/x/io/fsx/cs3/metadata/metadata.go new file mode 100644 index 0000000000..aecf62fdaf --- /dev/null +++ b/pkg/x/io/fsx/cs3/metadata/metadata.go @@ -0,0 +1,9 @@ +package metadata + +import ( + "errors" +) + +var ( + ErrNotImplemented = errors.New("not implemented") +) diff --git a/protogen/gen/opencloud/services/web/v0/web.pb.go b/protogen/gen/opencloud/services/web/v0/web.pb.go new file mode 100644 index 0000000000..badb1022d2 --- /dev/null +++ b/protogen/gen/opencloud/services/web/v0/web.pb.go @@ -0,0 +1,499 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc (unknown) +// source: opencloud/services/web/v0/web.proto + +package v0 + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ThemeAddRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // The ID of the theme to add + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` // The theme data in bytes +} + +func (x *ThemeAddRequest) Reset() { + *x = ThemeAddRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_opencloud_services_web_v0_web_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ThemeAddRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ThemeAddRequest) ProtoMessage() {} + +func (x *ThemeAddRequest) ProtoReflect() protoreflect.Message { + mi := &file_opencloud_services_web_v0_web_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ThemeAddRequest.ProtoReflect.Descriptor instead. +func (*ThemeAddRequest) Descriptor() ([]byte, []int) { + return file_opencloud_services_web_v0_web_proto_rawDescGZIP(), []int{0} +} + +func (x *ThemeAddRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *ThemeAddRequest) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +type ThemeAddResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ThemeAddResponse) Reset() { + *x = ThemeAddResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_opencloud_services_web_v0_web_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ThemeAddResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ThemeAddResponse) ProtoMessage() {} + +func (x *ThemeAddResponse) ProtoReflect() protoreflect.Message { + mi := &file_opencloud_services_web_v0_web_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ThemeAddResponse.ProtoReflect.Descriptor instead. +func (*ThemeAddResponse) Descriptor() ([]byte, []int) { + return file_opencloud_services_web_v0_web_proto_rawDescGZIP(), []int{1} +} + +type ThemeExistsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // The ID of the theme to check +} + +func (x *ThemeExistsRequest) Reset() { + *x = ThemeExistsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_opencloud_services_web_v0_web_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ThemeExistsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ThemeExistsRequest) ProtoMessage() {} + +func (x *ThemeExistsRequest) ProtoReflect() protoreflect.Message { + mi := &file_opencloud_services_web_v0_web_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ThemeExistsRequest.ProtoReflect.Descriptor instead. +func (*ThemeExistsRequest) Descriptor() ([]byte, []int) { + return file_opencloud_services_web_v0_web_proto_rawDescGZIP(), []int{2} +} + +func (x *ThemeExistsRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type ThemeExistsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Exists bool `protobuf:"varint,1,opt,name=exists,proto3" json:"exists,omitempty"` // Indicates if the theme exists +} + +func (x *ThemeExistsResponse) Reset() { + *x = ThemeExistsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_opencloud_services_web_v0_web_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ThemeExistsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ThemeExistsResponse) ProtoMessage() {} + +func (x *ThemeExistsResponse) ProtoReflect() protoreflect.Message { + mi := &file_opencloud_services_web_v0_web_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ThemeExistsResponse.ProtoReflect.Descriptor instead. +func (*ThemeExistsResponse) Descriptor() ([]byte, []int) { + return file_opencloud_services_web_v0_web_proto_rawDescGZIP(), []int{3} +} + +func (x *ThemeExistsResponse) GetExists() bool { + if x != nil { + return x.Exists + } + return false +} + +type ThemeRemoveRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // The ID of the theme to remove +} + +func (x *ThemeRemoveRequest) Reset() { + *x = ThemeRemoveRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_opencloud_services_web_v0_web_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ThemeRemoveRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ThemeRemoveRequest) ProtoMessage() {} + +func (x *ThemeRemoveRequest) ProtoReflect() protoreflect.Message { + mi := &file_opencloud_services_web_v0_web_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ThemeRemoveRequest.ProtoReflect.Descriptor instead. +func (*ThemeRemoveRequest) Descriptor() ([]byte, []int) { + return file_opencloud_services_web_v0_web_proto_rawDescGZIP(), []int{4} +} + +func (x *ThemeRemoveRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type ThemeRemoveResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ThemeRemoveResponse) Reset() { + *x = ThemeRemoveResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_opencloud_services_web_v0_web_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ThemeRemoveResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ThemeRemoveResponse) ProtoMessage() {} + +func (x *ThemeRemoveResponse) ProtoReflect() protoreflect.Message { + mi := &file_opencloud_services_web_v0_web_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ThemeRemoveResponse.ProtoReflect.Descriptor instead. +func (*ThemeRemoveResponse) Descriptor() ([]byte, []int) { + return file_opencloud_services_web_v0_web_proto_rawDescGZIP(), []int{5} +} + +var File_opencloud_services_web_v0_web_proto protoreflect.FileDescriptor + +var file_opencloud_services_web_v0_web_proto_rawDesc = []byte{ + 0x0a, 0x23, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x2f, 0x77, 0x65, 0x62, 0x2f, 0x76, 0x30, 0x2f, 0x77, 0x65, 0x62, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x65, 0x62, 0x2e, 0x76, 0x30, + 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, + 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0x35, 0x0a, 0x0f, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x12, 0x0a, 0x10, 0x54, 0x68, 0x65, 0x6d, 0x65, + 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x0a, 0x12, 0x54, + 0x68, 0x65, 0x6d, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x22, 0x2d, 0x0a, 0x13, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x69, 0x73, + 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, + 0x22, 0x24, 0x0a, 0x12, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x52, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xcd, 0x02, + 0x0a, 0x0a, 0x57, 0x65, 0x62, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x63, 0x0a, 0x08, + 0x54, 0x68, 0x65, 0x6d, 0x65, 0x41, 0x64, 0x64, 0x12, 0x2a, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, + 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x65, + 0x62, 0x2e, 0x76, 0x30, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x65, 0x62, 0x2e, 0x76, 0x30, + 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x6c, 0x0a, 0x0b, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, + 0x12, 0x2d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x65, 0x62, 0x2e, 0x76, 0x30, 0x2e, 0x54, 0x68, 0x65, + 0x6d, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2e, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x2e, 0x77, 0x65, 0x62, 0x2e, 0x76, 0x30, 0x2e, 0x54, 0x68, 0x65, 0x6d, + 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x6c, 0x0a, 0x0b, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x12, 0x2d, + 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x77, 0x65, 0x62, 0x2e, 0x76, 0x30, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, + 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x2e, 0x77, 0x65, 0x62, 0x2e, 0x76, 0x30, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x52, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0xea, 0x02, + 0x5a, 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, + 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2d, 0x65, 0x75, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, + 0x6f, 0x75, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x65, 0x6e, + 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2f, 0x77, 0x65, 0x62, 0x2f, 0x76, 0x30, 0x92, 0x41, 0x9c, 0x02, 0x12, 0xb4, + 0x01, 0x0a, 0x0d, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x77, 0x65, 0x62, + 0x22, 0x51, 0x0a, 0x0e, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x47, 0x6d, + 0x62, 0x48, 0x12, 0x29, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, + 0x2d, 0x65, 0x75, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x1a, 0x14, 0x73, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, + 0x2e, 0x65, 0x75, 0x2a, 0x49, 0x0a, 0x0a, 0x41, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2d, 0x32, 0x2e, + 0x30, 0x12, 0x3b, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2d, + 0x65, 0x75, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x62, 0x6c, 0x6f, + 0x62, 0x2f, 0x6d, 0x61, 0x69, 0x6e, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, + 0x31, 0x2e, 0x30, 0x2e, 0x30, 0x2a, 0x02, 0x01, 0x02, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x72, 0x3b, 0x0a, + 0x10, 0x44, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x75, 0x61, + 0x6c, 0x12, 0x27, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x64, 0x6f, 0x63, 0x73, 0x2e, + 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x65, 0x75, 0x2f, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x77, 0x65, 0x62, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_opencloud_services_web_v0_web_proto_rawDescOnce sync.Once + file_opencloud_services_web_v0_web_proto_rawDescData = file_opencloud_services_web_v0_web_proto_rawDesc +) + +func file_opencloud_services_web_v0_web_proto_rawDescGZIP() []byte { + file_opencloud_services_web_v0_web_proto_rawDescOnce.Do(func() { + file_opencloud_services_web_v0_web_proto_rawDescData = protoimpl.X.CompressGZIP(file_opencloud_services_web_v0_web_proto_rawDescData) + }) + return file_opencloud_services_web_v0_web_proto_rawDescData +} + +var file_opencloud_services_web_v0_web_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_opencloud_services_web_v0_web_proto_goTypes = []interface{}{ + (*ThemeAddRequest)(nil), // 0: opencloud.services.web.v0.ThemeAddRequest + (*ThemeAddResponse)(nil), // 1: opencloud.services.web.v0.ThemeAddResponse + (*ThemeExistsRequest)(nil), // 2: opencloud.services.web.v0.ThemeExistsRequest + (*ThemeExistsResponse)(nil), // 3: opencloud.services.web.v0.ThemeExistsResponse + (*ThemeRemoveRequest)(nil), // 4: opencloud.services.web.v0.ThemeRemoveRequest + (*ThemeRemoveResponse)(nil), // 5: opencloud.services.web.v0.ThemeRemoveResponse +} +var file_opencloud_services_web_v0_web_proto_depIdxs = []int32{ + 0, // 0: opencloud.services.web.v0.WebService.ThemeAdd:input_type -> opencloud.services.web.v0.ThemeAddRequest + 2, // 1: opencloud.services.web.v0.WebService.ThemeExists:input_type -> opencloud.services.web.v0.ThemeExistsRequest + 4, // 2: opencloud.services.web.v0.WebService.ThemeRemove:input_type -> opencloud.services.web.v0.ThemeRemoveRequest + 1, // 3: opencloud.services.web.v0.WebService.ThemeAdd:output_type -> opencloud.services.web.v0.ThemeAddResponse + 3, // 4: opencloud.services.web.v0.WebService.ThemeExists:output_type -> opencloud.services.web.v0.ThemeExistsResponse + 5, // 5: opencloud.services.web.v0.WebService.ThemeRemove:output_type -> opencloud.services.web.v0.ThemeRemoveResponse + 3, // [3:6] is the sub-list for method output_type + 0, // [0:3] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_opencloud_services_web_v0_web_proto_init() } +func file_opencloud_services_web_v0_web_proto_init() { + if File_opencloud_services_web_v0_web_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_opencloud_services_web_v0_web_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ThemeAddRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opencloud_services_web_v0_web_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ThemeAddResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opencloud_services_web_v0_web_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ThemeExistsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opencloud_services_web_v0_web_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ThemeExistsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opencloud_services_web_v0_web_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ThemeRemoveRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opencloud_services_web_v0_web_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ThemeRemoveResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_opencloud_services_web_v0_web_proto_rawDesc, + NumEnums: 0, + NumMessages: 6, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_opencloud_services_web_v0_web_proto_goTypes, + DependencyIndexes: file_opencloud_services_web_v0_web_proto_depIdxs, + MessageInfos: file_opencloud_services_web_v0_web_proto_msgTypes, + }.Build() + File_opencloud_services_web_v0_web_proto = out.File + file_opencloud_services_web_v0_web_proto_rawDesc = nil + file_opencloud_services_web_v0_web_proto_goTypes = nil + file_opencloud_services_web_v0_web_proto_depIdxs = nil +} diff --git a/protogen/gen/opencloud/services/web/v0/web.pb.micro.go b/protogen/gen/opencloud/services/web/v0/web.pb.micro.go new file mode 100644 index 0000000000..4e351b033e --- /dev/null +++ b/protogen/gen/opencloud/services/web/v0/web.pb.micro.go @@ -0,0 +1,122 @@ +// Code generated by protoc-gen-micro. DO NOT EDIT. +// source: opencloud/services/web/v0/web.proto + +package v0 + +import ( + fmt "fmt" + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + proto "google.golang.org/protobuf/proto" + math "math" +) + +import ( + context "context" + api "go-micro.dev/v4/api" + client "go-micro.dev/v4/client" + server "go-micro.dev/v4/server" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// Reference imports to suppress errors if they are not otherwise used. +var _ api.Endpoint +var _ context.Context +var _ client.Option +var _ server.Option + +// Api Endpoints for WebService service + +func NewWebServiceEndpoints() []*api.Endpoint { + return []*api.Endpoint{} +} + +// Client API for WebService service + +type WebService interface { + ThemeAdd(ctx context.Context, in *ThemeAddRequest, opts ...client.CallOption) (*ThemeAddResponse, error) + ThemeExists(ctx context.Context, in *ThemeExistsRequest, opts ...client.CallOption) (*ThemeExistsResponse, error) + ThemeRemove(ctx context.Context, in *ThemeRemoveRequest, opts ...client.CallOption) (*ThemeRemoveResponse, error) +} + +type webService struct { + c client.Client + name string +} + +func NewWebService(name string, c client.Client) WebService { + return &webService{ + c: c, + name: name, + } +} + +func (c *webService) ThemeAdd(ctx context.Context, in *ThemeAddRequest, opts ...client.CallOption) (*ThemeAddResponse, error) { + req := c.c.NewRequest(c.name, "WebService.ThemeAdd", in) + out := new(ThemeAddResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *webService) ThemeExists(ctx context.Context, in *ThemeExistsRequest, opts ...client.CallOption) (*ThemeExistsResponse, error) { + req := c.c.NewRequest(c.name, "WebService.ThemeExists", in) + out := new(ThemeExistsResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *webService) ThemeRemove(ctx context.Context, in *ThemeRemoveRequest, opts ...client.CallOption) (*ThemeRemoveResponse, error) { + req := c.c.NewRequest(c.name, "WebService.ThemeRemove", in) + out := new(ThemeRemoveResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for WebService service + +type WebServiceHandler interface { + ThemeAdd(context.Context, *ThemeAddRequest, *ThemeAddResponse) error + ThemeExists(context.Context, *ThemeExistsRequest, *ThemeExistsResponse) error + ThemeRemove(context.Context, *ThemeRemoveRequest, *ThemeRemoveResponse) error +} + +func RegisterWebServiceHandler(s server.Server, hdlr WebServiceHandler, opts ...server.HandlerOption) error { + type webService interface { + ThemeAdd(ctx context.Context, in *ThemeAddRequest, out *ThemeAddResponse) error + ThemeExists(ctx context.Context, in *ThemeExistsRequest, out *ThemeExistsResponse) error + ThemeRemove(ctx context.Context, in *ThemeRemoveRequest, out *ThemeRemoveResponse) error + } + type WebService struct { + webService + } + h := &webServiceHandler{hdlr} + return s.Handle(s.NewHandler(&WebService{h}, opts...)) +} + +type webServiceHandler struct { + WebServiceHandler +} + +func (h *webServiceHandler) ThemeAdd(ctx context.Context, in *ThemeAddRequest, out *ThemeAddResponse) error { + return h.WebServiceHandler.ThemeAdd(ctx, in, out) +} + +func (h *webServiceHandler) ThemeExists(ctx context.Context, in *ThemeExistsRequest, out *ThemeExistsResponse) error { + return h.WebServiceHandler.ThemeExists(ctx, in, out) +} + +func (h *webServiceHandler) ThemeRemove(ctx context.Context, in *ThemeRemoveRequest, out *ThemeRemoveResponse) error { + return h.WebServiceHandler.ThemeRemove(ctx, in, out) +} diff --git a/protogen/gen/opencloud/services/web/v0/web.swagger.json b/protogen/gen/opencloud/services/web/v0/web.swagger.json new file mode 100644 index 0000000000..2e48770b69 --- /dev/null +++ b/protogen/gen/opencloud/services/web/v0/web.swagger.json @@ -0,0 +1,80 @@ +{ + "swagger": "2.0", + "info": { + "title": "OpenCloud web", + "version": "1.0.0", + "contact": { + "name": "OpenCloud GmbH", + "url": "https://github.com/opencloud-eu/opencloud", + "email": "support@opencloud.eu" + }, + "license": { + "name": "Apache-2.0", + "url": "https://github.com/opencloud-eu/opencloud/blob/main/LICENSE" + } + }, + "tags": [ + { + "name": "WebService" + } + ], + "schemes": [ + "http", + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": {}, + "definitions": { + "protobufAny": { + "type": "object", + "properties": { + "@type": { + "type": "string" + } + }, + "additionalProperties": {} + }, + "rpcStatus": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/protobufAny" + } + } + } + }, + "v0ThemeAddResponse": { + "type": "object" + }, + "v0ThemeExistsResponse": { + "type": "object", + "properties": { + "exists": { + "type": "boolean", + "title": "Indicates if the theme exists" + } + } + }, + "v0ThemeRemoveResponse": { + "type": "object" + } + }, + "externalDocs": { + "description": "Developer Manual", + "url": "https://docs.opencloud.eu/services/web/" + } +} diff --git a/protogen/proto/buf.gen.yaml b/protogen/proto/buf.gen.yaml index 1e7689dce4..adca608319 100644 --- a/protogen/proto/buf.gen.yaml +++ b/protogen/proto/buf.gen.yaml @@ -25,6 +25,7 @@ plugins: opencloud.services.eventhistory.v0;\ opencloud.messages.eventhistory.v0;\ opencloud.services.policies.v0;\ + opencloud.services.web.v0;\ opencloud.messages.policies.v0" - name: openapiv2 diff --git a/protogen/proto/opencloud/services/web/v0/web.proto b/protogen/proto/opencloud/services/web/v0/web.proto new file mode 100644 index 0000000000..46da4b2a57 --- /dev/null +++ b/protogen/proto/opencloud/services/web/v0/web.proto @@ -0,0 +1,58 @@ +syntax = "proto3"; + +package opencloud.services.web.v0; + +option go_package = "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/web/v0"; + +import "protoc-gen-openapiv2/options/annotations.proto"; + +option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { + info: { + title: "OpenCloud web"; + version: "1.0.0"; + contact: { + name: "OpenCloud GmbH"; + url: "https://github.com/opencloud-eu/opencloud"; + email: "support@opencloud.eu"; + }; + license: { + name: "Apache-2.0"; + url: "https://github.com/opencloud-eu/opencloud/blob/main/LICENSE"; + }; + }; + schemes: HTTP; + schemes: HTTPS; + consumes: "application/json"; + produces: "application/json"; + external_docs: { + description: "Developer Manual"; + url: "https://docs.opencloud.eu/services/web/"; + }; +}; + +service WebService { + rpc ThemeAdd(ThemeAddRequest) returns (ThemeAddResponse); + rpc ThemeExists(ThemeExistsRequest) returns (ThemeExistsResponse); + rpc ThemeRemove(ThemeRemoveRequest) returns (ThemeRemoveResponse); +} + +message ThemeAddRequest { + string id = 1; // The ID of the theme to add + bytes data = 2; // The theme data in bytes +} + +message ThemeAddResponse {} + +message ThemeExistsRequest { + string id = 1; // The ID of the theme to check +} + +message ThemeExistsResponse { + bool exists = 1; // Indicates if the theme exists +} + +message ThemeRemoveRequest { + string id = 1; // The ID of the theme to remove +} + +message ThemeRemoveResponse {} diff --git a/services/web/Makefile b/services/web/Makefile index 5c172a5e70..0ecba31b45 100644 --- a/services/web/Makefile +++ b/services/web/Makefile @@ -11,6 +11,13 @@ include ../../.make/default.mk include ../../.make/go.mk include ../../.make/release.mk include ../../.make/docs.mk +include ../../.make/protobuf.mk + +.PHONY: go-generate +go-generate: protobuf + +.PHONY: protobuf +protobuf: buf-generate .PHONY: node-generate-dev node-generate-dev: pull-assets diff --git a/services/web/pkg/command/server.go b/services/web/pkg/command/server.go index 38df9caec4..145decf00d 100644 --- a/services/web/pkg/command/server.go +++ b/services/web/pkg/command/server.go @@ -10,11 +10,13 @@ import ( "github.com/opencloud-eu/opencloud/pkg/config/configlog" "github.com/opencloud-eu/opencloud/pkg/log" "github.com/opencloud-eu/opencloud/pkg/runner" + ogrpc "github.com/opencloud-eu/opencloud/pkg/service/grpc" "github.com/opencloud-eu/opencloud/pkg/tracing" "github.com/opencloud-eu/opencloud/services/web/pkg/config" "github.com/opencloud-eu/opencloud/services/web/pkg/config/parser" "github.com/opencloud-eu/opencloud/services/web/pkg/metrics" "github.com/opencloud-eu/opencloud/services/web/pkg/server/debug" + "github.com/opencloud-eu/opencloud/services/web/pkg/server/grpc" "github.com/opencloud-eu/opencloud/services/web/pkg/server/http" "github.com/spf13/cobra" @@ -48,6 +50,10 @@ func Server(cfg *config.Config) *cobra.Command { } } + cfg.GrpcClient, err = ogrpc.NewClient( + append(ogrpc.GetClientOptions(cfg.GRPCClientTLS), ogrpc.WithTraceProvider(traceProvider))..., + ) + var cancel context.CancelFunc if cfg.Context == nil { cfg.Context, cancel = signal.NotifyContext(context.Background(), runner.StopSignals...) @@ -79,6 +85,23 @@ func Server(cfg *config.Config) *cobra.Command { gr.Add(runner.NewGoMicroHttpServerRunner(cfg.Service.Name+".http", server)) } + { + grpcServer, err := grpc.Server( + grpc.Config(cfg), + grpc.Logger(logger), + grpc.Name(cfg.Service.Name), + grpc.Context(ctx), + grpc.JWTSecret(cfg.TokenManager.JWTSecret), + grpc.TraceProvider(traceProvider), + ) + if err != nil { + logger.Info().Err(err).Str("transport", "grpc").Msg("Failed to initialize server") + return err + } + + gr.Add(runner.NewGoMicroGrpcServerRunner(cfg.Service.Name+".grpc", grpcServer)) + } + { debugServer, err := debug.Server( debug.Logger(logger), diff --git a/services/web/pkg/config/config.go b/services/web/pkg/config/config.go index e2edcab53a..ac391ded42 100644 --- a/services/web/pkg/config/config.go +++ b/services/web/pkg/config/config.go @@ -3,6 +3,8 @@ package config import ( "context" + "go-micro.dev/v4/client" + "github.com/opencloud-eu/opencloud/pkg/shared" ) @@ -17,6 +19,9 @@ type Config struct { HTTP HTTP `yaml:"http"` + GRPC GRPCConfig `yaml:"grpc"` + GrpcClient client.Client `yaml:"-"` + Asset Asset `yaml:"asset"` File string `yaml:"file" env:"WEB_UI_CONFIG_FILE" desc:"Read the OpenCloud Web json based configuration from this path/file. The config file takes precedence over WEB_OPTION_xxx environment variables. See the text description for more details." introductionVersion:"1.0.0"` Web Web `yaml:"web"` @@ -24,8 +29,21 @@ type Config struct { TokenManager *TokenManager `yaml:"token_manager"` - GatewayAddress string `yaml:"gateway_addr" env:"WEB_GATEWAY_GRPC_ADDR" desc:"The bind address of the GRPC service." introductionVersion:"1.0.0"` - Context context.Context `yaml:"-"` + GatewayAddress string `yaml:"gateway_addr" env:"WEB_GATEWAY_GRPC_ADDR" desc:"The bind address of the GRPC service." introductionVersion:"1.0.0"` + Context context.Context `yaml:"-"` + GRPCClientTLS *shared.GRPCClientTLS `yaml:"grpc_client_tls"` + + Metadata Metadata `yaml:"metadata_config"` +} + +// Metadata configures the metadata store to use +type Metadata struct { + GatewayAddress string `yaml:"gateway_addr" env:"WEB_STORAGE_GATEWAY_GRPC_ADDR;STORAGE_GATEWAY_GRPC_ADDR" desc:"GRPC address of the STORAGE-SYSTEM service." introductionVersion:"%%NEXT%%"` + StorageAddress string `yaml:"storage_addr" env:"WEB_STORAGE_GRPC_ADDR;STORAGE_GRPC_ADDR" desc:"GRPC address of the STORAGE-SYSTEM service." introductionVersion:"%%NEXT%%"` + + SystemUserID string `yaml:"system_user_id" env:"OC_SYSTEM_USER_ID;WEB_SYSTEM_USER_ID" desc:"ID of the OpenCloud STORAGE-SYSTEM system user. Admins need to set the ID for the STORAGE-SYSTEM system user in this config option which is then used to reference the user. Any reasonable long string is possible, preferably this would be an UUIDv4 format." introductionVersion:"%%NEXT%%"` + SystemUserIDP string `yaml:"system_user_idp" env:"OC_SYSTEM_USER_IDP;WEB_SYSTEM_USER_IDP" desc:"IDP of the OpenCloud STORAGE-SYSTEM system user." introductionVersion:"%%NEXT%%"` + SystemUserAPIKey string `yaml:"system_user_api_key" env:"OC_SYSTEM_USER_API_KEY" desc:"API key for the STORAGE-SYSTEM system user." introductionVersion:"%%NEXT%%"` } // Asset defines the available asset configuration. diff --git a/services/web/pkg/config/defaults/defaultconfig.go b/services/web/pkg/config/defaults/defaultconfig.go index 8120394aa6..98514dce79 100644 --- a/services/web/pkg/config/defaults/defaultconfig.go +++ b/services/web/pkg/config/defaults/defaultconfig.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/opencloud-eu/opencloud/pkg/config/defaults" + "github.com/opencloud-eu/opencloud/pkg/structs" "github.com/opencloud-eu/opencloud/services/web/pkg/config" ) @@ -25,6 +26,11 @@ func DefaultConfig() *config.Config { Pprof: false, Zpages: false, }, + GRPC: config.GRPCConfig{ + Addr: "127.0.0.1:9221", + Namespace: "eu.opencloud.api", + Protocol: "tcp", + }, HTTP: config.HTTP{ Addr: "127.0.0.1:9100", Root: "/", @@ -85,12 +91,16 @@ func DefaultConfig() *config.Config { ThemesPath: filepath.Join(defaults.BaseDataPath(), "web/assets/themes"), }, GatewayAddress: "eu.opencloud.api.gateway", + Metadata: config.Metadata{ + GatewayAddress: "eu.opencloud.api.storage-system", + StorageAddress: "eu.opencloud.api.storage-system", + SystemUserIDP: "internal", + }, Web: config.Web{ ThemeServer: "https://localhost:9200", ThemePath: "/themes/opencloud/theme.json", Config: config.WebConfig{ Server: "https://localhost:9200", - Theme: "", OpenIDConnect: config.OIDC{ MetadataURL: "", Authority: "https://localhost:9200", @@ -140,6 +150,14 @@ func EnsureDefaults(cfg *config.Config) { cfg.HTTP.CORS.AllowedOrigins[0] == "https://localhost:9200") { cfg.HTTP.CORS.AllowedOrigins = []string{cfg.Commons.OpenCloudURL} } + + if cfg.Metadata.SystemUserAPIKey == "" && cfg.Commons != nil && cfg.Commons.SystemUserAPIKey != "" { + cfg.Metadata.SystemUserAPIKey = cfg.Commons.SystemUserAPIKey + } + + if cfg.Metadata.SystemUserID == "" && cfg.Commons != nil && cfg.Commons.SystemUserID != "" { + cfg.Metadata.SystemUserID = cfg.Commons.SystemUserID + } } // Sanitize sanitized the configuration @@ -183,4 +201,12 @@ func Sanitize(cfg *config.Config) { cfg.Web.Config.Options.Embed.DelegateAuthenticationOrigin == "" { cfg.Web.Config.Options.Embed = nil } + + if cfg.GRPCClientTLS == nil && cfg.Commons != nil { + cfg.GRPCClientTLS = structs.CopyOrZeroValue(cfg.Commons.GRPCClientTLS) + } + + if cfg.GRPC.TLS == nil && cfg.Commons != nil { + cfg.GRPC.TLS = structs.CopyOrZeroValue(cfg.Commons.GRPCServiceTLS) + } } diff --git a/services/web/pkg/config/grpc.go b/services/web/pkg/config/grpc.go new file mode 100644 index 0000000000..44eca8a5f6 --- /dev/null +++ b/services/web/pkg/config/grpc.go @@ -0,0 +1,11 @@ +package config + +import "github.com/opencloud-eu/opencloud/pkg/shared" + +// GRPCConfig defines the available grpc configuration. +type GRPCConfig struct { + Addr string `yaml:"addr" env:"WEB_GRPC_ADDR" desc:"The bind address of the GRPC service." introductionVersion:"%%NEXT%%"` + Namespace string `yaml:"-"` + TLS *shared.GRPCServiceTLS `yaml:"tls"` + Protocol string `yaml:"protocol" env:"OC_GRPC_PROTOCOL;WEB_GRPC_PROTOCOL" desc:"The transport protocol of the GRPC service." introductionVersion:"%%NEXT%%"` +} diff --git a/services/web/pkg/fs/fs.go b/services/web/pkg/fs/fs.go new file mode 100644 index 0000000000..bb6c74e7af --- /dev/null +++ b/services/web/pkg/fs/fs.go @@ -0,0 +1,52 @@ +package fs + +import ( + "context" + "fmt" + "time" + + revaMetadata "github.com/opencloud-eu/reva/v2/pkg/storage/utils/metadata" + + "github.com/opencloud-eu/opencloud/pkg/storage/metadata" + "github.com/opencloud-eu/opencloud/pkg/x/io/fsx" + metadataFs "github.com/opencloud-eu/opencloud/pkg/x/io/fsx/cs3/metadata" + "github.com/opencloud-eu/opencloud/services/web" + "github.com/opencloud-eu/opencloud/services/web/pkg/config" +) + +func NewThemeFS(c *config.Config) (*fsx.FallbackFS, error) { + storage, err := revaMetadata.NewCS3Storage( + c.Metadata.GatewayAddress, + c.Metadata.StorageAddress, + c.Metadata.SystemUserID, + c.Metadata.SystemUserIDP, + c.Metadata.SystemUserAPIKey, + ) + if err != nil { + return nil, err + } + + storage, err = metadata.NewLazyStorage(storage) + if err != nil { + return nil, err + } + + time.Sleep(3 * time.Second) // fixme: wait for the storage to be initialized + + if err := storage.Init(context.Background(), "web-storage"); err != nil { + return nil, err + } + + storageFS := metadataFs.NewMetadataFs(storage) + if err := storageFS.MkdirAll("assets/themes", 0755); err != nil { + return nil, fmt.Errorf("failed to create themes directory: %w", err) + } + + return fsx.NewFallbackFS( + fsx.NewBasePathFs(fsx.FromAfero(storageFS), "assets/themes"), + fsx.NewFallbackFS( + fsx.NewReadOnlyFs(fsx.NewBasePathFs(fsx.FromIOFS(web.Assets), "assets/themes")), + fsx.NewReadOnlyFs(fsx.NewBasePathFs(fsx.NewOsFs(), c.Asset.ThemesPath)), + ), + ), nil +} diff --git a/services/web/pkg/server/grpc/option.go b/services/web/pkg/server/grpc/option.go new file mode 100644 index 0000000000..d83163d748 --- /dev/null +++ b/services/web/pkg/server/grpc/option.go @@ -0,0 +1,94 @@ +package grpc + +import ( + "context" + + "go.opentelemetry.io/otel/trace" + + "github.com/opencloud-eu/opencloud/pkg/log" + svc "github.com/opencloud-eu/opencloud/services/search/pkg/service/grpc/v0" + "github.com/opencloud-eu/opencloud/services/web/pkg/config" + "github.com/opencloud-eu/opencloud/services/web/pkg/metrics" +) + +// Option defines a single option function. +type Option func(o *Options) + +// Options defines the available options for this package. +type Options struct { + Name string + Logger log.Logger + Context context.Context + Config *config.Config + Metrics *metrics.Metrics + Handler *svc.Service + JWTSecret string + TraceProvider trace.TracerProvider +} + +// newOptions initializes the available default options. +func newOptions(opts ...Option) Options { + opt := Options{} + + for _, o := range opts { + o(&opt) + } + + return opt +} + +// Name provides a name for the service. +func Name(val string) Option { + return func(o *Options) { + o.Name = val + } +} + +// Logger provides a function to set the logger option. +func Logger(val log.Logger) Option { + return func(o *Options) { + o.Logger = val + } +} + +// Context provides a function to set the context option. +func Context(val context.Context) Option { + return func(o *Options) { + o.Context = val + } +} + +// Config provides a function to set the config option. +func Config(val *config.Config) Option { + return func(o *Options) { + o.Config = val + } +} + +// Metrics provides a function to set the metrics option. +func Metrics(val *metrics.Metrics) Option { + return func(o *Options) { + o.Metrics = val + } +} + +// Handler provides a function to set the handler option. +func Handler(val *svc.Service) Option { + return func(o *Options) { + o.Handler = val + } +} + +// JWTSecret provides a function to set the Config option. +func JWTSecret(val string) Option { + return func(o *Options) { + o.JWTSecret = val + } +} + +// TraceProvider provides a function to set the trace provider option. +func TraceProvider(val trace.TracerProvider) Option { + return func(o *Options) { + o.TraceProvider = val + } +} diff --git a/services/web/pkg/server/grpc/server.go b/services/web/pkg/server/grpc/server.go new file mode 100644 index 0000000000..b349411323 --- /dev/null +++ b/services/web/pkg/server/grpc/server.go @@ -0,0 +1,67 @@ +package grpc + +import ( + "fmt" + + "github.com/opencloud-eu/opencloud/pkg/service/grpc" + "github.com/opencloud-eu/opencloud/pkg/version" + websvc "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/web/v0" + "github.com/opencloud-eu/opencloud/services/web/pkg/fs" + svc "github.com/opencloud-eu/opencloud/services/web/pkg/service/grpc/v0" +) + +// Server initializes a new go-micro service ready to run +func Server(opts ...Option) (grpc.Service, error) { + options := newOptions(opts...) + + service, err := grpc.NewServiceWithClient( + options.Config.GrpcClient, + grpc.TLSEnabled(options.Config.GRPC.TLS.Enabled), + grpc.TLSCert( + options.Config.GRPC.TLS.Cert, + options.Config.GRPC.TLS.Key, + ), + grpc.Name(options.Config.Service.Name), + grpc.Context(options.Context), + grpc.Address(options.Config.GRPC.Addr), + grpc.Namespace(options.Config.GRPC.Namespace), + grpc.Logger(options.Logger), + grpc.Version(version.GetString()), + grpc.TraceProvider(options.TraceProvider), + ) + if err != nil { + options.Logger.Fatal().Err(err).Msg("Error creating web service") + return grpc.Service{}, err + } + + themeFS, err := fs.NewThemeFS(options.Config) + if err != nil { + return grpc.Service{}, fmt.Errorf("could not initialize theme filesystem: %w", err) + } + + handle, err := svc.NewHandler( + svc.Config(options.Config), + svc.Logger(options.Logger), + svc.JWTSecret(options.JWTSecret), + svc.TracerProvider(options.TraceProvider), + svc.ThemeFS(themeFS), + ) + if err != nil { + options.Logger.Error(). + Err(err). + Msg("Error initializing web service") + return grpc.Service{}, err + } + + if err := websvc.RegisterWebServiceHandler( + service.Server(), + handle, + ); err != nil { + options.Logger.Error(). + Err(err). + Msg("Error registering web provider handler") + return grpc.Service{}, err + } + + return service, nil +} diff --git a/services/web/pkg/server/http/server.go b/services/web/pkg/server/http/server.go index 25f210b13b..5714d05c95 100644 --- a/services/web/pkg/server/http/server.go +++ b/services/web/pkg/server/http/server.go @@ -5,9 +5,12 @@ import ( "path" chimiddleware "github.com/go-chi/chi/v5/middleware" - "github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool" "go-micro.dev/v4" + "github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool" + + "github.com/opencloud-eu/opencloud/services/web/pkg/fs" + "github.com/opencloud-eu/opencloud/pkg/cors" "github.com/opencloud-eu/opencloud/pkg/middleware" "github.com/opencloud-eu/opencloud/pkg/registry" @@ -71,10 +74,11 @@ func Server(opts ...Option) (http.Service, error) { fsx.NewBasePathFs(fsx.NewOsFs(), options.Config.Asset.CorePath), fsx.NewBasePathFs(fsx.FromIOFS(web.Assets), "assets/core"), ) - themeFS := fsx.NewFallbackFS( - fsx.NewBasePathFs(fsx.NewOsFs(), options.Config.Asset.ThemesPath), - fsx.NewBasePathFs(fsx.FromIOFS(web.Assets), "assets/themes"), - ) + + themeFS, err := fs.NewThemeFS(options.Config) + if err != nil { + return http.Service{}, fmt.Errorf("could not initialize theme filesystem: %w", err) + } handle, err := svc.NewService( svc.Logger(options.Logger), diff --git a/services/web/pkg/service/grpc/v0/option.go b/services/web/pkg/service/grpc/v0/option.go new file mode 100644 index 0000000000..e0903033c4 --- /dev/null +++ b/services/web/pkg/service/grpc/v0/option.go @@ -0,0 +1,68 @@ +package service + +import ( + "go.opentelemetry.io/otel/trace" + + "github.com/opencloud-eu/opencloud/pkg/log" + "github.com/opencloud-eu/opencloud/pkg/x/io/fsx" + "github.com/opencloud-eu/opencloud/services/web/pkg/config" + "github.com/opencloud-eu/opencloud/services/web/pkg/theme" +) + +// Option defines a single option function. +type Option func(o *Options) + +// Options defines the available options for this package. +type Options struct { + Logger log.Logger + Config *config.Config + JWTSecret string + TracerProvider trace.TracerProvider + ThemeService *theme.Service + ThemeFS *fsx.FallbackFS +} + +func newOptions(opts ...Option) Options { + opt := Options{} + + for _, o := range opts { + o(&opt) + } + + return opt +} + +// Logger provides a function to set the Logger option. +func Logger(val log.Logger) Option { + return func(o *Options) { + o.Logger = val + } +} + +// Config provides a function to set the Config option. +func Config(val *config.Config) Option { + return func(o *Options) { + o.Config = val + } +} + +// JWTSecret provides a function to set the Config option. +func JWTSecret(val string) Option { + return func(o *Options) { + o.JWTSecret = val + } +} + +// TracerProvider provides a function to set the TracerProvider option +func TracerProvider(val trace.TracerProvider) Option { + return func(o *Options) { + o.TracerProvider = val + } +} + +// ThemeFS provides a function to set the themeFS option. +func ThemeFS(val *fsx.FallbackFS) Option { + return func(o *Options) { + o.ThemeFS = val + } +} diff --git a/services/web/pkg/service/grpc/v0/service.go b/services/web/pkg/service/grpc/v0/service.go new file mode 100644 index 0000000000..a305a1c6d7 --- /dev/null +++ b/services/web/pkg/service/grpc/v0/service.go @@ -0,0 +1,67 @@ +package service + +import ( + "archive/zip" + "bytes" + "context" + "fmt" + + "github.com/opencloud-eu/opencloud/pkg/log" + websvc "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/web/v0" + "github.com/opencloud-eu/opencloud/services/web/pkg/config" + "github.com/opencloud-eu/opencloud/services/web/pkg/theme" +) + +// NewHandler returns a service implementation for Service. +func NewHandler(opts ...Option) (websvc.WebServiceHandler, error) { + options := newOptions(opts...) + logger := options.Logger + cfg := options.Config + + themeService, err := theme.NewService( + theme.ServiceOptions{}. + WithThemeFS(options.ThemeFS.Primary()), + ) + if err != nil { + return nil, fmt.Errorf("could not initialize theme service: %w", err) + } + + return &Service{ + log: logger, + cfg: cfg, + themeService: themeService, + }, nil +} + +type Service struct { + log log.Logger + cfg *config.Config + themeService *theme.Service +} + +func (s Service) ThemeAdd(_ context.Context, req *websvc.ThemeAddRequest, res *websvc.ThemeAddResponse) error { + zr, err := zip.NewReader(bytes.NewReader(req.Data), int64(len(req.Data))) + if err != nil { + return err + } + + if err := s.themeService.Add(req.Id, zr); err != nil { + return fmt.Errorf("could not add theme %s: %w", req.Id, err) + } + + return nil +} + +func (s Service) ThemeRemove(_ context.Context, req *websvc.ThemeRemoveRequest, res *websvc.ThemeRemoveResponse) error { + if err := s.themeService.Remove(req.Id); err != nil { + return fmt.Errorf("could not remove theme %s: %w", req.Id, err) + } + + return nil +} + +func (s Service) ThemeExists(_ context.Context, req *websvc.ThemeExistsRequest, res *websvc.ThemeExistsResponse) error { + res.Exists = s.themeService.Exists(req.Id) + + return nil +} diff --git a/services/web/pkg/service/v0/service.go b/services/web/pkg/service/v0/service.go index a16ba5336f..e8a2b85f66 100644 --- a/services/web/pkg/service/v0/service.go +++ b/services/web/pkg/service/v0/service.go @@ -10,24 +10,18 @@ import ( "strings" "time" - gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" "github.com/go-chi/chi/v5" - "github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool" "github.com/riandyrn/otelchi" "github.com/opencloud-eu/opencloud/pkg/account" "github.com/opencloud-eu/opencloud/pkg/log" "github.com/opencloud-eu/opencloud/pkg/middleware" "github.com/opencloud-eu/opencloud/pkg/tracing" - "github.com/opencloud-eu/opencloud/pkg/x/io/fsx" "github.com/opencloud-eu/opencloud/services/web/pkg/assets" "github.com/opencloud-eu/opencloud/services/web/pkg/config" "github.com/opencloud-eu/opencloud/services/web/pkg/theme" ) -// ErrConfigInvalid is returned when the config parse is invalid. -var ErrConfigInvalid = `Invalid or missing config` - // Service defines the service handlers. type Service interface { ServeHTTP(w http.ResponseWriter, r *http.Request) @@ -50,22 +44,28 @@ func NewService(opts ...Option) (Service, error) { ), ) - svc := Web{ - logger: options.Logger, - config: options.Config, - mux: m, - coreFS: options.CoreFS, - themeFS: options.ThemeFS, - gatewaySelector: options.GatewaySelector, - } - themeService, err := theme.NewService( theme.ServiceOptions{}. - WithThemeFS(options.ThemeFS). - WithGatewaySelector(options.GatewaySelector), + WithThemeFS(options.ThemeFS), ) if err != nil { - return svc, err + return Web{}, err + } + + themeAPI, err := theme.NewHTTP( + theme.HTTPOptions{}. + WithService(themeService). + WithLogger(options.Logger), + ) + if err != nil { + return Web{}, err + } + + svc := Web{ + logger: options.Logger, + config: options.Config, + mux: m, + themeService: themeService, } m.Route(options.Config.HTTP.Root, func(r chi.Router) { @@ -75,11 +75,9 @@ func NewService(opts ...Option) (Service, error) { account.Logger(options.Logger), account.JWTSecret(options.Config.TokenManager.JWTSecret), )) - r.Post("/", themeService.LogoUpload) - r.Delete("/", themeService.LogoReset) }) r.Route("/themes", func(r chi.Router) { - r.Get("/{id}/theme.json", themeService.Get) + r.Get("/{id}/theme.json", themeAPI.Get) r.Mount("/", svc.Static( options.ThemeFS.IOFS(), path.Join(svc.config.HTTP.Root, "/themes"), @@ -92,7 +90,7 @@ func NewService(opts ...Option) (Service, error) { options.Config.HTTP.CacheTTL, )) r.Mount("/", svc.Static( - svc.coreFS, + options.CoreFS, svc.config.HTTP.Root, options.Config.HTTP.CacheTTL, )) @@ -107,12 +105,10 @@ func NewService(opts ...Option) (Service, error) { // Web defines the handlers for the web service. type Web struct { - logger log.Logger - config *config.Config - mux *chi.Mux - coreFS fs.FS - themeFS *fsx.FallbackFS - gatewaySelector pool.Selectable[gateway.GatewayAPIClient] + logger log.Logger + config *config.Config + mux *chi.Mux + themeService *theme.Service } // ServeHTTP implements the Service interface. @@ -120,33 +116,34 @@ func (p Web) ServeHTTP(w http.ResponseWriter, r *http.Request) { p.mux.ServeHTTP(w, r) } -func (p Web) getPayload() (payload []byte, err error) { - // render dynamically using config +// Config handles HTTP requests to provide the current configuration as a JSON response. +func (p Web) Config(w http.ResponseWriter, _ *http.Request) { + // decouple theme-related config changes + conf := *p.config + // check if the console theme exists and apply it + if conf.Web.ThemeServer == conf.Web.Config.Server && p.themeService.Exists("_console") { + conf.Web.ThemePath = path.Join("themes", "_console", "theme.json") + } // build theme url - if themeServer, err := url.Parse(p.config.Web.ThemeServer); err == nil { - p.config.Web.Config.Theme = themeServer.String() + p.config.Web.ThemePath + if themeServer, err := url.Parse(conf.Web.ThemeServer); err == nil { + themeServer.Path = conf.Web.ThemePath + conf.Web.Config.Theme = themeServer.String() } else { - p.config.Web.Config.Theme = p.config.Web.ThemePath + conf.Web.Config.Theme = conf.Web.ThemePath } - // make apps render as empty array if it is empty + // make apps render as an empty array if it is empty // TODO remove once https://github.com/golang/go/issues/27589 is fixed - if len(p.config.Web.Config.Apps) == 0 { - p.config.Web.Config.Apps = make([]string, 0) + if len(conf.Web.Config.Apps) == 0 { + conf.Web.Config.Apps = make([]string, 0) } - // ensure that the server url has a trailing slash - p.config.Web.Config.Server = strings.TrimRight(p.config.Web.Config.Server, "/") + "/" - - return json.Marshal(p.config.Web.Config) -} - -// Config implements the Service interface. -func (p Web) Config(w http.ResponseWriter, _ *http.Request) { - payload, err := p.getPayload() + payload, err := json.Marshal(conf.Web.Config) if err != nil { - http.Error(w, ErrConfigInvalid, http.StatusUnprocessableEntity) + msg := "Invalid or missing config" + p.logger.Error().Err(err).Msg(msg) + http.Error(w, msg, http.StatusUnprocessableEntity) return } diff --git a/services/web/pkg/theme/http.go b/services/web/pkg/theme/http.go new file mode 100644 index 0000000000..4612005e8e --- /dev/null +++ b/services/web/pkg/theme/http.go @@ -0,0 +1,72 @@ +package theme + +import ( + "encoding/json" + "errors" + "net/http" + + "github.com/opencloud-eu/opencloud/pkg/log" +) + +// HTTPOptions defines the options to configure HTTP. +type HTTPOptions struct { + service *Service + logger log.Logger +} + +// WithService sets the service for HTTPOptions. +func (o HTTPOptions) WithService(s *Service) HTTPOptions { + o.service = s + return o +} + +// WithLogger sets the logger for the Service. +func (o HTTPOptions) WithLogger(logger log.Logger) HTTPOptions { + o.logger = logger + return o +} + +// validate validates the input parameters. +func (o HTTPOptions) validate() error { + if o.service == nil { + return errors.New("service is required") + } + + return nil +} + +type HTTP struct { + service *Service + logger log.Logger +} + +// NewHTTP initializes a new HTTP. +func NewHTTP(options HTTPOptions) (HTTP, error) { + if err := options.validate(); err != nil { + return HTTP{}, err + } + + return HTTP(options), nil +} + +// Get renders the theme for the given ID. +func (h HTTP) Get(w http.ResponseWriter, r *http.Request) { + theme, err := h.service.Build(r.PathValue("id")) + if err != nil { + h.logger.Error().Err(err).Msg("failed to merge themes") + http.Error(w, ErrBuildingThemeFailed.Error(), http.StatusInternalServerError) + } + + b, err := json.Marshal(theme) + if err != nil { + h.logger.Error().Err(err).Msg("failed to marshal theme") + http.Error(w, ErrBuildingThemeFailed.Error(), http.StatusInternalServerError) + return + } + + if _, err = w.Write(b); err != nil { + h.logger.Error().Err(err).Msg("failed to write response") + http.Error(w, ErrBuildingThemeFailed.Error(), http.StatusInternalServerError) + return + } +} diff --git a/services/web/pkg/theme/http_test.go b/services/web/pkg/theme/http_test.go new file mode 100644 index 0000000000..28a667241b --- /dev/null +++ b/services/web/pkg/theme/http_test.go @@ -0,0 +1,57 @@ +package theme_test + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/spf13/afero" + "github.com/stretchr/testify/assert" + "github.com/tidwall/gjson" + + "github.com/opencloud-eu/opencloud/pkg/x/io/fsx" + "github.com/opencloud-eu/opencloud/services/graph/pkg/unifiedrole" + "github.com/opencloud-eu/opencloud/services/web/pkg/theme" +) + +func TestHTTP_Get(t *testing.T) { + primaryFS := fsx.NewMemMapFs() + fallbackFS := fsx.NewFallbackFS(primaryFS, fsx.NewMemMapFs()) + + add := func(filename string, content interface{}) { + b, err := json.Marshal(content) + assert.Nil(t, err) + + assert.Nil(t, afero.WriteFile(primaryFS, filename, b, 0644)) + } + + // baseTheme + add("base/theme.json", map[string]interface{}{ + "base": "base", + }) + // brandingTheme + add("_branding/theme.json", map[string]interface{}{ + "_branding": "_branding", + }) + + service, err := theme.NewService(theme.ServiceOptions{}.WithThemeFS(fallbackFS)) + assert.NoError(t, err) + + handlers, err := theme.NewHTTP(theme.HTTPOptions{}.WithService(service)) + assert.NoError(t, err) + + r := httptest.NewRequest(http.MethodGet, "/", nil) + r.SetPathValue("id", "base") + + w := httptest.NewRecorder() + handlers.Get(w, r) + + jsonData := gjson.Parse(w.Body.String()) + // baseTheme + assert.Equal(t, jsonData.Get("base").String(), "base") + // brandingTheme + assert.Equal(t, jsonData.Get("_branding").String(), "_branding") + // themeDefaults + assert.Equal(t, jsonData.Get("common.shareRoles."+unifiedrole.UnifiedRoleViewerID+".name").String(), "UnifiedRoleViewer") +} diff --git a/services/web/pkg/theme/kv.go b/services/web/pkg/theme/kv.go index 1c8c7f9117..981c153840 100644 --- a/services/web/pkg/theme/kv.go +++ b/services/web/pkg/theme/kv.go @@ -1,12 +1,10 @@ package theme import ( - "bytes" "encoding/json" - "strings" + "io/fs" "dario.cat/mergo" - "github.com/spf13/afero" ) // KV is a generic key-value map. @@ -26,43 +24,15 @@ func MergeKV(values ...KV) (KV, error) { return kv, nil } -// PatchKV injects the given values into to v. -func PatchKV(v map[string]interface{}, values KV) KV { - if v == nil { - v = KV{} - } - for k, val := range values { - t := v - path := strings.Split(k, ".") - for i, p := range path { - if i == len(path)-1 { - switch val { - // if the value is nil, we delete the key - case nil: - delete(t, p) - default: - t[p] = val - } - break - } - - if _, ok := t[p]; !ok { - t[p] = map[string]interface{}{} - } - - t = t[p].(map[string]interface{}) - } - } - return v -} - // LoadKV loads a key-value map from the given file system. -func LoadKV(fsys afero.Fs, p string) (KV, error) { +func LoadKV(fsys fs.FS, p string) (KV, error) { f, err := fsys.Open(p) if err != nil { return nil, err } - defer f.Close() + defer func() { + _ = f.Close() + }() var kv KV err = json.NewDecoder(f).Decode(&kv) @@ -72,27 +42,3 @@ func LoadKV(fsys afero.Fs, p string) (KV, error) { return kv, nil } - -// WriteKV writes the given key-value map to the file system. -func WriteKV(fsys afero.Fs, p string, kv KV) error { - data, err := json.Marshal(kv) - if err != nil { - return err - } - - return afero.WriteReader(fsys, p, bytes.NewReader(data)) -} - -// UpdateKV updates the key-value map at the given path with the given values. -func UpdateKV(fsys afero.Fs, p string, values KV) error { - var kv KV - - existing, err := LoadKV(fsys, p) - if err == nil { - kv = existing - } - - kv = PatchKV(kv, values) - - return WriteKV(fsys, p, kv) -} diff --git a/services/web/pkg/theme/kv_test.go b/services/web/pkg/theme/kv_test.go index 11901619d7..b3f6b780bc 100644 --- a/services/web/pkg/theme/kv_test.go +++ b/services/web/pkg/theme/kv_test.go @@ -30,82 +30,6 @@ func TestMergeKV(t *testing.T) { }) } -func TestPatchKV(t *testing.T) { - in := theme.KV{ - "a": map[string]interface{}{ - "value": "a", - }, - "b": map[string]interface{}{ - "value": "b", - }, - } - out := theme.PatchKV(in, theme.KV{ - "b.value": "b-new", - "c.value": "c-new", - "d": "d-new", - "e.value.subvalue": "e-new", - }) - assert.Equal(t, theme.KV{ - "a": map[string]interface{}{ - "value": "a", - }, - "b": map[string]interface{}{ - "value": "b-new", - }, - "c": map[string]interface{}{ - "value": "c-new", - }, - "d": "d-new", - "e": map[string]interface{}{ - "value": map[string]interface{}{ - "subvalue": "e-new", - }, - }, - }, out) -} - -func TestPatchKVUnset(t *testing.T) { - in := theme.KV{ - "a": map[string]interface{}{ - "value": "a", - }, - "b": map[string]interface{}{ - "value": "b", - }, - } - out := theme.PatchKV(in, theme.KV{ - "a.value": nil, - "b": nil, - }) - assert.Equal(t, theme.KV{ - "a": map[string]interface{}{}, - }, out) -} - -func TestPatchKVwithNil(t *testing.T) { - var in theme.KV - out := theme.PatchKV(in, theme.KV{ - "b.value": "b-new", - "c.value": "c-new", - "d": "d-new", - "e.value.subvalue": "e-new", - }) - assert.Equal(t, theme.KV{ - "b": map[string]interface{}{ - "value": "b-new", - }, - "c": map[string]interface{}{ - "value": "c-new", - }, - "d": "d-new", - "e": map[string]interface{}{ - "value": map[string]interface{}{ - "subvalue": "e-new", - }, - }, - }, out) -} - func TestLoadKV(t *testing.T) { in := theme.KV{ "a": map[string]interface{}{ @@ -121,66 +45,7 @@ func TestLoadKV(t *testing.T) { fsys := fsx.NewMemMapFs() assert.Nil(t, afero.WriteFile(fsys, "some.json", b, 0644)) - out, err := theme.LoadKV(fsys, "some.json") - assert.Nil(t, err) - assert.Equal(t, in, out) -} - -func TestWriteKV(t *testing.T) { - in := theme.KV{ - "a": map[string]interface{}{ - "value": "a", - }, - "b": map[string]interface{}{ - "value": "b", - }, - } - - fsys := fsx.NewMemMapFs() - assert.Nil(t, theme.WriteKV(fsys, "some.json", in)) - - f, err := fsys.Open("some.json") + out, err := theme.LoadKV(fsys.IOFS(), "some.json") assert.Nil(t, err) - - var out theme.KV - assert.Nil(t, json.NewDecoder(f).Decode(&out)) assert.Equal(t, in, out) } - -func TestUpdateKV(t *testing.T) { - fileKV := theme.KV{ - "a": map[string]interface{}{ - "value": "a", - }, - "b": map[string]interface{}{ - "value": "b", - }, - } - - wb, err := json.Marshal(fileKV) - assert.Nil(t, err) - - fsys := fsx.NewMemMapFs() - assert.Nil(t, afero.WriteFile(fsys, "some.json", wb, 0644)) - _ = theme.UpdateKV(fsys, "some.json", theme.KV{ - "b.value": "b-new", - "c.value": "c-new", - }) - - f, err := fsys.Open("some.json") - assert.Nil(t, err) - - var out theme.KV - assert.Nil(t, json.NewDecoder(f).Decode(&out)) - assert.Equal(t, out, theme.KV{ - "a": map[string]interface{}{ - "value": "a", - }, - "b": map[string]interface{}{ - "value": "b-new", - }, - "c": map[string]interface{}{ - "value": "c-new", - }, - }) -} diff --git a/services/web/pkg/theme/service.go b/services/web/pkg/theme/service.go index 511671614c..1bce1d29f4 100644 --- a/services/web/pkg/theme/service.go +++ b/services/web/pkg/theme/service.go @@ -1,17 +1,12 @@ package theme import ( - "encoding/json" - "net/http" + "archive/zip" + "io" + "os" + "path/filepath" - gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" - permissionsapi "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1" - rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" "github.com/pkg/errors" - "github.com/spf13/afero" - - revactx "github.com/opencloud-eu/reva/v2/pkg/ctx" - "github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool" "github.com/opencloud-eu/opencloud/pkg/x/io/fsx" "github.com/opencloud-eu/opencloud/pkg/x/path/filepathx" @@ -19,57 +14,49 @@ import ( // ServiceOptions defines the options to configure the Service. type ServiceOptions struct { - themeFS *fsx.FallbackFS - gatewaySelector pool.Selectable[gateway.GatewayAPIClient] + themeFS fsx.FS } // WithThemeFS sets the theme filesystem. -func (o ServiceOptions) WithThemeFS(fSys *fsx.FallbackFS) ServiceOptions { +func (o ServiceOptions) WithThemeFS(fSys fsx.FS) ServiceOptions { o.themeFS = fSys return o } -// WithGatewaySelector sets the gateway selector. -func (o ServiceOptions) WithGatewaySelector(gws pool.Selectable[gateway.GatewayAPIClient]) ServiceOptions { - o.gatewaySelector = gws - return o -} - // validate validates the input parameters. func (o ServiceOptions) validate() error { if o.themeFS == nil { return errors.New("themeFS is required") } - if o.gatewaySelector == nil { - return errors.New("gatewaySelector is required") - } - return nil } // Service defines the http service. type Service struct { - themeFS *fsx.FallbackFS - gatewaySelector pool.Selectable[gateway.GatewayAPIClient] + themeFS fsx.FS } // NewService initializes a new Service. -func NewService(options ServiceOptions) (Service, error) { +func NewService(options ServiceOptions) (*Service, error) { if err := options.validate(); err != nil { - return Service{}, err + return nil, err } - return Service(options), nil + return &Service{ + themeFS: options.themeFS, + }, nil } -// Get renders the theme, the theme is a merge of the default theme, the base theme, and the branding theme. -func (s Service) Get(w http.ResponseWriter, r *http.Request) { +// Build builds the theme, the theme is a merge of the default theme, the base theme, and the branding theme. +func (s Service) Build(id string) (KV, error) { + themeFS := s.themeFS.IOFS() + // there is no guarantee that the theme exists, its optional; therefore, we ignore the error - baseTheme, _ := LoadKV(s.themeFS, filepathx.JailJoin(r.PathValue("id"), _themeFileName)) + baseTheme, _ := LoadKV(themeFS, filepathx.JailJoin(id, _themeFileName)) // there is no guarantee that the theme exists, its optional; therefore, we ignore the error here too - brandingTheme, _ := LoadKV(s.themeFS, filepathx.JailJoin(_brandingRoot, _themeFileName)) + brandingTheme, _ := LoadKV(themeFS, filepathx.JailJoin(_brandingRoot, _themeFileName)) // merge the themes, the order is important, the last one wins and overrides the previous ones // themeDefaults: contains all the default values, this is guaranteed to exist @@ -78,118 +65,63 @@ func (s Service) Get(w http.ResponseWriter, r *http.Request) { // mergedTheme = themeDefaults < baseTheme < brandingTheme mergedTheme, err := MergeKV(themeDefaults, baseTheme, brandingTheme) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return + return nil, errors.Wrap(err, "failed to merge themes") } - b, err := json.Marshal(mergedTheme) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } + return mergedTheme, nil +} - _, err = w.Write(b) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } +func (s Service) Exists(id string) bool { + info, err := s.themeFS.Stat(filepathx.JailJoin(id, _themeFileName)) + return err == nil && !info.IsDir() && info.Size() > 0 } -// LogoUpload implements the endpoint to upload a custom logo for the OpenCloud instance. -func (s Service) LogoUpload(w http.ResponseWriter, r *http.Request) { - gatewayClient, err := s.gatewaySelector.Next() - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return +func (s Service) Remove(id string) error { + if !s.Exists(id) { + return errors.Errorf("theme %s does not exist", id) } - user := revactx.ContextMustGetUser(r.Context()) - rsp, err := gatewayClient.CheckPermission(r.Context(), &permissionsapi.CheckPermissionRequest{ - Permission: "Logo.Write", - SubjectRef: &permissionsapi.SubjectReference{ - Spec: &permissionsapi.SubjectReference_UserId{ - UserId: user.GetId(), - }, - }, - }) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - if rsp.GetStatus().GetCode() != rpc.Code_CODE_OK { - w.WriteHeader(http.StatusForbidden) - return - } + // remove the theme directory + return s.themeFS.RemoveAll(id) +} - file, fileHeader, err := r.FormFile("logo") - if err != nil { - if errors.Is(err, http.ErrMissingFile) { - w.WriteHeader(http.StatusBadRequest) - } - w.WriteHeader(http.StatusInternalServerError) - return +func (s Service) Add(id string, r *zip.Reader) error { + if s.Exists(id) { + return errors.Errorf("theme %s already exists", id) } - defer file.Close() - if !isFiletypePermitted(fileHeader.Filename, fileHeader.Header.Get("Content-Type")) { - w.WriteHeader(http.StatusBadRequest) - return - } + for _, f := range r.File { + filePath := filepath.Join(id, f.Name) + if f.FileInfo().IsDir() { + err := s.themeFS.MkdirAll(filePath, os.ModePerm) + if err != nil { + return err + } - fp := filepathx.JailJoin(_brandingRoot, fileHeader.Filename) - err = afero.WriteReader(s.themeFS, fp, file) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } + continue + } - err = UpdateKV(s.themeFS, filepathx.JailJoin(_brandingRoot, _themeFileName), KV{ - "common.logo": filepathx.JailJoin("themes", fp), - "clients.web.defaults.logo": filepathx.JailJoin("themes", fp), - }) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } + if err := s.themeFS.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { + return err + } - w.WriteHeader(http.StatusOK) -} + source, err := f.Open() + if err != nil { + return err + } -// LogoReset implements the endpoint to reset the instance logo. -// The config will be changed back to use the embedded logo asset. -func (s Service) LogoReset(w http.ResponseWriter, r *http.Request) { - gatewayClient, err := s.gatewaySelector.Next() - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } + dest, err := s.themeFS.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + return err + } - user := revactx.ContextMustGetUser(r.Context()) - rsp, err := gatewayClient.CheckPermission(r.Context(), &permissionsapi.CheckPermissionRequest{ - Permission: "Logo.Write", - SubjectRef: &permissionsapi.SubjectReference{ - Spec: &permissionsapi.SubjectReference_UserId{ - UserId: user.GetId(), - }, - }, - }) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - if rsp.GetStatus().GetCode() != rpc.Code_CODE_OK { - w.WriteHeader(http.StatusForbidden) - return - } + if _, err := io.Copy(dest, source); err != nil { + return err + } - err = UpdateKV(s.themeFS, filepathx.JailJoin(_brandingRoot, _themeFileName), KV{ - "common.logo": nil, - "clients.web.defaults.logo": nil, - }) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return + _ = dest.Close() + _ = source.Close() } - w.WriteHeader(http.StatusOK) + return nil } diff --git a/services/web/pkg/theme/service_test.go b/services/web/pkg/theme/service_test.go index ca392582df..9418da4b2f 100644 --- a/services/web/pkg/theme/service_test.go +++ b/services/web/pkg/theme/service_test.go @@ -1,19 +1,11 @@ package theme_test import ( - "encoding/json" - "net/http" - "net/http/httptest" "testing" - gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" - "github.com/spf13/afero" "github.com/stretchr/testify/assert" - "github.com/tidwall/gjson" "github.com/opencloud-eu/opencloud/pkg/x/io/fsx" - "github.com/opencloud-eu/opencloud/services/graph/mocks" - "github.com/opencloud-eu/opencloud/services/graph/pkg/unifiedrole" "github.com/opencloud-eu/opencloud/services/web/pkg/theme" ) @@ -26,49 +18,8 @@ func TestNewService(t *testing.T) { t.Run("success if the options are valid", func(t *testing.T) { _, err := theme.NewService( theme.ServiceOptions{}. - WithThemeFS(fsx.NewFallbackFS(fsx.NewMemMapFs(), fsx.NewMemMapFs())). - WithGatewaySelector(mocks.NewSelectable[gateway.GatewayAPIClient](t)), + WithThemeFS(fsx.NewFallbackFS(fsx.NewMemMapFs(), fsx.NewMemMapFs())), ) assert.NoError(t, err) }) } - -func TestService_Get(t *testing.T) { - primaryFS := fsx.NewMemMapFs() - fallbackFS := fsx.NewFallbackFS(primaryFS, fsx.NewMemMapFs()) - - add := func(filename string, content interface{}) { - b, err := json.Marshal(content) - assert.Nil(t, err) - - assert.Nil(t, afero.WriteFile(primaryFS, filename, b, 0644)) - } - - // baseTheme - add("base/theme.json", map[string]interface{}{ - "base": "base", - }) - // brandingTheme - add("_branding/theme.json", map[string]interface{}{ - "_branding": "_branding", - }) - - service, _ := theme.NewService( - theme.ServiceOptions{}. - WithThemeFS(fallbackFS). - WithGatewaySelector(mocks.NewSelectable[gateway.GatewayAPIClient](t)), - ) - r := httptest.NewRequest(http.MethodGet, "/", nil) - r.SetPathValue("id", "base") - - w := httptest.NewRecorder() - service.Get(w, r) - - jsonData := gjson.Parse(w.Body.String()) - // baseTheme - assert.Equal(t, jsonData.Get("base").String(), "base") - // brandingTheme - assert.Equal(t, jsonData.Get("_branding").String(), "_branding") - // themeDefaults - assert.Equal(t, jsonData.Get("common.shareRoles."+unifiedrole.UnifiedRoleViewerID+".name").String(), "UnifiedRoleViewer") -} diff --git a/services/web/pkg/theme/theme.go b/services/web/pkg/theme/theme.go index 56f190d04e..af23ccb788 100644 --- a/services/web/pkg/theme/theme.go +++ b/services/web/pkg/theme/theme.go @@ -1,6 +1,7 @@ package theme import ( + "errors" "path" "github.com/opencloud-eu/opencloud/pkg/capabilities" @@ -8,8 +9,9 @@ import ( ) var ( - _brandingRoot = "_branding" - _themeFileName = "theme.json" + _brandingRoot = "_branding" + _themeFileName = "theme.json" + ErrBuildingThemeFailed = errors.New("building theme failed") ) // themeDefaults contains the default values for the theme. From ff0cabc5cf8350b9d510b002590681459246b47c Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Thu, 9 Apr 2026 11:39:50 +0200 Subject: [PATCH 3/4] feat(console): introduce console service with remote integration adds a new console service that connects the system with the console remote (instance management). includes initial support for using themes managed by the remote service. # Conflicts: # go.mod --- go.mod | 10 + go.sum | 18 + opencloud/pkg/command/services.go | 6 + opencloud/pkg/runtime/service/service.go | 6 + pkg/config/config.go | 2 + pkg/config/defaultconfig.go | 2 + services/console/.mockery.yaml | 15 + services/console/Makefile | 16 + services/console/mocks/console_repository.go | 101 + services/console/mocks/repository.go | 225 + services/console/mocks/service.go | 140 + services/console/pkg/command/command.go | 15 + services/console/pkg/command/health.go | 22 + services/console/pkg/command/root.go | 31 + services/console/pkg/command/update.go | 194 + services/console/pkg/command/version.go | 51 + services/console/pkg/config/config.go | 69 + services/console/pkg/config/console.go | 5 + .../pkg/config/defaults/defaultconfig.go | 58 + services/console/pkg/config/parser/parse.go | 41 + services/console/pkg/console/console.go | 14 + services/console/pkg/console/grpc.go | 30 + services/console/pkg/console/http.go | 35 + services/console/pkg/console/jwt.go | 30 + services/console/pkg/console/remote/remote.go | 1 + .../console/pkg/console/remote/repository.go | 71 + .../pkg/console/remote/subscription.go | 41 + .../console/remote/subscription_centrifugo.go | 125 + services/console/pkg/console/url.go | 43 + services/console/pkg/features/features.go | 17 + .../console/pkg/features/features_release.go | 13 + services/console/pkg/web/repository.go | 70 + services/console/pkg/web/service_core.go | 90 + services/console/pkg/web/service_core_test.go | 31 + services/console/pkg/web/service_sse.go | 55 + services/console/pkg/web/web.go | 21 + services/web/pkg/service/v0/service.go | 5 +- .../centrifugal/centrifuge-go/.gitignore | 34 + .../centrifugal/centrifuge-go/LICENSE | 21 + .../centrifugal/centrifuge-go/README.md | 67 + .../centrifugal/centrifuge-go/changelog.md | 189 + .../centrifugal/centrifuge-go/client.go | 2132 ++++ .../centrifuge-go/client_events.go | 194 + .../centrifugal/centrifuge-go/codes.go | 27 + .../centrifugal/centrifuge-go/config.go | 78 + .../centrifugal/centrifuge-go/errors.go | 98 + .../centrifugal/centrifuge-go/log.go | 39 + .../centrifugal/centrifuge-go/protocol.go | 90 + .../centrifugal/centrifuge-go/queue.go | 96 + .../centrifugal/centrifuge-go/reconnect.go | 49 + .../centrifugal/centrifuge-go/subscription.go | 862 ++ .../centrifuge-go/subscription_events.go | 119 + .../centrifugal/centrifuge-go/transport.go | 20 + .../centrifuge-go/transport_websocket.go | 220 + .../centrifugal/protocol/.gitignore | 5 + .../github.com/centrifugal/protocol/LICENSE | 21 + .../github.com/centrifugal/protocol/Makefile | 7 + .../github.com/centrifugal/protocol/bpool.go | 81 + .../centrifugal/protocol/client.pb.go | 3811 ++++++ .../protocol/client.pb_easyjson.go | 4034 ++++++ .../centrifugal/protocol/client.proto | 299 + .../centrifugal/protocol/client_vtproto.pb.go | 10440 ++++++++++++++++ .../github.com/centrifugal/protocol/decode.go | 217 + .../centrifugal/protocol/decode_stream.go | 161 + .../github.com/centrifugal/protocol/encode.go | 609 + .../centrifugal/protocol/encode_writer.go | 396 + .../centrifugal/protocol/generate.sh | 56 + vendor/github.com/centrifugal/protocol/raw.go | 32 + .../github.com/centrifugal/protocol/readme.md | 1 + .../centrifugal/protocol/replies.go | 189 + .../github.com/centrifugal/protocol/type.go | 198 + .../github.com/gorilla/websocket/.gitignore | 25 + vendor/github.com/gorilla/websocket/AUTHORS | 9 + vendor/github.com/gorilla/websocket/LICENSE | 22 + vendor/github.com/gorilla/websocket/README.md | 33 + vendor/github.com/gorilla/websocket/client.go | 434 + .../gorilla/websocket/compression.go | 148 + vendor/github.com/gorilla/websocket/conn.go | 1238 ++ vendor/github.com/gorilla/websocket/doc.go | 227 + vendor/github.com/gorilla/websocket/join.go | 42 + vendor/github.com/gorilla/websocket/json.go | 60 + vendor/github.com/gorilla/websocket/mask.go | 55 + .../github.com/gorilla/websocket/mask_safe.go | 16 + .../github.com/gorilla/websocket/prepared.go | 102 + vendor/github.com/gorilla/websocket/proxy.go | 77 + vendor/github.com/gorilla/websocket/server.go | 365 + .../gorilla/websocket/tls_handshake.go | 21 + .../gorilla/websocket/tls_handshake_116.go | 21 + vendor/github.com/gorilla/websocket/util.go | 298 + .../gorilla/websocket/x_net_proxy.go | 473 + vendor/github.com/josharian/intern/README.md | 5 + vendor/github.com/josharian/intern/intern.go | 44 + vendor/github.com/josharian/intern/license.md | 21 + vendor/github.com/jpillora/backoff/LICENSE | 21 + vendor/github.com/jpillora/backoff/README.md | 119 + vendor/github.com/jpillora/backoff/backoff.go | 100 + vendor/github.com/mailru/easyjson/.gitignore | 6 + vendor/github.com/mailru/easyjson/.travis.yml | 15 + vendor/github.com/mailru/easyjson/LICENSE | 7 + vendor/github.com/mailru/easyjson/Makefile | 72 + vendor/github.com/mailru/easyjson/README.md | 387 + .../github.com/mailru/easyjson/buffer/pool.go | 278 + vendor/github.com/mailru/easyjson/helpers.go | 114 + .../mailru/easyjson/jlexer/bytestostr.go | 24 + .../easyjson/jlexer/bytestostr_nounsafe.go | 13 + .../mailru/easyjson/jlexer/error.go | 15 + .../mailru/easyjson/jlexer/lexer.go | 1244 ++ .../mailru/easyjson/jwriter/writer.go | 405 + vendor/github.com/mailru/easyjson/raw.go | 45 + .../mailru/easyjson/unknown_fields.go | 32 + .../github.com/planetscale/vtprotobuf/LICENSE | 29 + .../vtprotobuf/protohelpers/protohelpers.go | 122 + .../github.com/segmentio/asm/ascii/ascii.go | 53 + .../segmentio/asm/ascii/equal_fold.go | 30 + .../segmentio/asm/ascii/equal_fold_amd64.go | 12 + .../segmentio/asm/ascii/equal_fold_amd64.s | 303 + .../segmentio/asm/ascii/equal_fold_default.go | 60 + .../github.com/segmentio/asm/ascii/valid.go | 18 + .../segmentio/asm/ascii/valid_amd64.go | 8 + .../segmentio/asm/ascii/valid_amd64.s | 131 + .../segmentio/asm/ascii/valid_default.go | 48 + .../segmentio/asm/ascii/valid_print.go | 18 + .../segmentio/asm/ascii/valid_print_amd64.go | 8 + .../segmentio/asm/ascii/valid_print_amd64.s | 184 + .../asm/ascii/valid_print_default.go | 46 + .../github.com/segmentio/asm/keyset/keyset.go | 40 + .../segmentio/asm/keyset/keyset_amd64.go | 9 + .../segmentio/asm/keyset/keyset_amd64.s | 107 + .../segmentio/asm/keyset/keyset_arm64.go | 8 + .../segmentio/asm/keyset/keyset_arm64.s | 143 + .../segmentio/asm/keyset/keyset_default.go | 19 + vendor/github.com/segmentio/encoding/LICENSE | 21 + .../segmentio/encoding/ascii/equal_fold.go | 40 + .../segmentio/encoding/ascii/valid.go | 26 + .../segmentio/encoding/ascii/valid_print.go | 26 + .../segmentio/encoding/iso8601/parse.go | 185 + .../segmentio/encoding/iso8601/valid.go | 179 + .../segmentio/encoding/json/README.md | 76 + .../segmentio/encoding/json/codec.go | 1239 ++ .../segmentio/encoding/json/decode.go | 1530 +++ .../segmentio/encoding/json/encode.go | 991 ++ .../github.com/segmentio/encoding/json/int.go | 98 + .../segmentio/encoding/json/json.go | 595 + .../segmentio/encoding/json/parse.go | 786 ++ .../segmentio/encoding/json/reflect.go | 20 + .../encoding/json/reflect_optimize.go | 30 + .../segmentio/encoding/json/string.go | 70 + .../segmentio/encoding/json/token.go | 428 + .../shadowspore/fossil-delta/.gitignore | 12 + .../shadowspore/fossil-delta/LICENSE | 35 + .../shadowspore/fossil-delta/README.md | 50 + .../shadowspore/fossil-delta/delta.go | 284 + .../shadowspore/fossil-delta/reader.go | 65 + .../shadowspore/fossil-delta/rolling_hash.go | 41 + .../shadowspore/fossil-delta/writer.go | 52 + .../valyala/bytebufferpool/.travis.yml | 15 + .../github.com/valyala/bytebufferpool/LICENSE | 22 + .../valyala/bytebufferpool/README.md | 21 + .../valyala/bytebufferpool/bytebuffer.go | 111 + .../github.com/valyala/bytebufferpool/doc.go | 7 + .../github.com/valyala/bytebufferpool/pool.go | 151 + vendor/modules.txt | 37 + 162 files changed, 41881 insertions(+), 2 deletions(-) create mode 100644 services/console/.mockery.yaml create mode 100644 services/console/Makefile create mode 100644 services/console/mocks/console_repository.go create mode 100644 services/console/mocks/repository.go create mode 100644 services/console/mocks/service.go create mode 100644 services/console/pkg/command/command.go create mode 100644 services/console/pkg/command/health.go create mode 100644 services/console/pkg/command/root.go create mode 100644 services/console/pkg/command/update.go create mode 100644 services/console/pkg/command/version.go create mode 100644 services/console/pkg/config/config.go create mode 100644 services/console/pkg/config/console.go create mode 100644 services/console/pkg/config/defaults/defaultconfig.go create mode 100644 services/console/pkg/config/parser/parse.go create mode 100644 services/console/pkg/console/console.go create mode 100644 services/console/pkg/console/grpc.go create mode 100644 services/console/pkg/console/http.go create mode 100644 services/console/pkg/console/jwt.go create mode 100644 services/console/pkg/console/remote/remote.go create mode 100644 services/console/pkg/console/remote/repository.go create mode 100644 services/console/pkg/console/remote/subscription.go create mode 100644 services/console/pkg/console/remote/subscription_centrifugo.go create mode 100644 services/console/pkg/console/url.go create mode 100644 services/console/pkg/features/features.go create mode 100644 services/console/pkg/features/features_release.go create mode 100644 services/console/pkg/web/repository.go create mode 100644 services/console/pkg/web/service_core.go create mode 100644 services/console/pkg/web/service_core_test.go create mode 100644 services/console/pkg/web/service_sse.go create mode 100644 services/console/pkg/web/web.go create mode 100644 vendor/github.com/centrifugal/centrifuge-go/.gitignore create mode 100644 vendor/github.com/centrifugal/centrifuge-go/LICENSE create mode 100644 vendor/github.com/centrifugal/centrifuge-go/README.md create mode 100644 vendor/github.com/centrifugal/centrifuge-go/changelog.md create mode 100644 vendor/github.com/centrifugal/centrifuge-go/client.go create mode 100644 vendor/github.com/centrifugal/centrifuge-go/client_events.go create mode 100644 vendor/github.com/centrifugal/centrifuge-go/codes.go create mode 100644 vendor/github.com/centrifugal/centrifuge-go/config.go create mode 100644 vendor/github.com/centrifugal/centrifuge-go/errors.go create mode 100644 vendor/github.com/centrifugal/centrifuge-go/log.go create mode 100644 vendor/github.com/centrifugal/centrifuge-go/protocol.go create mode 100644 vendor/github.com/centrifugal/centrifuge-go/queue.go create mode 100644 vendor/github.com/centrifugal/centrifuge-go/reconnect.go create mode 100644 vendor/github.com/centrifugal/centrifuge-go/subscription.go create mode 100644 vendor/github.com/centrifugal/centrifuge-go/subscription_events.go create mode 100644 vendor/github.com/centrifugal/centrifuge-go/transport.go create mode 100644 vendor/github.com/centrifugal/centrifuge-go/transport_websocket.go create mode 100644 vendor/github.com/centrifugal/protocol/.gitignore create mode 100644 vendor/github.com/centrifugal/protocol/LICENSE create mode 100644 vendor/github.com/centrifugal/protocol/Makefile create mode 100644 vendor/github.com/centrifugal/protocol/bpool.go create mode 100644 vendor/github.com/centrifugal/protocol/client.pb.go create mode 100644 vendor/github.com/centrifugal/protocol/client.pb_easyjson.go create mode 100644 vendor/github.com/centrifugal/protocol/client.proto create mode 100644 vendor/github.com/centrifugal/protocol/client_vtproto.pb.go create mode 100644 vendor/github.com/centrifugal/protocol/decode.go create mode 100644 vendor/github.com/centrifugal/protocol/decode_stream.go create mode 100644 vendor/github.com/centrifugal/protocol/encode.go create mode 100644 vendor/github.com/centrifugal/protocol/encode_writer.go create mode 100644 vendor/github.com/centrifugal/protocol/generate.sh create mode 100644 vendor/github.com/centrifugal/protocol/raw.go create mode 100644 vendor/github.com/centrifugal/protocol/readme.md create mode 100644 vendor/github.com/centrifugal/protocol/replies.go create mode 100644 vendor/github.com/centrifugal/protocol/type.go create mode 100644 vendor/github.com/gorilla/websocket/.gitignore create mode 100644 vendor/github.com/gorilla/websocket/AUTHORS create mode 100644 vendor/github.com/gorilla/websocket/LICENSE create mode 100644 vendor/github.com/gorilla/websocket/README.md create mode 100644 vendor/github.com/gorilla/websocket/client.go create mode 100644 vendor/github.com/gorilla/websocket/compression.go create mode 100644 vendor/github.com/gorilla/websocket/conn.go create mode 100644 vendor/github.com/gorilla/websocket/doc.go create mode 100644 vendor/github.com/gorilla/websocket/join.go create mode 100644 vendor/github.com/gorilla/websocket/json.go create mode 100644 vendor/github.com/gorilla/websocket/mask.go create mode 100644 vendor/github.com/gorilla/websocket/mask_safe.go create mode 100644 vendor/github.com/gorilla/websocket/prepared.go create mode 100644 vendor/github.com/gorilla/websocket/proxy.go create mode 100644 vendor/github.com/gorilla/websocket/server.go create mode 100644 vendor/github.com/gorilla/websocket/tls_handshake.go create mode 100644 vendor/github.com/gorilla/websocket/tls_handshake_116.go create mode 100644 vendor/github.com/gorilla/websocket/util.go create mode 100644 vendor/github.com/gorilla/websocket/x_net_proxy.go create mode 100644 vendor/github.com/josharian/intern/README.md create mode 100644 vendor/github.com/josharian/intern/intern.go create mode 100644 vendor/github.com/josharian/intern/license.md create mode 100644 vendor/github.com/jpillora/backoff/LICENSE create mode 100644 vendor/github.com/jpillora/backoff/README.md create mode 100644 vendor/github.com/jpillora/backoff/backoff.go create mode 100644 vendor/github.com/mailru/easyjson/.gitignore create mode 100644 vendor/github.com/mailru/easyjson/.travis.yml create mode 100644 vendor/github.com/mailru/easyjson/LICENSE create mode 100644 vendor/github.com/mailru/easyjson/Makefile create mode 100644 vendor/github.com/mailru/easyjson/README.md create mode 100644 vendor/github.com/mailru/easyjson/buffer/pool.go create mode 100644 vendor/github.com/mailru/easyjson/helpers.go create mode 100644 vendor/github.com/mailru/easyjson/jlexer/bytestostr.go create mode 100644 vendor/github.com/mailru/easyjson/jlexer/bytestostr_nounsafe.go create mode 100644 vendor/github.com/mailru/easyjson/jlexer/error.go create mode 100644 vendor/github.com/mailru/easyjson/jlexer/lexer.go create mode 100644 vendor/github.com/mailru/easyjson/jwriter/writer.go create mode 100644 vendor/github.com/mailru/easyjson/raw.go create mode 100644 vendor/github.com/mailru/easyjson/unknown_fields.go create mode 100644 vendor/github.com/planetscale/vtprotobuf/LICENSE create mode 100644 vendor/github.com/planetscale/vtprotobuf/protohelpers/protohelpers.go create mode 100644 vendor/github.com/segmentio/asm/ascii/ascii.go create mode 100644 vendor/github.com/segmentio/asm/ascii/equal_fold.go create mode 100644 vendor/github.com/segmentio/asm/ascii/equal_fold_amd64.go create mode 100644 vendor/github.com/segmentio/asm/ascii/equal_fold_amd64.s create mode 100644 vendor/github.com/segmentio/asm/ascii/equal_fold_default.go create mode 100644 vendor/github.com/segmentio/asm/ascii/valid.go create mode 100644 vendor/github.com/segmentio/asm/ascii/valid_amd64.go create mode 100644 vendor/github.com/segmentio/asm/ascii/valid_amd64.s create mode 100644 vendor/github.com/segmentio/asm/ascii/valid_default.go create mode 100644 vendor/github.com/segmentio/asm/ascii/valid_print.go create mode 100644 vendor/github.com/segmentio/asm/ascii/valid_print_amd64.go create mode 100644 vendor/github.com/segmentio/asm/ascii/valid_print_amd64.s create mode 100644 vendor/github.com/segmentio/asm/ascii/valid_print_default.go create mode 100644 vendor/github.com/segmentio/asm/keyset/keyset.go create mode 100644 vendor/github.com/segmentio/asm/keyset/keyset_amd64.go create mode 100644 vendor/github.com/segmentio/asm/keyset/keyset_amd64.s create mode 100644 vendor/github.com/segmentio/asm/keyset/keyset_arm64.go create mode 100644 vendor/github.com/segmentio/asm/keyset/keyset_arm64.s create mode 100644 vendor/github.com/segmentio/asm/keyset/keyset_default.go create mode 100644 vendor/github.com/segmentio/encoding/LICENSE create mode 100644 vendor/github.com/segmentio/encoding/ascii/equal_fold.go create mode 100644 vendor/github.com/segmentio/encoding/ascii/valid.go create mode 100644 vendor/github.com/segmentio/encoding/ascii/valid_print.go create mode 100644 vendor/github.com/segmentio/encoding/iso8601/parse.go create mode 100644 vendor/github.com/segmentio/encoding/iso8601/valid.go create mode 100644 vendor/github.com/segmentio/encoding/json/README.md create mode 100644 vendor/github.com/segmentio/encoding/json/codec.go create mode 100644 vendor/github.com/segmentio/encoding/json/decode.go create mode 100644 vendor/github.com/segmentio/encoding/json/encode.go create mode 100644 vendor/github.com/segmentio/encoding/json/int.go create mode 100644 vendor/github.com/segmentio/encoding/json/json.go create mode 100644 vendor/github.com/segmentio/encoding/json/parse.go create mode 100644 vendor/github.com/segmentio/encoding/json/reflect.go create mode 100644 vendor/github.com/segmentio/encoding/json/reflect_optimize.go create mode 100644 vendor/github.com/segmentio/encoding/json/string.go create mode 100644 vendor/github.com/segmentio/encoding/json/token.go create mode 100644 vendor/github.com/shadowspore/fossil-delta/.gitignore create mode 100644 vendor/github.com/shadowspore/fossil-delta/LICENSE create mode 100644 vendor/github.com/shadowspore/fossil-delta/README.md create mode 100644 vendor/github.com/shadowspore/fossil-delta/delta.go create mode 100644 vendor/github.com/shadowspore/fossil-delta/reader.go create mode 100644 vendor/github.com/shadowspore/fossil-delta/rolling_hash.go create mode 100644 vendor/github.com/shadowspore/fossil-delta/writer.go create mode 100644 vendor/github.com/valyala/bytebufferpool/.travis.yml create mode 100644 vendor/github.com/valyala/bytebufferpool/LICENSE create mode 100644 vendor/github.com/valyala/bytebufferpool/README.md create mode 100644 vendor/github.com/valyala/bytebufferpool/bytebuffer.go create mode 100644 vendor/github.com/valyala/bytebufferpool/doc.go create mode 100644 vendor/github.com/valyala/bytebufferpool/pool.go diff --git a/go.mod b/go.mod index e28d00e8ef..0035d1d23a 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/beevik/etree v1.6.0 github.com/blevesearch/bleve/v2 v2.5.7 github.com/cenkalti/backoff v2.2.1+incompatible + github.com/centrifugal/centrifuge-go v0.10.12 github.com/coreos/go-oidc/v3 v3.18.0 github.com/cs3org/go-cs3apis v0.0.0-20260407125717-5d69ba49048b github.com/davidbyttow/govips/v2 v2.17.0 @@ -163,6 +164,7 @@ require ( github.com/bombsimon/logrusr/v3 v3.1.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect + github.com/centrifugal/protocol v0.16.0 // indirect github.com/ceph/go-ceph v0.38.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73 // indirect @@ -244,6 +246,7 @@ require ( github.com/gookit/goutil v0.7.1 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/schema v1.4.1 // indirect + github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-plugin v1.7.0 // indirect @@ -255,6 +258,8 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jonboulle/clockwork v0.5.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/juliangruber/go-intersect v1.1.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect @@ -276,6 +281,7 @@ require ( github.com/longsleep/rndm v1.2.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.10 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -322,6 +328,7 @@ require ( github.com/philhofer/fwd v1.2.0 // indirect github.com/pierrec/lz4/v4 v4.1.15 // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/pquerna/cachecontrol v0.2.0 // indirect @@ -340,12 +347,14 @@ require ( github.com/samber/slog-common v0.21.0 // indirect github.com/samber/slog-zerolog/v2 v2.9.2 // indirect github.com/segmentio/asm v1.2.1 // indirect + github.com/segmentio/encoding v0.4.0 // indirect github.com/segmentio/kafka-go v0.4.50 // indirect github.com/segmentio/ksuid v1.0.4 // indirect github.com/sercand/kuberesolver/v5 v5.1.1 // indirect github.com/sergi/go-diff v1.4.0 // indirect github.com/sethvargo/go-diceware v0.5.0 // indirect github.com/sethvargo/go-password v0.3.1 // indirect + github.com/shadowspore/fossil-delta v0.0.0-20241213113458-1d797d70cbe3 // indirect github.com/shamaton/msgpack/v2 v2.4.0 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect @@ -366,6 +375,7 @@ require ( github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 // indirect github.com/trustelem/zxcvbn v1.0.1 // indirect github.com/urfave/cli/v2 v2.27.7 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fastjson v1.6.7 // indirect github.com/vektah/gqlparser/v2 v2.5.32 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect diff --git a/go.sum b/go.sum index 37cab7ca7a..79289af90e 100644 --- a/go.sum +++ b/go.sum @@ -208,6 +208,10 @@ github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1x github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/centrifugal/centrifuge-go v0.10.12 h1:CX8zgXJ/WD0psI1bMdLiyz0HmXV+xV+IzDkYgpfN58Q= +github.com/centrifugal/centrifuge-go v0.10.12/go.mod h1:yONZBopAssVw7mluSRs/6YTYkqKe+llSGIokNVlBDZ4= +github.com/centrifugal/protocol v0.16.0 h1:bAQm4YvONSPqq6kR8UgBNyf5Yh63AHKnjSKj/g9anPk= +github.com/centrifugal/protocol v0.16.0/go.mod h1:7V5vI30VcoxJe4UD87xi7bOsvI0bmEhvbQuMjrFM2L4= github.com/ceph/go-ceph v0.38.0 h1:Ux0sIpl6VJNgY21hxuBZI9Z2Z8tQsBMJhjLjYBoa7s0= github.com/ceph/go-ceph v0.38.0/go.mod h1:GQVPe5YWoCMOrGnpDDieQoQZRLkB0tJmIokbqxbwPBQ= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -607,6 +611,8 @@ github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWS github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E= github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= @@ -694,8 +700,11 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -793,6 +802,8 @@ github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= @@ -998,6 +1009,8 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= github.com/pkg/xattr v0.4.12 h1:rRTkSyFNTRElv6pkA3zpjHpQ90p/OdHQC1GmGh1aTjM= github.com/pkg/xattr v0.4.12/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -1096,6 +1109,8 @@ github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f/ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/segmentio/encoding v0.4.0 h1:MEBYvRqiUB2nfR2criEXWqwdY6HJOUrCn5hboVOVmy8= +github.com/segmentio/encoding v0.4.0/go.mod h1:/d03Cd8PoaDeceuhUUUQWjU0KhWjrmYrWPgtJHYZSnI= github.com/segmentio/kafka-go v0.4.50 h1:mcyC3tT5WeyWzrFbd6O374t+hmcu1NKt2Pu1L3QaXmc= github.com/segmentio/kafka-go v0.4.50/go.mod h1:Y1gn60kzLEEaW28YshXyk2+VCUKbJ3Qr6DrnT3i4+9E= github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= @@ -1108,6 +1123,8 @@ github.com/sethvargo/go-diceware v0.5.0 h1:exrQ7GpaBo00GqRVM1N8ChXSsi3oS7tjQiIeh github.com/sethvargo/go-diceware v0.5.0/go.mod h1:Lg1SyPS7yQO6BBgTN5r4f2MUDkqGfLWsOjHPY0kA8iw= github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU= github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs= +github.com/shadowspore/fossil-delta v0.0.0-20241213113458-1d797d70cbe3 h1:/4/IJi5iyTdh6mqOUaASW148HQpujYiHl0Wl78dSOSc= +github.com/shadowspore/fossil-delta v0.0.0-20241213113458-1d797d70cbe3/go.mod h1:aJIMhRsunltJR926EB2MUg8qHemFQDreSB33pyto2Ps= github.com/shamaton/msgpack/v2 v2.4.0 h1:O5Z08MRmbo0lA9o2xnQ4TXx6teJbPqEurqcCOQ8Oi/4= github.com/shamaton/msgpack/v2 v2.4.0/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= @@ -1230,6 +1247,7 @@ github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fastjson v1.6.7 h1:ZE4tRy0CIkh+qDc5McjatheGX2czdn8slQjomexVpBM= github.com/valyala/fastjson v1.6.7/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= diff --git a/opencloud/pkg/command/services.go b/opencloud/pkg/command/services.go index 3c0d3852d0..c1f80d4bde 100644 --- a/opencloud/pkg/command/services.go +++ b/opencloud/pkg/command/services.go @@ -19,6 +19,7 @@ import ( authservice "github.com/opencloud-eu/opencloud/services/auth-service/pkg/command" clientlog "github.com/opencloud-eu/opencloud/services/clientlog/pkg/command" collaboration "github.com/opencloud-eu/opencloud/services/collaboration/pkg/command" + console "github.com/opencloud-eu/opencloud/services/console/pkg/command" eventhistory "github.com/opencloud-eu/opencloud/services/eventhistory/pkg/command" frontend "github.com/opencloud-eu/opencloud/services/frontend/pkg/command" gateway "github.com/opencloud-eu/opencloud/services/gateway/pkg/command" @@ -113,6 +114,11 @@ var serviceCommands = []register.Command{ cfg.Collaboration.Commons = cfg.Commons }) }, + func(cfg *config.Config) *cobra.Command { + return ServiceCommand(cfg, cfg.Console.Service.Name, console.GetCommands(cfg.Console), func(c *config.Config) { + cfg.Console.Commons = cfg.Commons + }) + }, func(cfg *config.Config) *cobra.Command { return ServiceCommand(cfg, cfg.EventHistory.Service.Name, eventhistory.GetCommands(cfg.EventHistory), func(c *config.Config) { cfg.EventHistory.Commons = cfg.Commons diff --git a/opencloud/pkg/runtime/service/service.go b/opencloud/pkg/runtime/service/service.go index c83fdf78da..186f45e86b 100644 --- a/opencloud/pkg/runtime/service/service.go +++ b/opencloud/pkg/runtime/service/service.go @@ -30,6 +30,7 @@ import ( authservice "github.com/opencloud-eu/opencloud/services/auth-service/pkg/command" clientlog "github.com/opencloud-eu/opencloud/services/clientlog/pkg/command" collaboration "github.com/opencloud-eu/opencloud/services/collaboration/pkg/command" + console "github.com/opencloud-eu/opencloud/services/console/pkg/command" eventhistory "github.com/opencloud-eu/opencloud/services/eventhistory/pkg/command" frontend "github.com/opencloud-eu/opencloud/services/frontend/pkg/command" gateway "github.com/opencloud-eu/opencloud/services/gateway/pkg/command" @@ -327,6 +328,11 @@ func NewService(ctx context.Context, options ...Option) (*Service, error) { cfg.Collaboration.Commons = cfg.Commons return collaboration.Execute(cfg.Collaboration) }) + areg(opts.Config.Console.Service.Name, func(ctx context.Context, cfg *occfg.Config) error { + cfg.Console.Context = ctx + cfg.Console.Commons = cfg.Commons + return console.Execute(cfg.Console) + }) areg(opts.Config.Policies.Service.Name, func(ctx context.Context, cfg *occfg.Config) error { cfg.Policies.Context = ctx cfg.Policies.Commons = cfg.Commons diff --git a/pkg/config/config.go b/pkg/config/config.go index 36536c8064..89a66d8511 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -14,6 +14,7 @@ import ( authservice "github.com/opencloud-eu/opencloud/services/auth-service/pkg/config" clientlog "github.com/opencloud-eu/opencloud/services/clientlog/pkg/config" collaboration "github.com/opencloud-eu/opencloud/services/collaboration/pkg/config" + console "github.com/opencloud-eu/opencloud/services/console/pkg/config" eventhistory "github.com/opencloud-eu/opencloud/services/eventhistory/pkg/config" frontend "github.com/opencloud-eu/opencloud/services/frontend/pkg/config" gateway "github.com/opencloud-eu/opencloud/services/gateway/pkg/config" @@ -94,6 +95,7 @@ type Config struct { AuthService *authservice.Config `yaml:"auth_service"` Clientlog *clientlog.Config `yaml:"clientlog"` Collaboration *collaboration.Config `yaml:"collaboration"` + Console *console.Config `yaml:"console"` EventHistory *eventhistory.Config `yaml:"eventhistory"` Frontend *frontend.Config `yaml:"frontend"` Gateway *gateway.Config `yaml:"gateway"` diff --git a/pkg/config/defaultconfig.go b/pkg/config/defaultconfig.go index 59f5de02b2..bf775741b6 100644 --- a/pkg/config/defaultconfig.go +++ b/pkg/config/defaultconfig.go @@ -14,6 +14,7 @@ import ( authservice "github.com/opencloud-eu/opencloud/services/auth-service/pkg/config/defaults" clientlog "github.com/opencloud-eu/opencloud/services/clientlog/pkg/config/defaults" collaboration "github.com/opencloud-eu/opencloud/services/collaboration/pkg/config/defaults" + console "github.com/opencloud-eu/opencloud/services/console/pkg/config/defaults" eventhistory "github.com/opencloud-eu/opencloud/services/eventhistory/pkg/config/defaults" frontend "github.com/opencloud-eu/opencloud/services/frontend/pkg/config/defaults" gateway "github.com/opencloud-eu/opencloud/services/gateway/pkg/config/defaults" @@ -69,6 +70,7 @@ func DefaultConfig() *Config { AuthService: authservice.DefaultConfig(), Clientlog: clientlog.DefaultConfig(), Collaboration: collaboration.DefaultConfig(), + Console: console.DefaultConfig(), EventHistory: eventhistory.DefaultConfig(), Frontend: frontend.DefaultConfig(), Gateway: gateway.DefaultConfig(), diff --git a/services/console/.mockery.yaml b/services/console/.mockery.yaml new file mode 100644 index 0000000000..029be12592 --- /dev/null +++ b/services/console/.mockery.yaml @@ -0,0 +1,15 @@ +# maintain v2 separate mocks dir +dir: "{{.InterfaceDir}}/mocks" +structname: "{{.InterfaceName}}" +filename: "{{.InterfaceName | snakecase }}.go" +pkgname: mocks + +template: testify +packages: + github.com/opencloud-eu/opencloud/services/console/pkg/web: + config: + dir: mocks + interfaces: + Service: {} + Repository: {} + ConsoleRepository: {} diff --git a/services/console/Makefile b/services/console/Makefile new file mode 100644 index 0000000000..c1e60e5846 --- /dev/null +++ b/services/console/Makefile @@ -0,0 +1,16 @@ +SHELL := bash +NAME := console + +ifneq (, $(shell command -v go 2> /dev/null)) # suppress `command not found warnings` for non go targets in CI +include ../../.bingo/Variables.mk +endif + +include ../../.make/default.mk +include ../../.make/go.mk +include ../../.make/release.mk +include ../../.make/docs.mk + + +.PHONY: go-generate +go-generate: $(MOCKERY) + $(MOCKERY) diff --git a/services/console/mocks/console_repository.go b/services/console/mocks/console_repository.go new file mode 100644 index 0000000000..6b8ec6170b --- /dev/null +++ b/services/console/mocks/console_repository.go @@ -0,0 +1,101 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package mocks + +import ( + "context" + "io" + + mock "github.com/stretchr/testify/mock" +) + +// NewConsoleRepository creates a new instance of ConsoleRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewConsoleRepository(t interface { + mock.TestingT + Cleanup(func()) +}) *ConsoleRepository { + mock := &ConsoleRepository{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// ConsoleRepository is an autogenerated mock type for the ConsoleRepository type +type ConsoleRepository struct { + mock.Mock +} + +type ConsoleRepository_Expecter struct { + mock *mock.Mock +} + +func (_m *ConsoleRepository) EXPECT() *ConsoleRepository_Expecter { + return &ConsoleRepository_Expecter{mock: &_m.Mock} +} + +// ThemeGet provides a mock function for the type ConsoleRepository +func (_mock *ConsoleRepository) ThemeGet(ctx context.Context) (io.ReadCloser, error) { + ret := _mock.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for ThemeGet") + } + + var r0 io.ReadCloser + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context) (io.ReadCloser, error)); ok { + return returnFunc(ctx) + } + if returnFunc, ok := ret.Get(0).(func(context.Context) io.ReadCloser); ok { + r0 = returnFunc(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(io.ReadCloser) + } + } + if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = returnFunc(ctx) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// ConsoleRepository_ThemeGet_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ThemeGet' +type ConsoleRepository_ThemeGet_Call struct { + *mock.Call +} + +// ThemeGet is a helper method to define mock.On call +// - ctx context.Context +func (_e *ConsoleRepository_Expecter) ThemeGet(ctx interface{}) *ConsoleRepository_ThemeGet_Call { + return &ConsoleRepository_ThemeGet_Call{Call: _e.mock.On("ThemeGet", ctx)} +} + +func (_c *ConsoleRepository_ThemeGet_Call) Run(run func(ctx context.Context)) *ConsoleRepository_ThemeGet_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *ConsoleRepository_ThemeGet_Call) Return(readCloser io.ReadCloser, err error) *ConsoleRepository_ThemeGet_Call { + _c.Call.Return(readCloser, err) + return _c +} + +func (_c *ConsoleRepository_ThemeGet_Call) RunAndReturn(run func(ctx context.Context) (io.ReadCloser, error)) *ConsoleRepository_ThemeGet_Call { + _c.Call.Return(run) + return _c +} diff --git a/services/console/mocks/repository.go b/services/console/mocks/repository.go new file mode 100644 index 0000000000..8f47fc0014 --- /dev/null +++ b/services/console/mocks/repository.go @@ -0,0 +1,225 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package mocks + +import ( + "context" + "io" + + mock "github.com/stretchr/testify/mock" +) + +// NewRepository creates a new instance of Repository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewRepository(t interface { + mock.TestingT + Cleanup(func()) +}) *Repository { + mock := &Repository{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// Repository is an autogenerated mock type for the Repository type +type Repository struct { + mock.Mock +} + +type Repository_Expecter struct { + mock *mock.Mock +} + +func (_m *Repository) EXPECT() *Repository_Expecter { + return &Repository_Expecter{mock: &_m.Mock} +} + +// ThemeAdd provides a mock function for the type Repository +func (_mock *Repository) ThemeAdd(ctx context.Context, id string, r io.Reader) error { + ret := _mock.Called(ctx, id, r) + + if len(ret) == 0 { + panic("no return value specified for ThemeAdd") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, string, io.Reader) error); ok { + r0 = returnFunc(ctx, id, r) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// Repository_ThemeAdd_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ThemeAdd' +type Repository_ThemeAdd_Call struct { + *mock.Call +} + +// ThemeAdd is a helper method to define mock.On call +// - ctx context.Context +// - id string +// - r io.Reader +func (_e *Repository_Expecter) ThemeAdd(ctx interface{}, id interface{}, r interface{}) *Repository_ThemeAdd_Call { + return &Repository_ThemeAdd_Call{Call: _e.mock.On("ThemeAdd", ctx, id, r)} +} + +func (_c *Repository_ThemeAdd_Call) Run(run func(ctx context.Context, id string, r io.Reader)) *Repository_ThemeAdd_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 string + if args[1] != nil { + arg1 = args[1].(string) + } + var arg2 io.Reader + if args[2] != nil { + arg2 = args[2].(io.Reader) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *Repository_ThemeAdd_Call) Return(err error) *Repository_ThemeAdd_Call { + _c.Call.Return(err) + return _c +} + +func (_c *Repository_ThemeAdd_Call) RunAndReturn(run func(ctx context.Context, id string, r io.Reader) error) *Repository_ThemeAdd_Call { + _c.Call.Return(run) + return _c +} + +// ThemeExists provides a mock function for the type Repository +func (_mock *Repository) ThemeExists(ctx context.Context, id string) (bool, error) { + ret := _mock.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for ThemeExists") + } + + var r0 bool + var r1 error + if returnFunc, ok := ret.Get(0).(func(context.Context, string) (bool, error)); ok { + return returnFunc(ctx, id) + } + if returnFunc, ok := ret.Get(0).(func(context.Context, string) bool); ok { + r0 = returnFunc(ctx, id) + } else { + r0 = ret.Get(0).(bool) + } + if returnFunc, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = returnFunc(ctx, id) + } else { + r1 = ret.Error(1) + } + return r0, r1 +} + +// Repository_ThemeExists_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ThemeExists' +type Repository_ThemeExists_Call struct { + *mock.Call +} + +// ThemeExists is a helper method to define mock.On call +// - ctx context.Context +// - id string +func (_e *Repository_Expecter) ThemeExists(ctx interface{}, id interface{}) *Repository_ThemeExists_Call { + return &Repository_ThemeExists_Call{Call: _e.mock.On("ThemeExists", ctx, id)} +} + +func (_c *Repository_ThemeExists_Call) Run(run func(ctx context.Context, id string)) *Repository_ThemeExists_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 string + if args[1] != nil { + arg1 = args[1].(string) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *Repository_ThemeExists_Call) Return(b bool, err error) *Repository_ThemeExists_Call { + _c.Call.Return(b, err) + return _c +} + +func (_c *Repository_ThemeExists_Call) RunAndReturn(run func(ctx context.Context, id string) (bool, error)) *Repository_ThemeExists_Call { + _c.Call.Return(run) + return _c +} + +// ThemeRemove provides a mock function for the type Repository +func (_mock *Repository) ThemeRemove(ctx context.Context, id string) error { + ret := _mock.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for ThemeRemove") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = returnFunc(ctx, id) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// Repository_ThemeRemove_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ThemeRemove' +type Repository_ThemeRemove_Call struct { + *mock.Call +} + +// ThemeRemove is a helper method to define mock.On call +// - ctx context.Context +// - id string +func (_e *Repository_Expecter) ThemeRemove(ctx interface{}, id interface{}) *Repository_ThemeRemove_Call { + return &Repository_ThemeRemove_Call{Call: _e.mock.On("ThemeRemove", ctx, id)} +} + +func (_c *Repository_ThemeRemove_Call) Run(run func(ctx context.Context, id string)) *Repository_ThemeRemove_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 string + if args[1] != nil { + arg1 = args[1].(string) + } + run( + arg0, + arg1, + ) + }) + return _c +} + +func (_c *Repository_ThemeRemove_Call) Return(err error) *Repository_ThemeRemove_Call { + _c.Call.Return(err) + return _c +} + +func (_c *Repository_ThemeRemove_Call) RunAndReturn(run func(ctx context.Context, id string) error) *Repository_ThemeRemove_Call { + _c.Call.Return(run) + return _c +} diff --git a/services/console/mocks/service.go b/services/console/mocks/service.go new file mode 100644 index 0000000000..222afd015b --- /dev/null +++ b/services/console/mocks/service.go @@ -0,0 +1,140 @@ +// Code generated by mockery; DO NOT EDIT. +// github.com/vektra/mockery +// template: testify + +package mocks + +import ( + "context" + + mock "github.com/stretchr/testify/mock" +) + +// NewService creates a new instance of Service. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewService(t interface { + mock.TestingT + Cleanup(func()) +}) *Service { + mock := &Service{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// Service is an autogenerated mock type for the Service type +type Service struct { + mock.Mock +} + +type Service_Expecter struct { + mock *mock.Mock +} + +func (_m *Service) EXPECT() *Service_Expecter { + return &Service_Expecter{mock: &_m.Mock} +} + +// ThemeApply provides a mock function for the type Service +func (_mock *Service) ThemeApply(context1 context.Context) error { + ret := _mock.Called(context1) + + if len(ret) == 0 { + panic("no return value specified for ThemeApply") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = returnFunc(context1) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// Service_ThemeApply_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ThemeApply' +type Service_ThemeApply_Call struct { + *mock.Call +} + +// ThemeApply is a helper method to define mock.On call +// - context1 context.Context +func (_e *Service_Expecter) ThemeApply(context1 interface{}) *Service_ThemeApply_Call { + return &Service_ThemeApply_Call{Call: _e.mock.On("ThemeApply", context1)} +} + +func (_c *Service_ThemeApply_Call) Run(run func(context1 context.Context)) *Service_ThemeApply_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *Service_ThemeApply_Call) Return(err error) *Service_ThemeApply_Call { + _c.Call.Return(err) + return _c +} + +func (_c *Service_ThemeApply_Call) RunAndReturn(run func(context1 context.Context) error) *Service_ThemeApply_Call { + _c.Call.Return(run) + return _c +} + +// ThemeRemove provides a mock function for the type Service +func (_mock *Service) ThemeRemove(context1 context.Context) error { + ret := _mock.Called(context1) + + if len(ret) == 0 { + panic("no return value specified for ThemeRemove") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = returnFunc(context1) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// Service_ThemeRemove_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ThemeRemove' +type Service_ThemeRemove_Call struct { + *mock.Call +} + +// ThemeRemove is a helper method to define mock.On call +// - context1 context.Context +func (_e *Service_Expecter) ThemeRemove(context1 interface{}) *Service_ThemeRemove_Call { + return &Service_ThemeRemove_Call{Call: _e.mock.On("ThemeRemove", context1)} +} + +func (_c *Service_ThemeRemove_Call) Run(run func(context1 context.Context)) *Service_ThemeRemove_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + run( + arg0, + ) + }) + return _c +} + +func (_c *Service_ThemeRemove_Call) Return(err error) *Service_ThemeRemove_Call { + _c.Call.Return(err) + return _c +} + +func (_c *Service_ThemeRemove_Call) RunAndReturn(run func(context1 context.Context) error) *Service_ThemeRemove_Call { + _c.Call.Return(run) + return _c +} diff --git a/services/console/pkg/command/command.go b/services/console/pkg/command/command.go new file mode 100644 index 0000000000..3328f31049 --- /dev/null +++ b/services/console/pkg/command/command.go @@ -0,0 +1,15 @@ +package command + +import ( + "github.com/spf13/cobra" + + "github.com/opencloud-eu/opencloud/pkg/config/configlog" + "github.com/opencloud-eu/opencloud/services/console/pkg/config" + "github.com/opencloud-eu/opencloud/services/console/pkg/config/parser" +) + +func preRunE(cfg *config.Config) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + return configlog.ReturnError(parser.ParseConfig(cfg)) + } +} diff --git a/services/console/pkg/command/health.go b/services/console/pkg/command/health.go new file mode 100644 index 0000000000..573d5f1f20 --- /dev/null +++ b/services/console/pkg/command/health.go @@ -0,0 +1,22 @@ +package command + +import ( + "github.com/spf13/cobra" + + "github.com/opencloud-eu/opencloud/services/console/pkg/config" +) + +// Health is the entrypoint for the health command. +func Health(cfg *config.Config) *cobra.Command { + return &cobra.Command{ + Use: "health", + Short: "check health status", + PreRunE: preRunE(cfg), + RunE: func(cmd *cobra.Command, args []string) error { + // fixMe: what to check? + // - remote service health // conn + // - web service rpc health, theme state? + return nil + }, + } +} diff --git a/services/console/pkg/command/root.go b/services/console/pkg/command/root.go new file mode 100644 index 0000000000..88197e0b55 --- /dev/null +++ b/services/console/pkg/command/root.go @@ -0,0 +1,31 @@ +package command + +import ( + "os" + + "github.com/opencloud-eu/opencloud/pkg/clihelper" + "github.com/opencloud-eu/opencloud/services/console/pkg/config" + + "github.com/spf13/cobra" +) + +// GetCommands provides all commands for this service +func GetCommands(cfg *config.Config) []*cobra.Command { + return []*cobra.Command{ + Update(cfg), + Health(cfg), + Version(cfg), + } +} + +// Execute is the entry point for the web command. +func Execute(cfg *config.Config) error { + app := clihelper.DefaultApp(&cobra.Command{ + Use: "console", + Short: "OpenCloud console", + }) + app.AddCommand(GetCommands(cfg)...) + app.SetArgs(os.Args[1:]) + + return app.ExecuteContext(cfg.Context) +} diff --git a/services/console/pkg/command/update.go b/services/console/pkg/command/update.go new file mode 100644 index 0000000000..f042dbffe7 --- /dev/null +++ b/services/console/pkg/command/update.go @@ -0,0 +1,194 @@ +package command + +import ( + "fmt" + "net/http" + + "github.com/centrifugal/centrifuge-go" + "github.com/golang-jwt/jwt/v5" + "github.com/spf13/cobra" + + "github.com/opencloud-eu/reva/v2/pkg/events/stream" + + "github.com/opencloud-eu/opencloud/pkg/generators" + "github.com/opencloud-eu/opencloud/pkg/log" + websvc "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/web/v0" + "github.com/opencloud-eu/opencloud/services/console/pkg/config" + "github.com/opencloud-eu/opencloud/services/console/pkg/console" + "github.com/opencloud-eu/opencloud/services/console/pkg/console/remote" + "github.com/opencloud-eu/opencloud/services/console/pkg/features" + "github.com/opencloud-eu/opencloud/services/console/pkg/web" +) + +func Update(cfg *config.Config) *cobra.Command { + connectCmd := &cobra.Command{ + Use: "update", + Short: "update instance related console settings", + } + + subscribeCmd := &cobra.Command{ + Use: "subscribe", + Short: "get updates automatically", + PreRunE: preRunE(cfg), + RunE: func(cmd *cobra.Command, args []string) error { + common, err := newCommons(cfg) + if err != nil { + return fmt.Errorf("failed to build environment: %w", err) + } + + client, err := remote.NewCentrifugoSubscription(remote.CentrifugoSubscriptionOptions{ + Token: common.token, + URLBuilder: common.urlBuilder, + Claims: common.claims, + CentrifugeConfig: centrifuge.Config{ + Token: common.token.Raw, + TLSConfig: features.DefaultTLSConfig, + }, + CentrifugeSubscriptionConfig: centrifuge.SubscriptionConfig{ + Recoverable: true, + JoinLeave: true, + }, + Logger: common.logger, + }) + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + defer func() { + _ = client.Close() + }() + + handler := remote.Handler{ + ThemeAssigned: func(m remote.Message[remote.MessagePayloadThemeAssigned]) error { + return common.webService.ThemeApply(cmd.Context()) + }, + ThemeUnassigned: func(m remote.Message[struct{}]) error { + return common.webService.ThemeRemove(cmd.Context()) + }, + } + + if err := client.Handle(handler); err != nil { + return fmt.Errorf("failed to subscribe: %w", err) + } + + <-cmd.Context().Done() + + return nil + }, + } + + runCmd := &cobra.Command{ + Use: "run", + Short: "run update related commands manually", + } + + { + webThemeGetCmd := &cobra.Command{ + Use: "web-theme-get", + Short: "download and apply configured console web theme", + PreRunE: preRunE(cfg), + RunE: func(cmd *cobra.Command, args []string) error { + common, err := newCommons(cfg) + if err != nil { + return fmt.Errorf("failed to build environment: %w", err) + } + + if err := common.webService.ThemeApply(cmd.Context()); err != nil { + return fmt.Errorf("failed to apply theme: %w", err) + } + + return nil + }, + } + + runCmd.AddCommand( + webThemeGetCmd, + ) + } + + connectCmd.AddCommand( + runCmd, + subscribeCmd, + ) + + return connectCmd +} + +type commons struct { + token *jwt.Token + claims *console.Claims + webService web.Service + logger log.Logger + urlBuilder console.URLBuilder +} + +func newCommons(cfg *config.Config) (commons, error) { + grpcClient, err := console.NewGRPCClient(cfg.Context, cfg.Commons.TracesExporter, cfg.Service.Name, cfg.GRPCClientTLS) + if err != nil { + return commons{}, err + } + + token, claims, err := console.ParseUnverifiedJWTToken(cfg.RemoteConsole.JWTToken) + if err != nil { + return commons{}, err + } + + urlBuilder, err := console.NewURLBuilder(claims) + if err != nil { + return commons{}, err + } + + logger := log.Configure(cfg.Service.Name, cfg.Commons, cfg.LogLevel) + consoleRepository, err := remote.NewHTTPRepository(remote.HTTPRepositoryOptions{ + Client: &http.Client{Transport: features.DefaultHTTPTransport}, + Token: token, + Claims: claims, + URLBuilder: urlBuilder, + Logger: logger, + }) + if err != nil { + return commons{}, fmt.Errorf("failed to create console repository: %w", err) + } + + webRepository, err := web.NewGRPCRepository(web.GRPCRepositoryOptions{ + WebService: websvc.NewWebService("eu.opencloud.api.web", grpcClient), + Logger: logger, + }) + if err != nil { + return commons{}, fmt.Errorf("failed to create web repository: %w", err) + } + + eventStream, err := stream.NatsFromConfig( + generators.GenerateConnectionName(cfg.Service.Name, generators.NTypeBus), + false, + stream.NatsConfig(cfg.Events), + ) + if err != nil { + return commons{}, err + } + + webSSEService, err := web.NewSSEService(web.SSEServiceOptions{ + EventStream: eventStream, + Logger: logger, + }) + if err != nil { + return commons{}, fmt.Errorf("failed to create web service: %w", err) + } + + webCoreService, err := web.NewCoreService(web.CoreServiceOptions{ + Repository: webRepository, + ConsoleRepository: consoleRepository, + Next: webSSEService, + Logger: logger, + }) + if err != nil { + return commons{}, fmt.Errorf("failed to create web service: %w", err) + } + + return commons{ + webService: webCoreService, + token: token, + claims: claims, + logger: logger, + urlBuilder: urlBuilder, + }, nil +} diff --git a/services/console/pkg/command/version.go b/services/console/pkg/command/version.go new file mode 100644 index 0000000000..02de50d9f1 --- /dev/null +++ b/services/console/pkg/command/version.go @@ -0,0 +1,51 @@ +package command + +import ( + "fmt" + "os" + + "github.com/opencloud-eu/opencloud/pkg/registry" + "github.com/opencloud-eu/opencloud/pkg/version" + "github.com/opencloud-eu/opencloud/services/console/pkg/config" + + "github.com/olekukonko/tablewriter" + "github.com/olekukonko/tablewriter/tw" + "github.com/spf13/cobra" +) + +// Version prints the service versions of all running instances. +func Version(cfg *config.Config) *cobra.Command { + return &cobra.Command{ + Use: "version", + Short: "print the version of this binary and the running service instances", + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println("Version: " + version.GetString()) + fmt.Printf("Compiled: %s\n", version.Compiled()) + fmt.Println("") + + reg := registry.GetRegistry() + services, err := reg.GetService(cfg.GRPC.Namespace + "." + cfg.Service.Name) + if err != nil { + fmt.Println(fmt.Errorf("could not get %s services from the registry: %v", cfg.Service.Name, err)) + return err + } + + if len(services) == 0 { + fmt.Println("No running " + cfg.Service.Name + " service found.") + return nil + } + + table := tablewriter.NewTable(os.Stdout, tablewriter.WithHeaderAutoFormat(tw.Off)) + table.Header([]string{"Version", "Address", "Id"}) + for _, s := range services { + for _, n := range s.Nodes { + if err := table.Append([]string{s.Version, n.Address, n.Id}); err != nil { + return err + } + } + } + + return table.Render() + }, + } +} diff --git a/services/console/pkg/config/config.go b/services/console/pkg/config/config.go new file mode 100644 index 0000000000..abd9cd22b2 --- /dev/null +++ b/services/console/pkg/config/config.go @@ -0,0 +1,69 @@ +package config + +import ( + "context" + + "go-micro.dev/v4/client" + + "github.com/opencloud-eu/opencloud/pkg/shared" +) + +// Config combines all available configuration parts. +type Config struct { + Commons *shared.Commons `yaml:"-"` // don't use this directly as configuration for a service + + Service Service `yaml:"-"` + + LogLevel string `yaml:"loglevel" env:"OC_LOG_LEVEL;THUMBNAILS_LOG_LEVEL" desc:"The log level. Valid values are: 'panic', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'." introductionVersion:"1.0.0"` + Debug Debug `yaml:"debug"` + + GRPC GRPCConfig `yaml:"grpc"` + + GRPCClientTLS *shared.GRPCClientTLS `yaml:"grpc_client_tls"` + GrpcClient client.Client `yaml:"-"` + Events Events `yaml:"events"` + + RemoteConsole *ConsoleRemote `yaml:"remote"` + + Context context.Context `yaml:"-"` +} + +// Service defines the available service configuration. +type Service struct { + Name string `yaml:"-"` +} + +// Debug defines the available debug configuration. +type Debug struct { + Addr string `yaml:"addr" env:"CONSOLE_DEBUG_ADDR" desc:"Bind address of the debug server, where metrics, health, config and debug endpoints will be exposed." introductionVersion:"%%NEXT%%"` + Token string `yaml:"token" env:"CONSOLE_DEBUG_TOKEN" desc:"Token to secure the metrics endpoint." introductionVersion:"%%NEXT%%"` + Pprof bool `yaml:"pprof" env:"CONSOLE_DEBUG_PPROF" desc:"Enables pprof, which can be used for profiling." introductionVersion:"%%NEXT%%"` + Zpages bool `yaml:"zpages" env:"CONSOLE_DEBUG_ZPAGES" desc:"Enables zpages, which can be used for collecting and viewing in-memory traces." introductionVersion:"%%NEXT%%"` +} + +// GRPCConfig defines the available grpc configuration. +type GRPCConfig struct { + Disabled bool `yaml:"disabled" env:"CONSOLE_GRPC_DISABLED" desc:"Disables the GRPC service. Set this to true if the service should only handle events." introductionVersion:"%%NEXT%%"` + Addr string `yaml:"addr" env:"CONSOLE_GRPC_ADDR" desc:"The bind address of the GRPC service." introductionVersion:"%%NEXT%%"` + Namespace string `yaml:"-"` + TLS *shared.GRPCServiceTLS `yaml:"tls"` +} + +// Tracing defines the available tracing configuration. +type Tracing struct { + Enabled bool `yaml:"enabled" env:"OC_TRACING_ENABLED;CONSOLE_TRACING_ENABLED" desc:"Activates tracing." introductionVersion:"%%NEXT%%"` + Type string `yaml:"type" env:"OC_TRACING_TYPE;CONSOLE_TRACING_TYPE" desc:"The type of tracing. Defaults to '', which is the same as 'jaeger'. Allowed tracing types are 'jaeger' and '' as of now." introductionVersion:"%%NEXT%%"` + Endpoint string `yaml:"endpoint" env:"OC_TRACING_ENDPOINT;CONSOLE_TRACING_ENDPOINT" desc:"The endpoint of the tracing agent." introductionVersion:"%%NEXT%%"` + Collector string `yaml:"collector" env:"OC_TRACING_COLLECTOR;CONSOLE_TRACING_COLLECTOR" desc:"The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset." introductionVersion:"%%NEXT%%"` +} + +// Events combines the configuration options for the event bus. +type Events struct { + Endpoint string `yaml:"endpoint" env:"OC_EVENTS_ENDPOINT;CONSOLE_EVENTS_ENDPOINT" desc:"The address of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture." introductionVersion:"1.0.0"` + Cluster string `yaml:"cluster" env:"OC_EVENTS_CLUSTER;CONSOLE_EVENTS_CLUSTER" desc:"The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system." introductionVersion:"1.0.0"` + TLSInsecure bool `yaml:"tls_insecure" env:"OC_INSECURE;OC_EVENTS_TLS_INSECURE;CONSOLE_EVENTS_TLS_INSECURE" desc:"Whether to verify the server TLS certificates." introductionVersion:"1.0.0"` + TLSRootCACertificate string `yaml:"tls_root_ca_certificate" env:"OC_EVENTS_TLS_ROOT_CA_CERTIFICATE;CONSOLE_EVENTS_TLS_ROOT_CA_CERTIFICATE" desc:"The root CA certificate used to validate the server's TLS certificate. If provided NOTIFICATIONS_EVENTS_TLS_INSECURE will be seen as false." introductionVersion:"1.0.0"` + EnableTLS bool `yaml:"enable_tls" env:"OC_EVENTS_ENABLE_TLS;CONSOLE_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the OpenCloud service which receives and delivers events between the services." introductionVersion:"1.0.0"` + AuthUsername string `yaml:"username" env:"OC_EVENTS_AUTH_USERNAME;CONSOLE_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the OpenCloud service which receives and delivers events between the services." introductionVersion:"1.0.0"` + AuthPassword string `yaml:"password" env:"OC_EVENTS_AUTH_PASSWORD;CONSOLE_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the OpenCloud service which receives and delivers events between the services." introductionVersion:"1.0.0"` +} diff --git a/services/console/pkg/config/console.go b/services/console/pkg/config/console.go new file mode 100644 index 0000000000..24359bee56 --- /dev/null +++ b/services/console/pkg/config/console.go @@ -0,0 +1,5 @@ +package config + +type ConsoleRemote struct { + JWTToken string `yaml:"jwt_token" env:"CONSOLE_REMOTE_JWT_TOKEN" desc:"The JWT token used to authenticate requests to the console service." introductionVersion:"%%NEXT%%"` +} diff --git a/services/console/pkg/config/defaults/defaultconfig.go b/services/console/pkg/config/defaults/defaultconfig.go new file mode 100644 index 0000000000..672391fe5b --- /dev/null +++ b/services/console/pkg/config/defaults/defaultconfig.go @@ -0,0 +1,58 @@ +package defaults + +import ( + "github.com/opencloud-eu/opencloud/pkg/structs" + "github.com/opencloud-eu/opencloud/services/console/pkg/config" +) + +// FullDefaultConfig returns a fully initialized default configuration +func FullDefaultConfig() *config.Config { + cfg := DefaultConfig() + EnsureDefaults(cfg) + Sanitize(cfg) + return cfg +} + +// DefaultConfig returns a basic default configuration +func DefaultConfig() *config.Config { + return &config.Config{ + Debug: config.Debug{ + Addr: "127.0.0.1:9225", + Token: "", + }, + GRPC: config.GRPCConfig{ + Addr: "127.0.0.1:9222", + Namespace: "eu.opencloud.api", + }, + Service: config.Service{ + Name: "console", + }, + Events: config.Events{ + Endpoint: "127.0.0.1:9233", + Cluster: "opencloud-cluster", + EnableTLS: false, + }, + } +} + +// EnsureDefaults adds default values to the configuration if they are not set yet +func EnsureDefaults(cfg *config.Config) { + if cfg.LogLevel == "" { + cfg.LogLevel = "error" + } + + if cfg.GRPC.TLS == nil && cfg.Commons != nil { + cfg.GRPC.TLS = structs.CopyOrZeroValue(cfg.Commons.GRPCServiceTLS) + } + + if cfg.RemoteConsole == nil { + cfg.RemoteConsole = &config.ConsoleRemote{} + } +} + +// Sanitize sanitizes the configuration +func Sanitize(cfg *config.Config) { + if cfg.GRPCClientTLS == nil && cfg.Commons != nil { + cfg.GRPCClientTLS = structs.CopyOrZeroValue(cfg.Commons.GRPCClientTLS) + } +} diff --git a/services/console/pkg/config/parser/parse.go b/services/console/pkg/config/parser/parse.go new file mode 100644 index 0000000000..bbc11eb625 --- /dev/null +++ b/services/console/pkg/config/parser/parse.go @@ -0,0 +1,41 @@ +package parser + +import ( + "errors" + + occfg "github.com/opencloud-eu/opencloud/pkg/config" + "github.com/opencloud-eu/opencloud/services/console/pkg/config" + "github.com/opencloud-eu/opencloud/services/console/pkg/config/defaults" + + "github.com/opencloud-eu/opencloud/pkg/config/envdecode" +) + +// ParseConfig loads configuration from known paths. +func ParseConfig(cfg *config.Config) error { + err := occfg.BindSourcesToStructs(cfg.Service.Name, cfg) + if err != nil { + return err + } + + defaults.EnsureDefaults(cfg) + + // load all env variables relevant to the config in the current context. + if err := envdecode.Decode(cfg); err != nil { + // no environment variable set for this config is an expected "error" + if !errors.Is(err, envdecode.ErrNoTargetFieldsAreSet) { + return err + } + } + + defaults.Sanitize(cfg) + + return Validate(cfg) +} + +func Validate(cfg *config.Config) error { + if cfg.RemoteConsole.JWTToken == "" { + return errors.New("jwt_token must be provided in remote console config") + } + + return nil +} diff --git a/services/console/pkg/console/console.go b/services/console/pkg/console/console.go new file mode 100644 index 0000000000..85b0fb8221 --- /dev/null +++ b/services/console/pkg/console/console.go @@ -0,0 +1,14 @@ +package console + +import ( + "errors" + + "github.com/go-playground/validator/v10" +) + +var ( + Validate = validator.New() + ErrValidation = errors.New("failed to validate") + ErrOptionsInvalid = errors.New("options are invalid") + ErrRequest = errors.New("request failed") +) diff --git a/services/console/pkg/console/grpc.go b/services/console/pkg/console/grpc.go new file mode 100644 index 0000000000..ba63c6a1e8 --- /dev/null +++ b/services/console/pkg/console/grpc.go @@ -0,0 +1,30 @@ +package console + +import ( + "context" + + "go-micro.dev/v4/client" + + "github.com/opencloud-eu/opencloud/pkg/service/grpc" + "github.com/opencloud-eu/opencloud/pkg/shared" + + "github.com/opencloud-eu/opencloud/pkg/tracing" +) + +func NewGRPCClient(ctx context.Context, exporter, name string, tlsConfig *shared.GRPCClientTLS) (client.Client, error) { + traceProvider, err := tracing.GetTraceProvider(ctx, exporter, name) + if err != nil { + return nil, err + } + + grpcClient, err := grpc.NewClient( + append(grpc.GetClientOptions(tlsConfig), + grpc.WithTraceProvider(traceProvider), + )..., + ) + if err != nil { + return nil, err + } + + return grpcClient, nil +} diff --git a/services/console/pkg/console/http.go b/services/console/pkg/console/http.go new file mode 100644 index 0000000000..d736d184f6 --- /dev/null +++ b/services/console/pkg/console/http.go @@ -0,0 +1,35 @@ +package console + +import ( + "crypto/tls" + "fmt" + "io" + "net/http" +) + +var DefaultHTTPClient = &http.Client{Transport: func() *http.Transport { + t := http.DefaultTransport.(*http.Transport).Clone() + t.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} // toDo: dev build flag + return t +}()} + +type HTTPRequest struct { + *http.Request +} + +func NewHTTPRequest(method, url string, body io.Reader) (*HTTPRequest, error) { + req, err := http.NewRequest(method, url, body) + if err != nil { + return nil, fmt.Errorf("failed to create http request: %w", err) + } + + return &HTTPRequest{req}, nil +} + +func (r *HTTPRequest) SetBearerAuth(bearer string) { + r.Header.Add("Authorization", "Bearer "+bearer) +} + +func (r *HTTPRequest) AsDefault() *http.Request { + return r.Request +} diff --git a/services/console/pkg/console/jwt.go b/services/console/pkg/console/jwt.go new file mode 100644 index 0000000000..b3ccf671bd --- /dev/null +++ b/services/console/pkg/console/jwt.go @@ -0,0 +1,30 @@ +package console + +import ( + "fmt" + + "github.com/golang-jwt/jwt/v5" +) + +type Claims struct { + jwt.RegisteredClaims + TenantId string `json:"tenant_id,omitempty" validate:"required"` + InstanceId string `json:"instance_id,omitempty" validate:"required"` + APIVersion string `json:"api_version,omitempty" validate:"required"` + WebsocketURL string `json:"websocket_url,omitempty" validate:"required"` +} + +func ParseUnverifiedJWTToken(tokenString string) (*jwt.Token, *Claims, error) { + claims := &Claims{} + + token, _, err := new(jwt.Parser).ParseUnverified(tokenString, claims) + if err != nil { + return nil, nil, fmt.Errorf("failed to parse token: %w", err) + } + + if err := Validate.Struct(claims); err != nil { + return nil, nil, fmt.Errorf("(%w): %w", ErrValidation, err) + } + + return token, claims, nil +} diff --git a/services/console/pkg/console/remote/remote.go b/services/console/pkg/console/remote/remote.go new file mode 100644 index 0000000000..fbe5b64eba --- /dev/null +++ b/services/console/pkg/console/remote/remote.go @@ -0,0 +1 @@ +package remote diff --git a/services/console/pkg/console/remote/repository.go b/services/console/pkg/console/remote/repository.go new file mode 100644 index 0000000000..e9cb30e721 --- /dev/null +++ b/services/console/pkg/console/remote/repository.go @@ -0,0 +1,71 @@ +package remote + +import ( + "context" + "fmt" + "io" + "net/http" + + "github.com/golang-jwt/jwt/v5" + + "github.com/opencloud-eu/opencloud/pkg/log" + + "github.com/opencloud-eu/opencloud/services/console/pkg/console" +) + +type HTTPRepositoryOptions struct { + Client *http.Client `validate:"required"` + Token *jwt.Token `validate:"required"` + URLBuilder console.URLBuilder + Claims *console.Claims `validate:"required"` + Logger log.Logger +} + +func (o HTTPRepositoryOptions) Validate() error { + if err := console.Validate.Struct(o); err != nil { + return fmt.Errorf("(%w): %w", console.ErrOptionsInvalid, err) + } + + return nil +} + +type HTTPRepository struct { + client *http.Client + token *jwt.Token + claims *console.Claims + urlBuilder console.URLBuilder + logger log.Logger +} + +func NewHTTPRepository(o HTTPRepositoryOptions) (HTTPRepository, error) { + if err := o.Validate(); err != nil { + return HTTPRepository{}, err + } + + return HTTPRepository{ + client: o.Client, + token: o.Token, + urlBuilder: o.URLBuilder, + claims: o.Claims, + logger: o.Logger, + }, nil +} + +func (r HTTPRepository) ThemeGet(ctx context.Context) (io.ReadCloser, error) { + req, err := console.NewHTTPRequest(http.MethodGet, r.urlBuilder.APIUrl("deployment", "theme").String(), nil) + if err != nil { + return nil, fmt.Errorf("failed to create http request: %w", err) + } + req.SetBearerAuth(r.token.Raw) + req.WithContext(ctx) + + res, err := r.client.Do(req.AsDefault()) + switch { + case err != nil: + return nil, fmt.Errorf("(%w) failed to execute http request: %w", console.ErrRequest, err) + case res.StatusCode != http.StatusOK: + return nil, fmt.Errorf("(%w) failed to fetch theme, status code: %d", console.ErrRequest, res.StatusCode) + } + + return res.Body, nil +} diff --git a/services/console/pkg/console/remote/subscription.go b/services/console/pkg/console/remote/subscription.go new file mode 100644 index 0000000000..f8127569f1 --- /dev/null +++ b/services/console/pkg/console/remote/subscription.go @@ -0,0 +1,41 @@ +package remote + +import ( + "encoding/json" + "time" +) + +type Notification any + +type Topic string + +const ( + TopicThemeAssigned Topic = "theme.assigned" + TopicThemeUnassigned Topic = "theme.unassigned" +) + +type Message[P any] struct { + Topic Topic `json:"topic"` + TenantId string `json:"tenantId"` + InstanceId string `json:"instanceId"` + Timestamp time.Time `json:"timestamp"` + Payload P `json:"payload"` +} + +type MessagePayloadThemeAssigned struct { + ThemeId string `json:"themeId"` +} + +func dispatchMessage[T any](h func(message Message[T]) error, b []byte) error { + var message Message[T] + if err := json.Unmarshal(b, &message); err != nil { + return err + } + + return h(message) +} + +type Handler struct { + ThemeAssigned func(Message[MessagePayloadThemeAssigned]) error + ThemeUnassigned func(Message[struct{}]) error +} diff --git a/services/console/pkg/console/remote/subscription_centrifugo.go b/services/console/pkg/console/remote/subscription_centrifugo.go new file mode 100644 index 0000000000..fb88b06991 --- /dev/null +++ b/services/console/pkg/console/remote/subscription_centrifugo.go @@ -0,0 +1,125 @@ +package remote + +import ( + "fmt" + + "github.com/centrifugal/centrifuge-go" + "github.com/golang-jwt/jwt/v5" + + "github.com/opencloud-eu/opencloud/pkg/log" + "github.com/opencloud-eu/opencloud/services/console/pkg/console" +) + +type CentrifugoSubscriptionOptions struct { + Token *jwt.Token `validate:"required"` + Claims *console.Claims `validate:"required"` + CentrifugeConfig centrifuge.Config `validate:"required"` + CentrifugeSubscriptionConfig centrifuge.SubscriptionConfig `validate:"required"` + URLBuilder console.URLBuilder + Logger log.Logger +} + +func (o CentrifugoSubscriptionOptions) Validate() error { + if err := console.Validate.Struct(o); err != nil { + return fmt.Errorf("(%w): %w", console.ErrOptionsInvalid, err) + } + + return nil +} + +type CentrifugoSubscription struct { + client *centrifuge.Client + subscriptionConfig centrifuge.SubscriptionConfig + channel string + logger log.Logger +} + +func NewCentrifugoSubscription(o CentrifugoSubscriptionOptions) (CentrifugoSubscription, error) { + if err := o.Validate(); err != nil { + return CentrifugoSubscription{}, err + } + + client := centrifuge.NewJsonClient(o.URLBuilder.SubscriptionUrl().String(), o.CentrifugeConfig) + if err := client.Connect(); err != nil { + return CentrifugoSubscription{}, fmt.Errorf("failed to connect to centrifugo: %w", err) + } + + client.OnConnected(func(_ centrifuge.ConnectedEvent) { + o.Logger.Info().Msg("console connection established") + }) + + client.OnConnecting(func(_ centrifuge.ConnectingEvent) { + o.Logger.Info().Msg("connecting to console") + }) + + client.OnDisconnected(func(e centrifuge.DisconnectedEvent) { + o.Logger.Info().Interface("event", e).Msg("console connection closed") + }) + + client.OnError(func(e centrifuge.ErrorEvent) { + o.Logger.Error().Err(e.Error).Msg("console connection closed") + }) + + return CentrifugoSubscription{ + client: client, + channel: fmt.Sprintf("#%s", o.Claims.TenantId), + subscriptionConfig: o.CentrifugeSubscriptionConfig, + logger: o.Logger, + }, nil +} + +func (cs CentrifugoSubscription) Close() error { + if cs.client != nil { + cs.client.Close() + } + + return nil +} + +func (cs CentrifugoSubscription) Handle(handler Handler) error { + if cs.client == nil { + return fmt.Errorf("client not initialized") + } + + subscription, err := cs.client.NewSubscription(cs.channel, cs.subscriptionConfig) + if err != nil { + return err + } + + subscription.OnSubscribed(func(_ centrifuge.SubscribedEvent) { + cs.logger.Info().Msg("personal channel subscription established") + }) + + subscription.OnError(func(e centrifuge.SubscriptionErrorEvent) { + cs.logger.Error().Err(e.Error).Msg("personal channel subscription failed") + }) + + subscription.OnUnsubscribed(func(e centrifuge.UnsubscribedEvent) { + cs.logger.Info().Interface("event", e).Msg("personal channel subscription closed") + }) + + subscription.OnPublication(func(e centrifuge.PublicationEvent) { + topic, ok := e.Tags["topic"] + if !ok { + cs.logger.Debug().Interface("event", e).Msg("event without topic") + return + } + + if err := cs.handle(Topic(topic), handler, e.Data); err != nil { + cs.logger.Error().Err(err) + } + }) + + return subscription.Subscribe() +} + +func (cs CentrifugoSubscription) handle(topic Topic, handler Handler, b []byte) error { + switch { + case topic == TopicThemeAssigned && handler.ThemeAssigned != nil: + return dispatchMessage(handler.ThemeAssigned, b) + case topic == TopicThemeUnassigned && handler.ThemeUnassigned != nil: + return dispatchMessage(handler.ThemeUnassigned, b) + default: + return fmt.Errorf("no handler registered for topic: %s", topic) + } +} diff --git a/services/console/pkg/console/url.go b/services/console/pkg/console/url.go new file mode 100644 index 0000000000..1e3e2b01ff --- /dev/null +++ b/services/console/pkg/console/url.go @@ -0,0 +1,43 @@ +package console + +import ( + "fmt" + "net/url" + "slices" +) + +type URLBuilder struct { + apiURL url.URL + websocketURL url.URL +} + +func NewURLBuilder(claims *Claims) (URLBuilder, error) { + apiURL, err := url.Parse(claims.Issuer) + switch { + case err != nil: + return URLBuilder{}, err + case !slices.Contains([]string{"http", "https"}, apiURL.Scheme): + return URLBuilder{}, fmt.Errorf("invalid scheme: %s", apiURL.Scheme) + } + + websocketURL, err := url.Parse(claims.WebsocketURL) + switch { + case err != nil: + return URLBuilder{}, err + case !slices.Contains([]string{"ws", "wss"}, websocketURL.Scheme): + return URLBuilder{}, fmt.Errorf("invalid scheme: %s", apiURL.Scheme) + } + + return URLBuilder{ + apiURL: *apiURL.JoinPath("api", claims.APIVersion), + websocketURL: *websocketURL, + }, nil +} + +func (b URLBuilder) APIUrl(elem ...string) *url.URL { + return b.apiURL.JoinPath(elem...) +} + +func (b URLBuilder) SubscriptionUrl() *url.URL { + return &b.websocketURL +} diff --git a/services/console/pkg/features/features.go b/services/console/pkg/features/features.go new file mode 100644 index 0000000000..a56101f474 --- /dev/null +++ b/services/console/pkg/features/features.go @@ -0,0 +1,17 @@ +//go:build !release + +package features + +import ( + "crypto/tls" + "net/http" +) + +var ( + DefaultTLSConfig = &tls.Config{InsecureSkipVerify: true} + DefaultHTTPTransport = http.DefaultTransport.(*http.Transport).Clone() +) + +func init() { + DefaultHTTPTransport.TLSClientConfig = DefaultTLSConfig +} diff --git a/services/console/pkg/features/features_release.go b/services/console/pkg/features/features_release.go new file mode 100644 index 0000000000..88d0c97cb0 --- /dev/null +++ b/services/console/pkg/features/features_release.go @@ -0,0 +1,13 @@ +//go:build release + +package features + +import ( + "crypto/tls" + "net/http" +) + +var ( + DefaultTLSConfig *tls.Config = nil + DefaultHTTPTransport = http.DefaultTransport.(*http.Transport).Clone() +) diff --git a/services/console/pkg/web/repository.go b/services/console/pkg/web/repository.go new file mode 100644 index 0000000000..674de161db --- /dev/null +++ b/services/console/pkg/web/repository.go @@ -0,0 +1,70 @@ +package web + +import ( + "context" + "fmt" + "io" + + "github.com/opencloud-eu/opencloud/pkg/log" + webService "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/web/v0" + "github.com/opencloud-eu/opencloud/services/console/pkg/console" +) + +type GRPCRepositoryOptions struct { + WebService webService.WebService `validate:"required"` + Logger log.Logger +} + +func (o GRPCRepositoryOptions) Validate() error { + if err := console.Validate.Struct(o); err != nil { + return fmt.Errorf("(%w): %w", console.ErrOptionsInvalid, err) + } + + return nil +} + +type GRPCRepository struct { + webService webService.WebService + logger log.Logger +} + +func NewGRPCRepository(o GRPCRepositoryOptions) (GRPCRepository, error) { + if err := o.Validate(); err != nil { + return GRPCRepository{}, err + } + + return GRPCRepository{ + webService: o.WebService, + logger: o.Logger, + }, nil +} + +func (r GRPCRepository) ThemeExists(ctx context.Context, id string) (bool, error) { + resp, err := r.webService.ThemeExists(ctx, &webService.ThemeExistsRequest{Id: id}) + if err != nil { + return false, fmt.Errorf("(%w) %w", console.ErrRequest, err) + } + + return resp.Exists, nil +} + +func (r GRPCRepository) ThemeAdd(ctx context.Context, id string, tr io.Reader) error { + tb, err := io.ReadAll(tr) + if err != nil { + return fmt.Errorf("failed to read theme data: %w", err) + } + + if _, err := r.webService.ThemeAdd(ctx, &webService.ThemeAddRequest{Id: id, Data: tb}); err != nil { + return fmt.Errorf("(%w) %w", console.ErrRequest, err) + } + + return nil +} + +func (r GRPCRepository) ThemeRemove(ctx context.Context, id string) error { + if _, err := r.webService.ThemeRemove(ctx, &webService.ThemeRemoveRequest{Id: id}); err != nil { + return fmt.Errorf("(%w) %w", console.ErrRequest, err) + } + + return nil +} diff --git a/services/console/pkg/web/service_core.go b/services/console/pkg/web/service_core.go new file mode 100644 index 0000000000..a1f96898ec --- /dev/null +++ b/services/console/pkg/web/service_core.go @@ -0,0 +1,90 @@ +package web + +import ( + "context" + "fmt" + + "github.com/opencloud-eu/opencloud/pkg/log" + "github.com/opencloud-eu/opencloud/services/console/pkg/console" +) + +const ( + ThemeID = "_console" +) + +type CoreServiceOptions struct { + Repository Repository `validate:"required"` + ConsoleRepository ConsoleRepository `validate:"required"` + Next Service `validate:"required"` + Logger log.Logger +} + +func (o CoreServiceOptions) Validate() error { + if err := console.Validate.Struct(o); err != nil { + return fmt.Errorf("(%w): %w", console.ErrOptionsInvalid, err) + } + + return nil +} + +type CoreService struct { + repository Repository + consoleRepository ConsoleRepository + next Service + logger log.Logger +} + +func NewCoreService(o CoreServiceOptions) (CoreService, error) { + if err := o.Validate(); err != nil { + return CoreService{}, err + } + + return CoreService{ + repository: o.Repository, + consoleRepository: o.ConsoleRepository, + next: o.Next, + logger: o.Logger, + }, nil +} + +func (s CoreService) ThemeApply(ctx context.Context) error { + data, err := s.consoleRepository.ThemeGet(context.Background()) + if err != nil { + return fmt.Errorf("could not get theme: %w", err) + } + defer func() { + _ = data.Close() + }() + + exists, err := s.repository.ThemeExists(ctx, ThemeID) + if err != nil { + return fmt.Errorf("could not check if theme %s exists: %w", ThemeID, err) + } + + if exists { + if err := s.repository.ThemeRemove(ctx, ThemeID); err != nil { + return fmt.Errorf("could not remove existing theme %s: %w", ThemeID, err) + } + } + + if err := s.repository.ThemeAdd(ctx, ThemeID, data); err != nil { + return fmt.Errorf("could not add theme %s: %w", ThemeID, err) + } + + return s.next.ThemeApply(ctx) +} + +func (s CoreService) ThemeRemove(ctx context.Context) error { + exists, err := s.repository.ThemeExists(ctx, ThemeID) + if err != nil { + return fmt.Errorf("could not check if theme %s exists: %w", ThemeID, err) + } + + if exists { + if err := s.repository.ThemeRemove(ctx, ThemeID); err != nil { + return fmt.Errorf("could not remove existing theme %s: %w", ThemeID, err) + } + } + + return s.next.ThemeRemove(ctx) +} diff --git a/services/console/pkg/web/service_core_test.go b/services/console/pkg/web/service_core_test.go new file mode 100644 index 0000000000..1f556b0e9b --- /dev/null +++ b/services/console/pkg/web/service_core_test.go @@ -0,0 +1,31 @@ +package web_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/opencloud-eu/opencloud/services/console/pkg/console" + "github.com/opencloud-eu/opencloud/services/console/pkg/web" +) + +func TestService_NewService(t *testing.T) { + t.Run("valid options", func(t *testing.T) { + _, err := web.NewCoreService(web.CoreServiceOptions{}) + assert.ErrorIs(t, err, console.ErrOptionsInvalid) + }) +} + +func TestService_ThemeApply(t *testing.T) { + //options := web.CoreServiceOptions{ + // Repository: mocks.NewRepository(t), + // ConsoleRepository: mocks.NewConsoleRepository(t), + //} + //service, err := web.NewCoreService(options) + //assert.NoError(t, err) + + t.Run("fails without tenantID claim", func(t *testing.T) { + //err := service.ThemeApply(t.Context()) + //assert.ErrorIs(t, err, console.ErrJWTTokenUnknownClaim) + }) +} diff --git a/services/console/pkg/web/service_sse.go b/services/console/pkg/web/service_sse.go new file mode 100644 index 0000000000..94e6c9cf54 --- /dev/null +++ b/services/console/pkg/web/service_sse.go @@ -0,0 +1,55 @@ +package web + +import ( + "context" + "fmt" + + "github.com/google/uuid" + + "github.com/opencloud-eu/reva/v2/pkg/events" + + "github.com/opencloud-eu/opencloud/pkg/log" + "github.com/opencloud-eu/opencloud/services/console/pkg/console" + "github.com/opencloud-eu/opencloud/services/sse/pkg/service" +) + +type SSEServiceOptions struct { + EventStream events.Stream `validate:"required"` + Logger log.Logger +} + +func (o SSEServiceOptions) Validate() error { + if err := console.Validate.Struct(o); err != nil { + return fmt.Errorf("(%w): %w", console.ErrOptionsInvalid, err) + } + + return nil +} + +type SSEService struct { + eventStream events.Stream + logger log.Logger +} + +func NewSSEService(o SSEServiceOptions) (SSEService, error) { + if err := o.Validate(); err != nil { + return SSEService{}, err + } + + return SSEService{ + eventStream: o.EventStream, + logger: o.Logger, + }, nil +} + +func (s SSEService) ThemeApply(ctx context.Context) error { + return events.Publish(ctx, s.eventStream, events.SendSSE{ + UserIDs: []string{service.SSETopicAllUsers}, + Type: "console-notification", + Message: []byte(fmt.Sprintf(`{"id":"%s", "itemid":"%s"}`, uuid.New().String(), "theme has changed, please reload")), + }) +} + +func (s SSEService) ThemeRemove(context.Context) error { + return nil +} diff --git a/services/console/pkg/web/web.go b/services/console/pkg/web/web.go new file mode 100644 index 0000000000..4f89f2fe57 --- /dev/null +++ b/services/console/pkg/web/web.go @@ -0,0 +1,21 @@ +package web + +import ( + "context" + "io" +) + +type Service interface { + ThemeApply(context.Context) error + ThemeRemove(context.Context) error +} + +type Repository interface { + ThemeExists(ctx context.Context, id string) (bool, error) + ThemeRemove(ctx context.Context, id string) error + ThemeAdd(ctx context.Context, id string, r io.Reader) error +} + +type ConsoleRepository interface { + ThemeGet(ctx context.Context) (io.ReadCloser, error) +} diff --git a/services/web/pkg/service/v0/service.go b/services/web/pkg/service/v0/service.go index e8a2b85f66..2e88a42a3f 100644 --- a/services/web/pkg/service/v0/service.go +++ b/services/web/pkg/service/v0/service.go @@ -17,6 +17,7 @@ import ( "github.com/opencloud-eu/opencloud/pkg/log" "github.com/opencloud-eu/opencloud/pkg/middleware" "github.com/opencloud-eu/opencloud/pkg/tracing" + consoleWebService "github.com/opencloud-eu/opencloud/services/console/pkg/web" "github.com/opencloud-eu/opencloud/services/web/pkg/assets" "github.com/opencloud-eu/opencloud/services/web/pkg/config" "github.com/opencloud-eu/opencloud/services/web/pkg/theme" @@ -121,8 +122,8 @@ func (p Web) Config(w http.ResponseWriter, _ *http.Request) { // decouple theme-related config changes conf := *p.config // check if the console theme exists and apply it - if conf.Web.ThemeServer == conf.Web.Config.Server && p.themeService.Exists("_console") { - conf.Web.ThemePath = path.Join("themes", "_console", "theme.json") + if conf.Web.ThemeServer == conf.Web.Config.Server && p.themeService.Exists(consoleWebService.ThemeID) { + conf.Web.ThemePath = path.Join("themes", consoleWebService.ThemeID, "theme.json") } // build theme url diff --git a/vendor/github.com/centrifugal/centrifuge-go/.gitignore b/vendor/github.com/centrifugal/centrifuge-go/.gitignore new file mode 100644 index 0000000000..d6bad99da6 --- /dev/null +++ b/vendor/github.com/centrifugal/centrifuge-go/.gitignore @@ -0,0 +1,34 @@ +.DS_Store +vendor/ + +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +.idea/ +.vscode/ + +*.orig +examples/benchmark/benchmark +local/ \ No newline at end of file diff --git a/vendor/github.com/centrifugal/centrifuge-go/LICENSE b/vendor/github.com/centrifugal/centrifuge-go/LICENSE new file mode 100644 index 0000000000..624d2f8f73 --- /dev/null +++ b/vendor/github.com/centrifugal/centrifuge-go/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Centrifugal Labs LTD + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/centrifugal/centrifuge-go/README.md b/vendor/github.com/centrifugal/centrifuge-go/README.md new file mode 100644 index 0000000000..3ddb665e0b --- /dev/null +++ b/vendor/github.com/centrifugal/centrifuge-go/README.md @@ -0,0 +1,67 @@ +[![GoDoc](https://pkg.go.dev/badge/centrifugal/centrifuge-go)](https://pkg.go.dev/github.com/centrifugal/centrifuge-go) + +Websocket client for [Centrifugo](https://github.com/centrifugal/centrifugo) server and [Centrifuge](https://github.com/centrifugal/centrifuge) library. + +There is no v1 release of this library yet – API still evolves. At the moment patch version updates only contain backwards compatible changes, minor version updates can have backwards incompatible API changes. + +Check out [client SDK API specification](https://centrifugal.dev/docs/transports/client_api) to learn how this SDK behaves. It's recommended to read that before starting to work with this SDK as the spec covers common SDK behavior - describes client and subscription state transitions, main options and methods. Also check out examples folder. + +The features implemented by this SDK can be found in [SDK feature matrix](https://centrifugal.dev/docs/transports/client_sdk#sdk-feature-matrix). + +> **The latest `centrifuge-go` is compatible with [Centrifugo](https://github.com/centrifugal/centrifugo) server v6, v5 and v4, and [Centrifuge](https://github.com/centrifugal/centrifuge) >= 0.25.0. For Centrifugo v2, Centrifugo v3 and Centrifuge < 0.25.0 you should use `centrifuge-go` v0.8.x.** + +## Callbacks should not block + +When using this SDK you should not block for a long time inside event handlers since handlers called synchronously by the SDK and block the connection read loop. The fact that the read loop is blocked also means that you can not issue blocking `Client` requests such as `Publish`, `RPC`, `History`, `Presence`, `PresenceStats` from the event handler code – this will result into a deadlock. Use a separate goroutine if you really need to issue a blocking call from inside an event handler. + +I.e. this code is broken: + +```go +client.OnMessage(func(e centrifuge.MessageEvent) { + result, err := c.RPC(context.Background(), "method", []byte("{}")) + if err != nil { + log.Println(err) + return + } + // Will never be called. + log.Printf("RPC result: %s", string(result.Data)) +}) +``` + +This code is correct as it does not block event handler forever: + +```go +client.OnMessage(func(e centrifuge.MessageEvent) { + // When issue blocking requests from inside event handler we must use + // a goroutine. Otherwise, we will ge a deadlock since the connection + // read loop is blocked. + go func() { + result, err := c.RPC(context.Background(), "method", []byte("{}")) + if err != nil { + log.Println(err) + return + } + log.Printf("RPC result: %s", string(result.Data)) + }() +}) +``` + +You can find similar limitations in [eclipse/paho.mqtt.golang](https://github.com/eclipse/paho.mqtt.golang#common-problems). In short, this is caused by a challenging mix of asynchronous protocol, Go and callback approach. In the previous versions of this SDK we allowed blocking requests from within event handlers – but it contradicts with the real-time nature of Centrifugal ecosystem, because we had to use separate callback queue, and that queue could grow huge without a reasonable way to backpressure (which we were not able to find). + +If you are calling `Publish`, `RPC`, `History`, `Presence`, `PresenceStats` from the outside of event handler – you should not do any special care. Also, if you are calling your own blocking APIs from inside Centrifuge event handlers – you won't get the deadlock, but the read loop of the underlying connection will not proceed till the event handler returns. + +## Run tests + +First run Centrifugo instance: + +``` +docker run -it --rm -p 8000:8000 \ +-e CENTRIFUGO_CHANNEL_WITHOUT_NAMESPACE_DELTA_PUBLISH=true \ +-e CENTRIFUGO_CHANNEL_WITHOUT_NAMESPACE_ALLOWED_DELTA_TYPES="fossil" \ +-e CENTRIFUGO_CHANNEL_WITHOUT_NAMESPACE_HISTORY_SIZE="100" \ +-e CENTRIFUGO_CHANNEL_WITHOUT_NAMESPACE_HISTORY_TTL="300s" \ +-e CENTRIFUGO_CHANNEL_WITHOUT_NAMESPACE_FORCE_RECOVERY="true" \ +centrifugo/centrifugo:v6 centrifugo --client.insecure +``` + +Then run `go test` diff --git a/vendor/github.com/centrifugal/centrifuge-go/changelog.md b/vendor/github.com/centrifugal/centrifuge-go/changelog.md new file mode 100644 index 0000000000..ddeb2e72b3 --- /dev/null +++ b/vendor/github.com/centrifugal/centrifuge-go/changelog.md @@ -0,0 +1,189 @@ +v0.10.0 +======= + +**Breaking change!** This release changes the semantics of working with connection tokens described in [Centrifugo v5 release post](https://centrifugal.dev/blog/2023/06/29/centrifugo-v5-released#token-behaviour-adjustments-in-sdks). + +Previously, returning an empty token string from `Config.GetToken` callback resulted in client disconnection with unauthorized reason. + +Now returning an empty string from `Config.GetToken` is a valid scenario which won't result into disconnect on the client side. It's still possible to disconnect client by returning a special error `ErrUnauthorized` from `GetToken` function. + +And we are putting back `Client.SetToken` method to the SDK – so it's now possible to reset the token to be empty upon user logout. + +v0.9.6 +====== + +* Properly handle disconnect push + +v0.9.5 +====== + +* Update protocol package to the latest one. + +v0.9.4 +====== + +* Fix wrong unsubscribe code handling – see commit. According to spec unsubscribe codes >= 2500 should result into resubscribe from the SDK side, centrifuge-go did not follow this, instead it never resubscribed upon receiving such codes from the server. Thus message recovery and automatic resubscribe did not work correctly. + +v0.9.3 +====== + +* Fix leaking connection when getting `token expired` error upon connect. + +v0.9.2 +====== + +* Fix Unlock of unlocked RWMutex panic when sending connect command and getting an error from it. + +v0.9.1 +====== + +* Fix setting `SubscriptionConfig.GetToken` - [#66](https://github.com/centrifugal/centrifuge-go/pull/66) + +v0.9.0 +====== + +This release adopts a new iteration of Centrifugal protocol and a new iteration of API. Client now behaves according to the client [SDK API specification](https://centrifugal.dev/docs/transports/client_api). The work has been done according to [Centrifugo v4 roadmap](https://github.com/centrifugal/centrifugo/issues/500). + +Check out [Centrifugo v4 release post](https://centrifugal.dev/blog/2022/07/19/centrifugo-v4-released) that covers the reasoning behind changes here. + +New release only works with Centrifugo >= v4.0.0 and [Centrifuge](https://github.com/centrifugal/centrifuge) >= 0.25.0. See [Centrifugo v4 migration guide](https://centrifugal.dev/docs/getting-started/migration_v4) for details about the changes in the ecosystem. + +Note, that Centrifugo v4 supports clients working over the previous protocol iteration, so you can update Centrifugo to v4 without any changes on the client side (but you need to turn on `use_client_protocol_v1_by_default` option in the configuration of Centrifugo, see Centrifugo v4 migration guide for details). + +Important change is that `centrifuge-go` **does not allow blocking calls from inside event handlers now**. See [a description in README](https://github.com/centrifugal/centrifuge-go/tree/master#callbacks-should-not-block). + +v0.8.3 +====== + +* Call OnServerSubscribe handler correctly for dynamic server subscriptions (happening after initial connect), see [#64](https://github.com/centrifugal/centrifuge-go/pull/64). + +v0.8.2 +====== + +* Update protocol to v0.7.3 + +v0.8.1 +====== + +* Support for History reverse option. + +``` +gorelease -base v0.8.0 -version v0.8.1 +github.com/centrifugal/centrifuge-go +------------------------------------ +Compatible changes: +- HistoryOptions.Reverse: added +- WithHistoryReverse: added + +v0.8.1 is a valid semantic version for this release. +``` + +v0.8.0 +====== + +Update to work with Centrifuge >= v0.18.0 and Centrifugo v3. + +Keep in mind that `New` is deprecated now, prefer using `NewJsonClient` or `NewProtobufClient` when server is based on Centrifuge >= v0.18.0 or Centrifugo >= v3.0.0 + +**Breaking change:** client History API behavior changed – Centrifuge >= v0.18.0 and Centrifugo >= v3.0.0 won't return all publications in a stream by default, see Centrifuge [v0.18.0 release notes](https://github.com/centrifugal/centrifuge/releases/tag/v0.18.0) or [Centrifugo v3 migration guide](https://centrifugal.dev/docs/getting-started/migration_v3) for more information and workaround on server-side. + +``` +gorelease -base v0.7.2 -version v0.8.0 +github.com/centrifugal/centrifuge-go +------------------------------------ +Incompatible changes: +- (*Client).History: changed from func(string) (HistoryResult, error) to func(string, ...HistoryOption) (HistoryResult, error) +- (*Subscription).History: changed from func() (HistoryResult, error) to func(...HistoryOption) (HistoryResult, error) +- (*Subscription).Subscribe: changed from func() error to func(...SubscribeOption) error +Compatible changes: +- HistoryOption: added +- HistoryOptions: added +- NewJsonClient: added +- NewProtobufClient: added +- StreamPosition: added +- SubscribeOption: added +- SubscribeOptions: added +- WithHistoryLimit: added +- WithHistorySince: added +- WithSubscribeSince: added + +v0.8.0 is a valid semantic version for this release. +``` + +v0.7.2 +====== + +* Bump protocol to v0.5.0 [#56](https://github.com/centrifugal/centrifuge-go/pull/56) + +v0.7.1 +====== + +* Fix atomic align on 32-bit [#49](https://github.com/centrifugal/centrifuge-go/pull/49) + +v0.7.0 +====== + +* Updated `github.com/centrifugal/protocol` package dependency to catch up with the latest changes in it +* Introduced `Error` type which is used where we previously exposed `protocol.Error` – so there is no need to import `protocol` package in application code to investigate error code or message +* Methods `Client.SetName` and `Client.SetVersion` removed in favour of `Name` and `Version` fields of `Config` +* Add top-level methods `Client.History`, `Client.Presence`, `Client.PresenceStats` – so it's possible to call corresponding client API methods when using server-side subscriptions + +``` +$ gorelease -base v0.6.5 -version v0.7.0 +github.com/centrifugal/centrifuge-go +------------------------------------ +Incompatible changes: +- (*Client).SetName: removed +- (*Client).SetVersion: removed +Compatible changes: +- (*Client).History: added +- (*Client).Presence: added +- (*Client).PresenceStats: added +- Config.Name: added +- Config.Version: added +- DefaultName: added +- Error: added + +v0.7.0 is a valid semantic version for this release. +``` + +v0.6.5 +====== + +* One more fix for memory align on 32bit arch, see [#46](https://github.com/centrifugal/centrifuge-go/pull/46) + +v0.6.4 +====== + +* Add `Subscription.Close` method to close Subscription when it's not needed anymore. This method unsubscribes from a channel and removes Subscription from internal `Client` subscription registry – thus freeing resources. Subscription is not usable after `Close` called. This method can be helpful if you work with lots of short-living subscriptions to different channels to prevent unlimited internal Subscription registry growth. + +v0.6.3 +====== + +* Fix memory align on 32bit arch, see [#40](https://github.com/centrifugal/centrifuge-go/pull/40) + +v0.6.2 +====== + +* fix deadlock on a private channel resubscribe - see [#38](https://github.com/centrifugal/centrifuge-go/pull/38) + +v0.6.1 +====== + +* fix setting server-side unsubscribe handler, call server-side unsubscribe event on disconnect + +v0.6.0 +====== + +* server-side subscriptions support +* get rid of Protobuf protocol struct `Publication` and `ClientInfo` aliases – use library scope structures instead +* change return values of `RPC`, `NamedRPC`, `History`, `Presence`, `PresenceStats`, `Publish` methods to be more meaningful and extensible +* much faster resubscribe to many subscriptions (previously we waited for each individual subscription response before moving further, now process is asynchronous) +* improved reconnect logic +* Client and Subscription status refactoring +* fix inconsistent join/subscribe event ordering – now both processed in order coming from server + +v0.5.2 +====== + +* `NamedRPC` method added - [#35](https://github.com/centrifugal/centrifuge-go/pull/35), thanks [@L11R](https://github.com/L11R) diff --git a/vendor/github.com/centrifugal/centrifuge-go/client.go b/vendor/github.com/centrifugal/centrifuge-go/client.go new file mode 100644 index 0000000000..dd2571fd4b --- /dev/null +++ b/vendor/github.com/centrifugal/centrifuge-go/client.go @@ -0,0 +1,2132 @@ +package centrifuge + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "math/rand" + "net/http" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/centrifugal/protocol" + "google.golang.org/protobuf/encoding/protojson" +) + +// State of client connection. +type State string + +// Describe client connection states. +const ( + StateDisconnected State = "disconnected" + StateConnecting State = "connecting" + StateConnected State = "connected" + StateClosed State = "closed" +) + +// Client represents client connection to Centrifugo or Centrifuge +// library based server. It provides methods to set various event +// handlers, subscribe channels, call RPC commands etc. Call client +// Connect method to trigger actual connection with a server. Call client +// Close method to clean up state when you don't need client instance +// anymore. +type Client struct { + futureID uint64 + cmdID uint32 + mu sync.RWMutex + endpoints []string + round int + protocolType protocol.Type + config Config + token string + data protocol.Raw + transport transport + disconnectedCh chan struct{} + state State + subs map[string]*Subscription + serverSubs map[string]*serverSub + requestsMu sync.RWMutex + requests map[uint32]request + receive chan []byte + reconnectAttempts int + reconnectStrategy reconnectStrategy + events *eventHub + sendPong bool + delayPing chan struct{} + closeCh chan struct{} + connectFutures map[uint64]connectFuture + cbQueue *cbQueue + reconnectTimer *time.Timer + refreshTimer *time.Timer + refreshRequired bool + logCh chan LogEntry + logCloseCh chan struct{} + logCloseOnce sync.Once +} + +// NewJsonClient initializes Client which uses JSON-based protocol internally. +// After client initialized call Client.Connect method. Use Client.NewSubscription to +// create Subscription objects. +// The provided endpoint must be a valid URL with ws:// or wss:// scheme – otherwise +// NewJsonClient will panic. +func NewJsonClient(endpoint string, config Config) *Client { + return newClient(endpoint, false, config) +} + +// NewProtobufClient initializes Client which uses Protobuf-based protocol internally. +// After client initialized call Client.Connect method. Use Client.NewSubscription to +// create Subscription objects. +// The provided endpoint must be a valid URL with ws:// or wss:// scheme – otherwise +// NewProtobufClient will panic. +func NewProtobufClient(endpoint string, config Config) *Client { + return newClient(endpoint, true, config) +} + +func (c *Client) logLevelEnabled(level LogLevel) bool { + return c.config.LogLevel > LogLevelNone && level >= c.config.LogLevel +} + +func (c *Client) log(level LogLevel, message string, fields map[string]string) { + logEntry := LogEntry{ + Level: level, + Message: message, + Fields: fields, + } + select { + case c.logCh <- logEntry: + default: + // If log channel is full, drop the log entry. + } +} + +func (c *Client) handleLogs() { + for { + select { + case entry := <-c.logCh: + c.config.LogHandler(entry) + case <-c.logCloseCh: + return + } + } +} + +func (c *Client) traceOutCmd(cmd *protocol.Command) { + jsonBytes, err := json.Marshal(cmd) + if err != nil { + jsonBytes, _ = protojson.Marshal(cmd) + } + c.log(LogLevelTrace, "-out->", map[string]string{"command": string(jsonBytes)}) +} + +func (c *Client) traceInReply(rep *protocol.Reply) { + jsonBytes, err := json.Marshal(rep) + if err != nil { + jsonBytes, _ = protojson.Marshal(rep) + } + c.log(LogLevelTrace, "<-in--", map[string]string{"reply": string(jsonBytes)}) +} + +func (c *Client) traceInPush(push *protocol.Push) { + jsonBytes, err := json.Marshal(push) + if err != nil { + jsonBytes, _ = protojson.Marshal(push) + } + c.log(LogLevelTrace, "<-in--", map[string]string{"push": string(jsonBytes)}) +} + +func newClient(endpoint string, isProtobuf bool, config Config) *Client { + if config.ReadTimeout == 0 { + config.ReadTimeout = 5 * time.Second + } + if config.WriteTimeout == 0 { + config.WriteTimeout = time.Second + } + if config.HandshakeTimeout == 0 { + config.HandshakeTimeout = time.Second + } + if config.MaxServerPingDelay == 0 { + config.MaxServerPingDelay = 10 * time.Second + } + if config.Header == nil { + config.Header = http.Header{} + } + if config.Name == "" { + config.Name = "go" + } + + // We support setting multiple endpoints to try in round-robin fashion. But + // for now this feature is not documented and used for internal tests. In most + // cases there should be a single public server WS endpoint. + endpoints := strings.Split(endpoint, ",") + if len(endpoints) == 0 { + panic("connection endpoint required") + } + rand.Shuffle(len(endpoints), func(i, j int) { + endpoints[i], endpoints[j] = endpoints[j], endpoints[i] + }) + for _, e := range endpoints { + if !strings.HasPrefix(e, "ws") { + panic(fmt.Sprintf("unsupported connection endpoint: %s", e)) + } + } + + protocolType := protocol.TypeJSON + if isProtobuf { + protocolType = protocol.TypeProtobuf + } + + client := &Client{ + endpoints: endpoints, + config: config, + state: StateDisconnected, + protocolType: protocolType, + subs: make(map[string]*Subscription), + serverSubs: make(map[string]*serverSub), + requests: make(map[uint32]request), + reconnectStrategy: newBackoffReconnect(config.MinReconnectDelay, config.MaxReconnectDelay), + delayPing: make(chan struct{}, 32), + events: newEventHub(), + connectFutures: make(map[uint64]connectFuture), + token: config.Token, + data: config.Data, + logCh: make(chan LogEntry, 256), + logCloseCh: make(chan struct{}), + } + + // Queue to run callbacks on. + client.cbQueue = &cbQueue{ + closeCh: make(chan struct{}), + } + client.cbQueue.cond = sync.NewCond(&client.cbQueue.mu) + go client.cbQueue.dispatch() + if client.config.LogLevel > 0 { + go client.handleLogs() + } + return client +} + +// Connect dials to server and sends connect message. Will return an error if first +// dial with a server failed. In case of failure client will automatically reconnect. +// To temporary disconnect from a server call Client.Disconnect. +func (c *Client) Connect() error { + return c.startConnecting() +} + +// Disconnect client from server. It's still possible to connect again later. If +// you don't need Client anymore – use Client.Close. +func (c *Client) Disconnect() error { + if c.isClosed() { + return ErrClientClosed + } + c.moveToDisconnected(disconnectedDisconnectCalled, "disconnect called") + return nil +} + +// Close closes Client and cleanups resources. Client is unusable after this. Use this +// method if you don't need client anymore, otherwise look at Client.Disconnect. +func (c *Client) Close() { + if c.isClosed() { + return + } + c.moveToDisconnected(disconnectedDisconnectCalled, "disconnect called") + c.moveToClosed() + c.logCloseOnce.Do(func() { + close(c.logCloseCh) + }) +} + +// State returns current Client state. Note that while you are processing +// this state - Client can move to a new one. +func (c *Client) State() State { + c.mu.RLock() + defer c.mu.RUnlock() + return c.state +} + +// SetToken allows updating Client's connection token. +func (c *Client) SetToken(token string) { + c.mu.Lock() + c.token = token + c.mu.Unlock() +} + +// NewSubscription allocates new Subscription on a channel. As soon as Subscription +// successfully created Client keeps reference to it inside internal map registry to +// manage automatic resubscribe on reconnect. After creating Subscription call its +// Subscription.Subscribe method to actually start subscribing process. To temporarily +// unsubscribe call Subscription.Unsubscribe. If you finished with Subscription then +// you can remove it from the internal registry by calling Client.RemoveSubscription +// method. +func (c *Client) NewSubscription(channel string, config ...SubscriptionConfig) (*Subscription, error) { + c.mu.Lock() + defer c.mu.Unlock() + var sub *Subscription + if _, ok := c.subs[channel]; ok { + return nil, ErrDuplicateSubscription + } + sub = newSubscription(c, channel, config...) + c.subs[channel] = sub + return sub, nil +} + +// RemoveSubscription removes Subscription from the internal client registry. +// Make sure Subscription is in unsubscribed state before removing it. +func (c *Client) RemoveSubscription(sub *Subscription) error { + if sub.State() != SubStateUnsubscribed { + return errors.New("subscription must be unsubscribed to be removed") + } + c.mu.Lock() + defer c.mu.Unlock() + delete(c.subs, sub.Channel) + return nil +} + +// GetSubscription allows getting Subscription from the internal client registry. +func (c *Client) GetSubscription(channel string) (*Subscription, bool) { + c.mu.RLock() + defer c.mu.RUnlock() + s, ok := c.subs[channel] + return s, ok +} + +// Subscriptions returns a map with all currently registered client-side subscriptions. +func (c *Client) Subscriptions() map[string]*Subscription { + subs := make(map[string]*Subscription) + c.mu.Lock() + defer c.mu.Unlock() + for k, v := range c.subs { + subs[k] = v + } + return subs +} + +// Send message to server without waiting for response. +// Message handler must be registered on server. +func (c *Client) Send(ctx context.Context, data []byte) error { + if c.isClosed() { + return ErrClientClosed + } + errCh := make(chan error, 1) + c.onConnect(func(err error) { + if err != nil { + errCh <- err + return + } + select { + case <-ctx.Done(): + errCh <- ctx.Err() + return + default: + } + cmd := &protocol.Command{} + params := &protocol.SendRequest{ + Data: data, + } + cmd.Send = params + errCh <- c.send(cmd) + }) + + select { + case <-ctx.Done(): + return ctx.Err() + case err := <-errCh: + return err + } +} + +// RPCResult contains data returned from server as RPC result. +type RPCResult struct { + Data []byte +} + +// RPC allows sending data to a server and waiting for a response. +// RPC handler must be registered on server. +func (c *Client) RPC(ctx context.Context, method string, data []byte) (RPCResult, error) { + if c.isClosed() { + return RPCResult{}, ErrClientClosed + } + resCh := make(chan RPCResult, 1) + errCh := make(chan error, 1) + c.sendRPC(ctx, method, data, func(result RPCResult, err error) { + resCh <- result + errCh <- err + }) + + select { + case <-ctx.Done(): + return RPCResult{}, ctx.Err() + case res := <-resCh: + return res, <-errCh + } +} + +func (c *Client) nextCmdID() uint32 { + return atomic.AddUint32(&c.cmdID, 1) +} + +func (c *Client) isConnected() bool { + c.mu.RLock() + defer c.mu.RUnlock() + return c.state == StateConnected +} + +func (c *Client) isClosed() bool { + c.mu.RLock() + defer c.mu.RUnlock() + return c.state == StateClosed +} + +func (c *Client) isSubscribed(channel string) bool { + c.mu.RLock() + _, ok := c.subs[channel] + c.mu.RUnlock() + return ok +} + +func (c *Client) sendRPC(ctx context.Context, method string, data []byte, fn func(RPCResult, error)) { + c.onConnect(func(err error) { + select { + case <-ctx.Done(): + fn(RPCResult{}, ctx.Err()) + return + default: + } + if err != nil { + fn(RPCResult{}, err) + return + } + cmd := &protocol.Command{ + Id: c.nextCmdID(), + } + + params := &protocol.RPCRequest{ + Data: data, + Method: method, + } + + cmd.Rpc = params + + err = c.sendAsync(cmd, func(r *protocol.Reply, err error) { + if err != nil { + fn(RPCResult{}, err) + return + } + if r.Error != nil { + fn(RPCResult{}, errorFromProto(r.Error)) + return + } + fn(RPCResult{Data: r.Rpc.Data}, nil) + }) + if err != nil { + fn(RPCResult{}, err) + return + } + }) +} + +func (c *Client) moveToDisconnected(code uint32, reason string) { + c.mu.Lock() + if c.state == StateDisconnected || c.state == StateClosed { + c.mu.Unlock() + return + } + if c.transport != nil { + _ = c.transport.Close() + c.transport = nil + } + + prevState := c.state + c.state = StateDisconnected + c.clearConnectedState() + c.resolveConnectFutures(ErrClientDisconnected) + + subsToUnsubscribe := make([]*Subscription, 0, len(c.subs)) + for _, s := range c.subs { + s.mu.Lock() + if s.state == SubStateUnsubscribed { + s.mu.Unlock() + continue + } + s.mu.Unlock() + subsToUnsubscribe = append(subsToUnsubscribe, s) + } + serverSubsToUnsubscribe := make([]string, 0, len(c.serverSubs)) + for ch := range c.serverSubs { + serverSubsToUnsubscribe = append(serverSubsToUnsubscribe, ch) + } + c.mu.Unlock() + + for _, s := range subsToUnsubscribe { + s.moveToSubscribing(subscribingTransportClosed, "transport closed") + } + + if prevState == StateConnected { + var serverSubscribingHandler ServerSubscribingHandler + if c.events != nil && c.events.onServerSubscribing != nil { + serverSubscribingHandler = c.events.onServerSubscribing + } + if serverSubscribingHandler != nil { + c.runHandlerAsync(func() { + for _, ch := range serverSubsToUnsubscribe { + serverSubscribingHandler(ServerSubscribingEvent{Channel: ch}) + } + }) + } + } + + var handler DisconnectHandler + if c.events != nil && c.events.onDisconnected != nil { + handler = c.events.onDisconnected + } + if handler != nil { + c.runHandlerAsync(func() { + event := DisconnectedEvent{Code: code, Reason: reason} + handler(event) + }) + } +} + +func (c *Client) moveToConnecting(code uint32, reason string) { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "moving client to connecting state", map[string]string{ + "code": strconv.Itoa(int(code)), + "reason": reason, + }) + } + c.mu.Lock() + if c.state == StateDisconnected || c.state == StateClosed || c.state == StateConnecting { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "client already in state that does not require extra work", map[string]string{ + "state": string(c.state), + }) + } + c.mu.Unlock() + return + } + if c.transport != nil { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "closing non-nil transport", nil) + } + _ = c.transport.Close() + c.transport = nil + } + + c.state = StateConnecting + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "client moved to connecting state", nil) + } + c.clearConnectedState() + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "cleared connected state", nil) + } + c.resolveConnectFutures(ErrClientDisconnected) + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "resolved connect futures", nil) + } + + subsToUnsubscribe := make([]*Subscription, 0, len(c.subs)) + for _, s := range c.subs { + s.mu.Lock() + if s.state == SubStateUnsubscribed { + s.mu.Unlock() + continue + } + s.mu.Unlock() + subsToUnsubscribe = append(subsToUnsubscribe, s) + } + serverSubsToUnsubscribe := make([]string, 0, len(c.serverSubs)) + for ch := range c.serverSubs { + serverSubsToUnsubscribe = append(serverSubsToUnsubscribe, ch) + } + c.mu.Unlock() + + for _, s := range subsToUnsubscribe { + s.moveToSubscribing(subscribingTransportClosed, "transport closed") + } + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "client-side subs unsubscribe events called", map[string]string{ + "num_subs": strconv.Itoa(len(subsToUnsubscribe)), + }) + } + + var serverSubscribingHandler ServerSubscribingHandler + if c.events != nil && c.events.onServerSubscribing != nil { + serverSubscribingHandler = c.events.onServerSubscribing + } + if serverSubscribingHandler != nil { + c.runHandlerSync(func() { + for _, ch := range serverSubsToUnsubscribe { + serverSubscribingHandler(ServerSubscribingEvent{Channel: ch}) + } + }) + } + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "server-side subs unsubscribe events called", map[string]string{ + "num_subs": strconv.Itoa(len(serverSubsToUnsubscribe)), + }) + } + + var handler ConnectingHandler + if c.events != nil && c.events.onConnecting != nil { + handler = c.events.onConnecting + } + if handler != nil { + c.runHandlerSync(func() { + event := ConnectingEvent{Code: code, Reason: reason} + handler(event) + }) + } + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "connecting event called", nil) + } + + c.mu.Lock() + if c.state != StateConnecting { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "not in connecting state, no need to reconnect", map[string]string{ + "state": string(c.state), + }) + } + c.mu.Unlock() + return + } + c.scheduleReconnectLocked() + c.mu.Unlock() +} + +func (c *Client) scheduleReconnectLocked() { + c.reconnectAttempts++ + if c.reconnectTimer != nil { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "stopping previous reconnect timer", nil) + } + c.reconnectTimer.Stop() + c.reconnectTimer = nil + } + reconnectDelay := c.getReconnectDelay() + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "reconnect with delay", map[string]string{ + "delay": reconnectDelay.String(), + }) + } + c.reconnectTimer = time.AfterFunc(reconnectDelay, func() { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "reconnect timer fired, start reconnecting", nil) + } + _ = c.startReconnecting() + }) +} + +func (c *Client) moveToClosed() { + c.mu.Lock() + if c.state == StateClosed { + c.mu.Unlock() + return + } + c.state = StateClosed + + subsToUnsubscribe := make([]*Subscription, 0, len(c.subs)) + for _, s := range c.subs { + s.mu.Lock() + if s.state == SubStateUnsubscribed { + s.mu.Unlock() + continue + } + s.mu.Unlock() + subsToUnsubscribe = append(subsToUnsubscribe, s) + } + serverSubsToUnsubscribe := make([]string, 0, len(c.serverSubs)) + for ch := range c.serverSubs { + serverSubsToUnsubscribe = append(serverSubsToUnsubscribe, ch) + } + c.mu.Unlock() + + for _, s := range subsToUnsubscribe { + s.moveToUnsubscribed(unsubscribedClientClosed, "client closed") + } + + var serverUnsubscribedHandler ServerUnsubscribedHandler + if c.events != nil && c.events.onServerUnsubscribed != nil { + serverUnsubscribedHandler = c.events.onServerUnsubscribed + } + if serverUnsubscribedHandler != nil { + c.runHandlerAsync(func() { + for _, ch := range serverSubsToUnsubscribe { + serverUnsubscribedHandler(ServerUnsubscribedEvent{Channel: ch}) + } + }) + } + + c.mu.RLock() + disconnectedCh := c.disconnectedCh + c.mu.RUnlock() + // At this point connection close was issued, so we wait until the reader goroutine + // finishes its work, after that it's safe to close the callback queue. + if disconnectedCh != nil { + <-disconnectedCh + } + + c.mu.Lock() + defer c.mu.Unlock() + c.disconnectedCh = nil + c.cbQueue.close() + c.cbQueue = nil +} + +func (c *Client) handleError(err error) { + var handler ErrorHandler + if c.events != nil && c.events.onError != nil { + handler = c.events.onError + } + if handler != nil { + c.runHandlerSync(func() { + handler(ErrorEvent{Error: err}) + }) + } +} + +// Lock must be held outside. +func (c *Client) clearConnectedState() { + if c.reconnectTimer != nil { + c.reconnectTimer.Stop() + c.reconnectTimer = nil + } + if c.refreshTimer != nil { + c.refreshTimer.Stop() + c.refreshTimer = nil + } + if c.closeCh != nil { + close(c.closeCh) + c.closeCh = nil + } + + c.requestsMu.Lock() + reqs := make(map[uint32]request, len(c.requests)) + for uid, req := range c.requests { + reqs[uid] = req + } + c.requests = make(map[uint32]request) + c.requestsMu.Unlock() + + for _, req := range reqs { + if req.cb != nil { + go req.cb(nil, ErrClientDisconnected) + } + } +} + +func (c *Client) handleDisconnect(d *disconnect) { + if d == nil { + d = &disconnect{ + Code: connectingTransportClosed, + Reason: "transport closed", + Reconnect: true, + } + } + if d.Reconnect { + c.moveToConnecting(d.Code, d.Reason) + } else { + c.moveToDisconnected(d.Code, d.Reason) + } +} + +func (c *Client) waitServerPing(disconnectCh chan struct{}, pingInterval uint32) { + timeout := c.config.MaxServerPingDelay + time.Duration(pingInterval)*time.Second + for { + select { + case <-c.delayPing: + case <-time.After(timeout): + go c.handleDisconnect(&disconnect{Code: connectingNoPing, Reason: "no ping", Reconnect: true}) + case <-disconnectCh: + return + } + } +} + +func (c *Client) readOnce(t transport) error { + reply, disconnect, err := t.Read() + if err != nil { + go c.handleDisconnect(disconnect) + return err + } + c.handle(reply) + return nil +} + +func (c *Client) reader(t transport, disconnectCh chan struct{}) { + defer close(disconnectCh) + for { + err := c.readOnce(t) + if err != nil { + return + } + } +} + +func (c *Client) runHandlerSync(fn func()) { + c.mu.RLock() + queue := c.cbQueue + c.mu.RUnlock() + if queue == nil { + return + } + waitCh := make(chan struct{}) + queue.push(func(delay time.Duration) { + defer close(waitCh) + fn() + }) + <-waitCh +} + +func (c *Client) runHandlerAsync(fn func()) { + c.mu.RLock() + queue := c.cbQueue + c.mu.RUnlock() + if queue == nil { + return + } + queue.push(func(delay time.Duration) { + fn() + }) +} + +func (c *Client) handle(reply *protocol.Reply) { + if reply.Id > 0 { + if c.logLevelEnabled(LogLevelTrace) { + c.traceInReply(reply) + } + c.requestsMu.RLock() + req, ok := c.requests[reply.Id] + c.requestsMu.RUnlock() + if ok { + if req.cb != nil { + req.cb(reply, nil) + } + } + c.removeRequest(reply.Id) + } else { + if reply.Push == nil { + if c.logLevelEnabled(LogLevelTrace) { + c.traceInReply(reply) + } + // Ping from server, send pong if needed. + select { + case c.delayPing <- struct{}{}: + default: + } + c.mu.RLock() + sendPong := c.sendPong + c.mu.RUnlock() + if sendPong { + cmd := &protocol.Command{} + _ = c.send(cmd) + } + return + } + if c.logLevelEnabled(LogLevelTrace) { + c.traceInPush(reply.Push) + } + c.mu.Lock() + if c.state != StateConnected { + c.mu.Unlock() + return + } + c.mu.Unlock() + c.handlePush(reply.Push) + } +} + +func (c *Client) handleMessage(msg *protocol.Message) error { + var handler MessageHandler + if c.events != nil && c.events.onMessage != nil { + handler = c.events.onMessage + } + if handler != nil { + event := MessageEvent{Data: msg.Data} + c.runHandlerSync(func() { + handler(event) + }) + } + return nil +} + +func (c *Client) handlePush(push *protocol.Push) { + channel := push.Channel + c.mu.RLock() + sub, ok := c.subs[channel] + c.mu.RUnlock() + switch { + case push.Message != nil: + _ = c.handleMessage(push.Message) + case push.Unsubscribe != nil: + if !ok { + c.handleServerUnsub(channel, push.Unsubscribe) + return + } + sub.handleUnsubscribe(push.Unsubscribe) + case push.Pub != nil: + if !ok { + c.handleServerPublication(channel, push.Pub) + return + } + sub.handlePublication(push.Pub) + case push.Join != nil: + if !ok { + c.handleServerJoin(channel, push.Join) + return + } + sub.handleJoin(push.Join.Info) + case push.Leave != nil: + if !ok { + c.handleServerLeave(channel, push.Leave) + return + } + sub.handleLeave(push.Leave.Info) + case push.Subscribe != nil: + if ok { + // Client-side subscription exists. + return + } + c.handleServerSub(channel, push.Subscribe) + return + case push.Disconnect != nil: + code := push.Disconnect.Code + reconnect := code < 3500 || code >= 5000 || (code >= 4000 && code < 4500) + if reconnect { + c.moveToConnecting(code, push.Disconnect.Reason) + } else { + c.moveToDisconnected(code, push.Disconnect.Reason) + } + default: + } +} + +func (c *Client) handleServerPublication(channel string, pub *protocol.Publication) { + c.mu.Lock() + serverSub, ok := c.serverSubs[channel] + if !ok { + c.mu.Unlock() + return + } + if serverSub.Recoverable && pub.Offset > 0 { + serverSub.Offset = pub.Offset + } + c.mu.Unlock() + + var handler ServerPublicationHandler + if c.events != nil && c.events.onServerPublication != nil { + handler = c.events.onServerPublication + } + if handler != nil { + c.runHandlerSync(func() { + handler(ServerPublicationEvent{Channel: channel, Publication: pubFromProto(pub)}) + }) + } +} + +func (c *Client) handleServerJoin(channel string, join *protocol.Join) { + c.mu.Lock() + _, ok := c.serverSubs[channel] + if !ok { + c.mu.Unlock() + return + } + c.mu.Unlock() + + var handler ServerJoinHandler + if c.events != nil && c.events.onServerJoin != nil { + handler = c.events.onServerJoin + } + if handler != nil { + c.runHandlerSync(func() { + handler(ServerJoinEvent{Channel: channel, ClientInfo: infoFromProto(join.Info)}) + }) + } +} + +func (c *Client) handleServerLeave(channel string, leave *protocol.Leave) { + c.mu.Lock() + _, ok := c.serverSubs[channel] + if !ok { + c.mu.Unlock() + return + } + c.mu.Unlock() + + var handler ServerLeaveHandler + if c.events != nil && c.events.onServerLeave != nil { + handler = c.events.onServerLeave + } + if handler != nil { + c.runHandlerSync(func() { + handler(ServerLeaveEvent{Channel: channel, ClientInfo: infoFromProto(leave.Info)}) + }) + } +} + +func (c *Client) handleServerSub(channel string, sub *protocol.Subscribe) { + c.mu.Lock() + _, ok := c.serverSubs[channel] + if ok { + c.mu.Unlock() + return + } + c.serverSubs[channel] = &serverSub{ + Offset: sub.Offset, + Epoch: sub.Epoch, + Recoverable: sub.Recoverable, + } + c.mu.Unlock() + + var handler ServerSubscribedHandler + if c.events != nil && c.events.onServerSubscribe != nil { + handler = c.events.onServerSubscribe + } + if handler != nil { + c.runHandlerSync(func() { + ev := ServerSubscribedEvent{ + Channel: channel, + Positioned: sub.GetPositioned(), + Recoverable: sub.GetRecoverable(), + Data: sub.GetData(), + } + if ev.Positioned || ev.Recoverable { + ev.StreamPosition = &StreamPosition{ + Epoch: sub.GetEpoch(), + Offset: sub.GetOffset(), + } + } + handler(ev) + }) + } +} + +func (c *Client) handleServerUnsub(channel string, _ *protocol.Unsubscribe) { + c.mu.Lock() + _, ok := c.serverSubs[channel] + if ok { + delete(c.serverSubs, channel) + } + c.mu.Unlock() + if !ok { + return + } + + var handler ServerUnsubscribedHandler + if c.events != nil && c.events.onServerUnsubscribed != nil { + handler = c.events.onServerUnsubscribed + } + if handler != nil { + c.runHandlerSync(func() { + handler(ServerUnsubscribedEvent{Channel: channel}) + }) + } +} + +func (c *Client) getReconnectDelay() time.Duration { + return c.reconnectStrategy.timeBeforeNextAttempt(c.reconnectAttempts) +} + +func (c *Client) startReconnecting() error { + c.mu.Lock() + c.round++ + round := c.round + if c.state != StateConnecting { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "not in connecting state, no need to reconnect", map[string]string{ + "state": string(c.state), + }) + } + c.mu.Unlock() + return nil + } + refreshRequired := c.refreshRequired + token := c.token + getTokenFunc := c.config.GetToken + c.mu.Unlock() + + wsConfig := websocketConfig{ + Proxy: c.config.Proxy, + NetDialContext: c.config.NetDialContext, + TLSConfig: c.config.TLSConfig, + HandshakeTimeout: c.config.HandshakeTimeout, + EnableCompression: c.config.EnableCompression, + CookieJar: c.config.CookieJar, + Header: c.config.Header, + } + + u := c.endpoints[round%len(c.endpoints)] + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "creating new transport", nil) + } + t, err := newWebsocketTransport(u, c.protocolType, wsConfig) + if err != nil { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "error creating new transport", map[string]string{ + "error": err.Error(), + }) + } + c.handleError(TransportError{err}) + c.mu.Lock() + if c.state != StateConnecting { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "not in connecting state, no need to reconnect", map[string]string{ + "state": string(c.state), + }) + } + c.mu.Unlock() + return nil + } + c.scheduleReconnectLocked() + c.mu.Unlock() + return err + } + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "new transport created", nil) + } + + if refreshRequired || (token == "" && getTokenFunc != nil) { + // Try to refresh token. + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "refreshing token", nil) + } + newToken, err := c.refreshToken() + if err != nil { + if errors.Is(err, ErrUnauthorized) { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "unauthorized error, move to disconnected", nil) + } + c.moveToDisconnected(disconnectedUnauthorized, "unauthorized") + return nil + } + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "error refreshing token", map[string]string{ + "error": err.Error(), + }) + } + c.handleError(RefreshError{err}) + c.mu.Lock() + if c.state != StateConnecting { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "not in connecting state, no need to continue", map[string]string{ + "state": string(c.state), + }) + } + _ = t.Close() + c.mu.Unlock() + return nil + } + c.scheduleReconnectLocked() + c.mu.Unlock() + return err + } else { + c.mu.Lock() + c.token = newToken + if c.state != StateConnecting { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "got token, but not in connecting state anymore", map[string]string{ + "state": string(c.state), + }) + } + c.mu.Unlock() + return nil + } + c.mu.Unlock() + } + } + + c.mu.Lock() + if c.state != StateConnecting { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "not in connecting state, no need to reconnect", map[string]string{ + "state": string(c.state), + }) + } + _ = t.Close() + c.mu.Unlock() + return nil + } + c.refreshRequired = false + disconnectCh := make(chan struct{}) + c.receive = make(chan []byte, 64) + c.transport = t + c.disconnectedCh = disconnectCh + + go c.reader(t, disconnectCh) + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "started reader loop, sending connect frame", nil) + } + err = c.sendConnect(func(res *protocol.ConnectResult, err error) { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "connect result received", nil) + } + c.mu.Lock() + if c.state != StateConnecting { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "not in connecting state, no need to continue", map[string]string{ + "state": string(c.state), + }) + } + c.mu.Unlock() + return + } + c.mu.Unlock() + if err != nil { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "connect error", map[string]string{ + "error": err.Error(), + }) + } + c.handleError(ConnectError{err}) + _ = t.Close() + if isTokenExpiredError(err) { + c.mu.Lock() + defer c.mu.Unlock() + if c.state != StateConnecting { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "not in connecting state, no need to continue", map[string]string{ + "state": string(c.state), + }) + } + return + } + c.refreshRequired = true + c.scheduleReconnectLocked() + return + } else if isServerError(err) && !isTemporaryError(err) { + var serverError *Error + if errors.As(err, &serverError) { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "server error, move to disconnected", map[string]string{ + "code": strconv.Itoa(int(serverError.Code)), + "message": serverError.Message, + }) + } + c.moveToDisconnected(serverError.Code, serverError.Message) + } else { + // Should not happen, but just in case. + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "not a server error", map[string]string{ + "error": err.Error(), + }) + } + } + return + } else { + c.mu.Lock() + defer c.mu.Unlock() + if c.state != StateConnecting { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "not in connecting state, no need to continue", map[string]string{ + "state": string(c.state), + }) + } + return + } + c.scheduleReconnectLocked() + return + } + } + c.mu.Lock() + if c.state != StateConnecting { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "not in connecting state, no need to continue", map[string]string{ + "state": string(c.state), + }) + } + _ = t.Close() + c.mu.Unlock() + return + } + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "connect ok, move to connected", map[string]string{ + "client_id": res.Client, + }) + } + c.state = StateConnected + + if res.Expires { + c.refreshTimer = time.AfterFunc(time.Duration(res.Ttl)*time.Second, c.sendRefresh) + } + c.resolveConnectFutures(nil) + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "resolved connect futures", nil) + } + c.mu.Unlock() + + if c.events != nil && c.events.onConnected != nil { + handler := c.events.onConnected + ev := ConnectedEvent{ + ClientID: res.Client, + Version: res.Version, + Data: res.Data, + } + c.runHandlerSync(func() { + handler(ev) + }) + } + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "connected event called", nil) + } + + var subscribeHandler ServerSubscribedHandler + if c.events != nil && c.events.onServerSubscribe != nil { + subscribeHandler = c.events.onServerSubscribe + } + + var publishHandler ServerPublicationHandler + if c.events != nil && c.events.onServerPublication != nil { + publishHandler = c.events.onServerPublication + } + + for channel, subRes := range res.Subs { + c.mu.Lock() + sub, ok := c.serverSubs[channel] + if ok { + sub.Epoch = subRes.Epoch + sub.Recoverable = subRes.Recoverable + } else { + sub = &serverSub{ + Epoch: subRes.Epoch, + Offset: subRes.Offset, + Recoverable: subRes.Recoverable, + } + } + if len(subRes.Publications) == 0 { + sub.Offset = subRes.Offset + } + c.serverSubs[channel] = sub + c.mu.Unlock() + + if subscribeHandler != nil { + c.runHandlerSync(func() { + ev := ServerSubscribedEvent{ + Channel: channel, + Data: subRes.GetData(), + Recovered: subRes.GetRecovered(), + WasRecovering: subRes.GetWasRecovering(), + Positioned: subRes.GetPositioned(), + Recoverable: subRes.GetRecoverable(), + } + if ev.Positioned || ev.Recoverable { + ev.StreamPosition = &StreamPosition{ + Epoch: subRes.GetEpoch(), + Offset: subRes.GetOffset(), + } + } + subscribeHandler(ev) + }) + } + if publishHandler != nil { + c.runHandlerSync(func() { + for _, pub := range subRes.Publications { + c.mu.Lock() + if sub, ok := c.serverSubs[channel]; ok { + sub.Offset = pub.Offset + } + c.serverSubs[channel] = sub + c.mu.Unlock() + publishHandler(ServerPublicationEvent{Channel: channel, Publication: pubFromProto(pub)}) + } + }) + } + } + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "connect server-side subscriptions processed", nil) + } + + for ch := range c.serverSubs { + if _, ok := res.Subs[ch]; !ok { + var serverUnsubscribedHandler ServerUnsubscribedHandler + if c.events != nil && c.events.onServerUnsubscribed != nil { + serverUnsubscribedHandler = c.events.onServerUnsubscribed + } + if serverUnsubscribedHandler != nil { + c.runHandlerSync(func() { + serverUnsubscribedHandler(ServerUnsubscribedEvent{Channel: ch}) + }) + } + } + } + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "connect server-side unsubscriptions processed", nil) + } + + c.mu.Lock() + defer c.mu.Unlock() + // Successfully connected – can reset reconnect attempts. + c.reconnectAttempts = 0 + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "reset reconnect attempts counter", nil) + } + + if c.state != StateConnected { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "not in connected state, no need to continue", map[string]string{ + "state": string(c.state), + }) + } + return + } + + if res.Ping > 0 { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "start waiting server ping", map[string]string{ + "ping": strconv.Itoa(int(res.Ping)), + }) + } + c.sendPong = res.Pong + go c.waitServerPing(disconnectCh, res.Ping) + } + c.resubscribe() + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "client-side subscriptions resubscribe called", nil) + } + }) + if err != nil { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "error sending connect frame", map[string]string{ + "error": err.Error(), + }) + } + _ = t.Close() + c.scheduleReconnectLocked() + } else { + if c.logLevelEnabled(LogLevelDebug) { + c.log(LogLevelDebug, "connect frame successfully sent", nil) + } + } + c.mu.Unlock() + if err != nil { + c.handleError(ConnectError{err}) + } + return err +} + +func (c *Client) startConnecting() error { + c.mu.Lock() + if c.state == StateClosed { + c.mu.Unlock() + return ErrClientClosed + } + if c.state == StateConnected || c.state == StateConnecting { + c.mu.Unlock() + return nil + } + if c.closeCh == nil { + c.closeCh = make(chan struct{}) + } + c.state = StateConnecting + c.mu.Unlock() + + var handler ConnectingHandler + if c.events != nil && c.events.onConnecting != nil { + handler = c.events.onConnecting + } + if handler != nil { + c.runHandlerSync(func() { + event := ConnectingEvent{Code: connectingConnectCalled, Reason: "connect called"} + handler(event) + }) + } + + return c.startReconnecting() +} + +func (c *Client) resubscribe() { + for _, sub := range c.subs { + sub.resubscribe() + } +} + +func isTokenExpiredError(err error) bool { + if e, ok := err.(*Error); ok && e.Code == 109 { + return true + } + return false +} + +func isServerError(err error) bool { + if _, ok := err.(*Error); ok { + return true + } + return false +} + +func isTemporaryError(err error) bool { + if e, ok := err.(*Error); ok && e.Temporary { + return true + } + return false +} + +func (c *Client) refreshToken() (string, error) { + handler := c.config.GetToken + if handler == nil { + c.handleError(ConfigurationError{Err: errors.New("GetToken must be set to handle expired token")}) + return "", ErrUnauthorized + } + return handler(ConnectionTokenEvent{}) +} + +func (c *Client) sendRefresh() { + token, err := c.refreshToken() + if err != nil { + if errors.Is(err, ErrUnauthorized) { + c.moveToDisconnected(disconnectedUnauthorized, "unauthorized") + return + } + c.handleError(RefreshError{err}) + c.mu.Lock() + defer c.mu.Unlock() + c.handleRefreshError() + return + } + c.mu.Lock() + c.token = token + c.mu.Unlock() + + cmd := &protocol.Command{ + Id: c.nextCmdID(), + } + params := &protocol.RefreshRequest{ + Token: c.token, + } + cmd.Refresh = params + + _ = c.sendAsync(cmd, func(r *protocol.Reply, err error) { + if err != nil { + c.handleError(RefreshError{err}) + c.mu.Lock() + defer c.mu.Unlock() + c.handleRefreshError() + return + } + if r.Error != nil { + c.mu.Lock() + if c.state != StateConnected { + c.mu.Unlock() + return + } + if r.Error.Temporary { + c.handleError(RefreshError{err}) + c.refreshTimer = time.AfterFunc(10*time.Second, c.sendRefresh) + c.mu.Unlock() + } else { + c.mu.Unlock() + c.moveToDisconnected(r.Error.Code, r.Error.Message) + } + return + } + expires := r.Refresh.Expires + ttl := r.Refresh.Ttl + if expires { + c.mu.Lock() + if c.state == StateConnected { + c.refreshTimer = time.AfterFunc(time.Duration(ttl)*time.Second, c.sendRefresh) + } + c.mu.Unlock() + } + }) +} + +// Lock must be held outside. +func (c *Client) handleRefreshError() { + if c.state != StateConnected { + return + } + c.refreshTimer = time.AfterFunc(10*time.Second, c.sendRefresh) +} + +func (c *Client) sendSubRefresh(channel string, token string, fn func(*protocol.SubRefreshResult, error)) { + cmd := &protocol.Command{ + Id: c.nextCmdID(), + } + params := &protocol.SubRefreshRequest{ + Channel: channel, + Token: token, + } + cmd.SubRefresh = params + + _ = c.sendAsync(cmd, func(r *protocol.Reply, err error) { + if err != nil { + fn(nil, err) + return + } + if r.Error != nil { + fn(nil, errorFromProto(r.Error)) + return + } + fn(r.SubRefresh, nil) + }) +} + +func (c *Client) sendConnect(fn func(*protocol.ConnectResult, error)) error { + cmd := &protocol.Command{ + Id: c.nextCmdID(), + } + + req := &protocol.ConnectRequest{} + req.Token = c.token + req.Name = c.config.Name + req.Version = c.config.Version + req.Data = c.data + + if len(c.serverSubs) > 0 { + subs := make(map[string]*protocol.SubscribeRequest) + for channel, serverSub := range c.serverSubs { + if !serverSub.Recoverable { + continue + } + subs[channel] = &protocol.SubscribeRequest{ + Recover: true, + Epoch: serverSub.Epoch, + Offset: serverSub.Offset, + } + } + req.Subs = subs + } + cmd.Connect = req + + return c.sendAsync(cmd, func(reply *protocol.Reply, err error) { + if err != nil { + fn(nil, err) + return + } + if reply.Error != nil { + fn(nil, errorFromProto(reply.Error)) + return + } + fn(reply.Connect, nil) + }) +} + +type StreamPosition struct { + Offset uint64 + Epoch string +} + +func (c *Client) sendSubscribe( + channel string, data []byte, recover bool, streamPos StreamPosition, token string, + positioned bool, recoverable bool, joinLeave bool, deltaType DeltaType, + fn func(res *protocol.SubscribeResult, err error), +) error { + params := &protocol.SubscribeRequest{ + Channel: channel, + } + + if recover { + params.Recover = true + if streamPos.Offset > 0 { + params.Offset = streamPos.Offset + } + params.Epoch = streamPos.Epoch + } + params.Token = token + params.Data = data + params.Positioned = positioned + params.Recoverable = recoverable + params.JoinLeave = joinLeave + + if deltaType != DeltaTypeNone { + params.Delta = string(deltaType) + } + + cmd := &protocol.Command{ + Id: c.nextCmdID(), + } + cmd.Subscribe = params + + return c.sendAsync(cmd, func(reply *protocol.Reply, err error) { + if err != nil { + fn(nil, err) + return + } + if reply.Error != nil { + fn(nil, errorFromProto(reply.Error)) + return + } + fn(reply.Subscribe, nil) + }) +} + +func (c *Client) nextFutureID() uint64 { + return atomic.AddUint64(&c.futureID, 1) +} + +type connectFuture struct { + fn func(error) + closeCh chan struct{} +} + +func newConnectFuture(fn func(error)) connectFuture { + return connectFuture{fn: fn, closeCh: make(chan struct{})} +} + +// lock must be held outside. +func (c *Client) resolveConnectFutures(err error) { + for _, fut := range c.connectFutures { + fut.fn(err) + close(fut.closeCh) + } + c.connectFutures = make(map[uint64]connectFuture) +} + +func (c *Client) onConnect(fn func(err error)) { + c.mu.Lock() + if c.state == StateConnected { + c.mu.Unlock() + fn(nil) + } else if c.state == StateDisconnected { + c.mu.Unlock() + fn(ErrClientDisconnected) + } else { + defer c.mu.Unlock() + id := c.nextFutureID() + fut := newConnectFuture(fn) + c.connectFutures[id] = fut + go func() { + select { + case <-fut.closeCh: + case <-time.After(c.config.ReadTimeout): + c.mu.Lock() + defer c.mu.Unlock() + fut, ok := c.connectFutures[id] + if !ok { + return + } + delete(c.connectFutures, id) + fut.fn(ErrTimeout) + } + }() + } +} + +// PublishResult contains the result of publish. +type PublishResult struct{} + +// Publish data into channel. +func (c *Client) Publish(ctx context.Context, channel string, data []byte) (PublishResult, error) { + if c.isClosed() { + return PublishResult{}, ErrClientClosed + } + resCh := make(chan PublishResult, 1) + errCh := make(chan error, 1) + c.publish(ctx, channel, data, func(result PublishResult, err error) { + resCh <- result + errCh <- err + }) + select { + case <-ctx.Done(): + return PublishResult{}, ctx.Err() + case res := <-resCh: + return res, <-errCh + } +} + +func (c *Client) publish(ctx context.Context, channel string, data []byte, fn func(PublishResult, error)) { + c.onConnect(func(err error) { + select { + case <-ctx.Done(): + fn(PublishResult{}, ctx.Err()) + return + default: + } + if err != nil { + fn(PublishResult{}, err) + return + } + c.sendPublish(channel, data, fn) + }) +} + +func (c *Client) sendPublish(channel string, data []byte, fn func(PublishResult, error)) { + params := &protocol.PublishRequest{ + Channel: channel, + Data: protocol.Raw(data), + } + + cmd := &protocol.Command{ + Id: c.nextCmdID(), + } + cmd.Publish = params + err := c.sendAsync(cmd, func(r *protocol.Reply, err error) { + if err != nil { + fn(PublishResult{}, err) + return + } + if r.Error != nil { + fn(PublishResult{}, errorFromProto(r.Error)) + return + } + fn(PublishResult{}, nil) + }) + if err != nil { + fn(PublishResult{}, err) + } +} + +// HistoryResult contains the result of history op. +type HistoryResult struct { + Publications []Publication + Offset uint64 + Epoch string +} + +// History for a channel without being subscribed. +func (c *Client) History(ctx context.Context, channel string, opts ...HistoryOption) (HistoryResult, error) { + if c.isClosed() { + return HistoryResult{}, ErrClientClosed + } + resCh := make(chan HistoryResult, 1) + errCh := make(chan error, 1) + historyOpts := &HistoryOptions{} + for _, opt := range opts { + opt(historyOpts) + } + c.history(ctx, channel, *historyOpts, func(result HistoryResult, err error) { + resCh <- result + errCh <- err + }) + select { + case <-ctx.Done(): + return HistoryResult{}, ctx.Err() + case res := <-resCh: + return res, <-errCh + } +} + +func (c *Client) history(ctx context.Context, channel string, opts HistoryOptions, fn func(HistoryResult, error)) { + c.onConnect(func(err error) { + select { + case <-ctx.Done(): + fn(HistoryResult{}, ctx.Err()) + return + default: + } + if err != nil { + fn(HistoryResult{}, err) + return + } + c.sendHistory(channel, opts, fn) + }) +} + +func (c *Client) sendHistory(channel string, opts HistoryOptions, fn func(HistoryResult, error)) { + params := &protocol.HistoryRequest{ + Channel: channel, + Limit: opts.Limit, + Reverse: opts.Reverse, + } + if opts.Since != nil { + params.Since = &protocol.StreamPosition{ + Offset: opts.Since.Offset, + Epoch: opts.Since.Epoch, + } + } + + cmd := &protocol.Command{ + Id: c.nextCmdID(), + } + cmd.History = params + + err := c.sendAsync(cmd, func(r *protocol.Reply, err error) { + if err != nil { + fn(HistoryResult{}, err) + return + } + if r.Error != nil { + fn(HistoryResult{}, errorFromProto(r.Error)) + return + } + + publications := r.History.Publications + offset := r.History.Offset + epoch := r.History.Epoch + + pubs := make([]Publication, len(publications)) + for i, m := range publications { + pubs[i] = pubFromProto(m) + } + fn(HistoryResult{ + Publications: pubs, + Offset: offset, + Epoch: epoch, + }, nil) + }) + if err != nil { + fn(HistoryResult{}, err) + return + } +} + +// PresenceResult contains the result of presence op. +type PresenceResult struct { + Clients map[string]ClientInfo +} + +// Presence for a channel without being subscribed. +func (c *Client) Presence(ctx context.Context, channel string) (PresenceResult, error) { + if c.isClosed() { + return PresenceResult{}, ErrClientClosed + } + resCh := make(chan PresenceResult, 1) + errCh := make(chan error, 1) + c.presence(ctx, channel, func(result PresenceResult, err error) { + resCh <- result + errCh <- err + }) + select { + case <-ctx.Done(): + return PresenceResult{}, ctx.Err() + case res := <-resCh: + return res, <-errCh + } +} + +func (c *Client) presence(ctx context.Context, channel string, fn func(PresenceResult, error)) { + c.onConnect(func(err error) { + select { + case <-ctx.Done(): + fn(PresenceResult{}, ctx.Err()) + return + default: + } + if err != nil { + fn(PresenceResult{}, err) + return + } + c.sendPresence(channel, fn) + }) +} + +func (c *Client) sendPresence(channel string, fn func(PresenceResult, error)) { + params := &protocol.PresenceRequest{ + Channel: channel, + } + + cmd := &protocol.Command{ + Id: c.nextCmdID(), + } + cmd.Presence = params + + err := c.sendAsync(cmd, func(r *protocol.Reply, err error) { + if err != nil { + fn(PresenceResult{}, err) + return + } + if r.Error != nil { + fn(PresenceResult{}, errorFromProto(r.Error)) + return + } + + p := make(map[string]ClientInfo) + + for uid, info := range r.Presence.Presence { + p[uid] = infoFromProto(info) + } + fn(PresenceResult{Clients: p}, nil) + }) + if err != nil { + fn(PresenceResult{}, err) + } +} + +// PresenceStats represents short presence information. +type PresenceStats struct { + NumClients int + NumUsers int +} + +// PresenceStatsResult wraps presence stats. +type PresenceStatsResult struct { + PresenceStats +} + +// PresenceStats for a channel without being subscribed. +func (c *Client) PresenceStats(ctx context.Context, channel string) (PresenceStatsResult, error) { + if c.isClosed() { + return PresenceStatsResult{}, ErrClientClosed + } + resCh := make(chan PresenceStatsResult, 1) + errCh := make(chan error, 1) + c.presenceStats(ctx, channel, func(result PresenceStatsResult, err error) { + resCh <- result + errCh <- err + }) + select { + case <-ctx.Done(): + return PresenceStatsResult{}, ctx.Err() + case res := <-resCh: + return res, <-errCh + } +} + +func (c *Client) presenceStats(ctx context.Context, channel string, fn func(PresenceStatsResult, error)) { + c.onConnect(func(err error) { + select { + case <-ctx.Done(): + fn(PresenceStatsResult{}, ctx.Err()) + return + default: + } + if err != nil { + fn(PresenceStatsResult{}, err) + return + } + c.sendPresenceStats(channel, fn) + }) +} + +func (c *Client) sendPresenceStats(channel string, fn func(PresenceStatsResult, error)) { + params := &protocol.PresenceStatsRequest{ + Channel: channel, + } + + cmd := &protocol.Command{ + Id: c.nextCmdID(), + } + cmd.PresenceStats = params + + err := c.sendAsync(cmd, func(r *protocol.Reply, err error) { + if err != nil { + fn(PresenceStatsResult{}, err) + return + } + if r.Error != nil { + fn(PresenceStatsResult{}, errorFromProto(r.Error)) + return + } + fn(PresenceStatsResult{PresenceStats{ + NumClients: int(r.PresenceStats.NumClients), + NumUsers: int(r.PresenceStats.NumUsers), + }}, nil) + }) + if err != nil { + fn(PresenceStatsResult{}, err) + return + } +} + +type UnsubscribeResult struct{} + +func (c *Client) unsubscribe(channel string, fn func(UnsubscribeResult, error)) { + if !c.isSubscribed(channel) { + return + } + c.mu.Lock() + defer c.mu.Unlock() + if c.state != StateConnected { + return + } + c.sendUnsubscribe(channel, fn) +} + +func (c *Client) sendUnsubscribe(channel string, fn func(UnsubscribeResult, error)) { + params := &protocol.UnsubscribeRequest{ + Channel: channel, + } + + cmd := &protocol.Command{ + Id: c.nextCmdID(), + } + cmd.Unsubscribe = params + + err := c.sendAsync(cmd, func(r *protocol.Reply, err error) { + if err != nil { + fn(UnsubscribeResult{}, err) + return + } + if r.Error != nil { + fn(UnsubscribeResult{}, errorFromProto(r.Error)) + return + } + fn(UnsubscribeResult{}, nil) + }) + if err != nil { + fn(UnsubscribeResult{}, err) + } +} + +func (c *Client) sendAsync(cmd *protocol.Command, cb func(*protocol.Reply, error)) error { + c.addRequest(cmd.Id, cb) + + err := c.send(cmd) + if err != nil { + return err + } + go func() { + c.mu.Lock() + closeCh := c.closeCh + c.mu.Unlock() + defer c.removeRequest(cmd.Id) + select { + case <-time.After(c.config.ReadTimeout): + c.requestsMu.RLock() + req, ok := c.requests[cmd.Id] + c.requestsMu.RUnlock() + if !ok { + return + } + req.cb(nil, ErrTimeout) + case <-closeCh: + c.requestsMu.RLock() + req, ok := c.requests[cmd.Id] + c.requestsMu.RUnlock() + if !ok { + return + } + req.cb(nil, ErrClientDisconnected) + } + }() + return nil +} + +func (c *Client) send(cmd *protocol.Command) error { + transport := c.transport + if transport == nil { + return ErrClientDisconnected + } + if c.logLevelEnabled(LogLevelTrace) { + c.traceOutCmd(cmd) + } + err := transport.Write(cmd, c.config.WriteTimeout) + if err != nil { + go c.handleDisconnect(&disconnect{Code: connectingTransportClosed, Reason: "write error", Reconnect: true}) + return io.EOF + } + return nil +} + +type request struct { + cb func(*protocol.Reply, error) +} + +func (c *Client) addRequest(id uint32, cb func(*protocol.Reply, error)) { + c.requestsMu.Lock() + defer c.requestsMu.Unlock() + c.requests[id] = request{cb} +} + +func (c *Client) removeRequest(id uint32) { + c.requestsMu.Lock() + defer c.requestsMu.Unlock() + delete(c.requests, id) +} + +type disconnect struct { + Code uint32 + Reason string + Reconnect bool +} + +type serverSub struct { + Offset uint64 + Epoch string + Recoverable bool +} diff --git a/vendor/github.com/centrifugal/centrifuge-go/client_events.go b/vendor/github.com/centrifugal/centrifuge-go/client_events.go new file mode 100644 index 0000000000..d863424fa8 --- /dev/null +++ b/vendor/github.com/centrifugal/centrifuge-go/client_events.go @@ -0,0 +1,194 @@ +package centrifuge + +// ConnectionTokenEvent may contain some useful contextual information in the future. +// For now, it's empty. +type ConnectionTokenEvent struct { +} + +// SubscriptionTokenEvent contains info required to get subscription token when +// client wants to subscribe on private channel. +type SubscriptionTokenEvent struct { + Channel string +} + +// ServerPublicationEvent has info about received channel Publication. +type ServerPublicationEvent struct { + Channel string + Publication +} + +// ServerSubscribedEvent has info about server-side subscription. +type ServerSubscribedEvent struct { + Channel string + WasRecovering bool + Recovered bool + Recoverable bool + Positioned bool + StreamPosition *StreamPosition + Data []byte +} + +// ServerJoinEvent has info about user who joined channel. +type ServerJoinEvent struct { + Channel string + ClientInfo +} + +// ServerLeaveEvent has info about user who left channel. +type ServerLeaveEvent struct { + Channel string + ClientInfo +} + +// ServerUnsubscribedEvent is an event passed to unsubscribe event handler. +type ServerUnsubscribedEvent struct { + Channel string +} + +// ServerSubscribingEvent is an event passed to subscribing event handler. +type ServerSubscribingEvent struct { + Channel string +} + +// ConnectedEvent is a connected event context passed to OnConnected callback. +type ConnectedEvent struct { + ClientID string + Version string + Data []byte +} + +// ConnectingEvent is a connecting event context passed to OnConnecting callback. +type ConnectingEvent struct { + Code uint32 + Reason string +} + +// DisconnectedEvent is a disconnected event context passed to OnDisconnected callback. +type DisconnectedEvent struct { + Code uint32 + Reason string +} + +// ErrorEvent is an error event context passed to OnError callback. +type ErrorEvent struct { + Error error +} + +// MessageEvent is an event for async message from server to client. +type MessageEvent struct { + Data []byte +} + +// ConnectingHandler is an interface describing how to handle connecting event. +type ConnectingHandler func(ConnectingEvent) + +// ConnectedHandler is an interface describing how to handle connect event. +type ConnectedHandler func(ConnectedEvent) + +// DisconnectHandler is an interface describing how to handle moveToDisconnected event. +type DisconnectHandler func(DisconnectedEvent) + +// MessageHandler is an interface describing how to handle async message from server. +type MessageHandler func(MessageEvent) + +// ServerPublicationHandler is an interface describing how to handle Publication from +// server-side subscriptions. +type ServerPublicationHandler func(ServerPublicationEvent) + +// ServerSubscribedHandler is an interface describing how to handle subscribe events from +// server-side subscriptions. +type ServerSubscribedHandler func(ServerSubscribedEvent) + +// ServerSubscribingHandler is an interface describing how to handle subscribing events for +// server-side subscriptions. +type ServerSubscribingHandler func(ServerSubscribingEvent) + +// ServerUnsubscribedHandler is an interface describing how to handle unsubscribe events from +// server-side subscriptions. +type ServerUnsubscribedHandler func(ServerUnsubscribedEvent) + +// ServerJoinHandler is an interface describing how to handle Join events from +// server-side subscriptions. +type ServerJoinHandler func(ServerJoinEvent) + +// ServerLeaveHandler is an interface describing how to handle Leave events from +// server-side subscriptions. +type ServerLeaveHandler func(ServerLeaveEvent) + +// ErrorHandler is an interface describing how to handle error event. +type ErrorHandler func(ErrorEvent) + +// eventHub has all event handlers for client. +type eventHub struct { + onConnected ConnectedHandler + onDisconnected DisconnectHandler + onConnecting ConnectingHandler + onError ErrorHandler + onMessage MessageHandler + onServerSubscribe ServerSubscribedHandler + onServerSubscribing ServerSubscribingHandler + onServerUnsubscribed ServerUnsubscribedHandler + onServerPublication ServerPublicationHandler + onServerJoin ServerJoinHandler + onServerLeave ServerLeaveHandler +} + +// newEventHub initializes new eventHub. +func newEventHub() *eventHub { + return &eventHub{} +} + +// OnConnected is a function to handle connect event. +func (c *Client) OnConnected(handler ConnectedHandler) { + c.events.onConnected = handler +} + +// OnConnecting is a function to handle connecting event. +func (c *Client) OnConnecting(handler ConnectingHandler) { + c.events.onConnecting = handler +} + +// OnDisconnected is a function to handle moveToDisconnected event. +func (c *Client) OnDisconnected(handler DisconnectHandler) { + c.events.onDisconnected = handler +} + +// OnError is a function that will receive unhandled errors for logging. +func (c *Client) OnError(handler ErrorHandler) { + c.events.onError = handler +} + +// OnMessage allows processing async message from server to client. +func (c *Client) OnMessage(handler MessageHandler) { + c.events.onMessage = handler +} + +// OnPublication sets function to handle Publications from server-side subscriptions. +func (c *Client) OnPublication(handler ServerPublicationHandler) { + c.events.onServerPublication = handler +} + +// OnSubscribed sets function to handle server-side subscription subscribe events. +func (c *Client) OnSubscribed(handler ServerSubscribedHandler) { + c.events.onServerSubscribe = handler +} + +// OnSubscribing sets function to handle server-side subscription subscribing events. +func (c *Client) OnSubscribing(handler ServerSubscribingHandler) { + c.events.onServerSubscribing = handler +} + +// OnUnsubscribed sets function to handle unsubscribe from server-side subscriptions. +func (c *Client) OnUnsubscribed(handler ServerUnsubscribedHandler) { + c.events.onServerUnsubscribed = handler +} + +// OnJoin sets function to handle Join event from server-side subscriptions. +func (c *Client) OnJoin(handler ServerJoinHandler) { + c.events.onServerJoin = handler +} + +// OnLeave sets function to handle Leave event from server-side subscriptions. +func (c *Client) OnLeave(handler ServerLeaveHandler) { + c.events.onServerLeave = handler +} diff --git a/vendor/github.com/centrifugal/centrifuge-go/codes.go b/vendor/github.com/centrifugal/centrifuge-go/codes.go new file mode 100644 index 0000000000..1b23208fe5 --- /dev/null +++ b/vendor/github.com/centrifugal/centrifuge-go/codes.go @@ -0,0 +1,27 @@ +package centrifuge + +const ( + disconnectedDisconnectCalled uint32 = 0 + disconnectedUnauthorized uint32 = 1 + disconnectBadProtocol uint32 = 2 + disconnectMessageSizeLimit uint32 = 3 +) + +const ( + connectingConnectCalled uint32 = 0 + connectingTransportClosed uint32 = 1 + connectingNoPing uint32 = 2 + connectingSubscribeTimeout uint32 = 3 + connectingUnsubscribeError uint32 = 4 +) + +const ( + subscribingSubscribeCalled uint32 = 0 + subscribingTransportClosed uint32 = 1 +) + +const ( + unsubscribedUnsubscribeCalled uint32 = 0 + unsubscribedUnauthorized uint32 = 1 + unsubscribedClientClosed uint32 = 2 +) diff --git a/vendor/github.com/centrifugal/centrifuge-go/config.go b/vendor/github.com/centrifugal/centrifuge-go/config.go new file mode 100644 index 0000000000..e62159c7fe --- /dev/null +++ b/vendor/github.com/centrifugal/centrifuge-go/config.go @@ -0,0 +1,78 @@ +package centrifuge + +import ( + "context" + "crypto/tls" + "net" + "net/http" + "net/url" + "time" +) + +// Config contains various client options. +type Config struct { + // Token for a connection authentication. + Token string + // GetToken called by SDK to get or refresh connection token. + GetToken func(ConnectionTokenEvent) (string, error) + // Data is an arbitrary data which can be sent to a server in a Connect command. + // Make sure it's a valid JSON when using JSON protocol client. + Data []byte + // CookieJar specifies the cookie jar to send in WebSocket Upgrade request. + CookieJar http.CookieJar + // Header specifies custom HTTP Header to send in WebSocket Upgrade request. + Header http.Header + // Name allows setting client name. You should only use a limited + // amount of client names throughout your applications – i.e. don't + // make it unique per user for example, this name semantically represents + // an environment from which client connects. + // Zero value means "go". + Name string + // Version allows setting client version. This is an application + // specific information. By default, no version set. + Version string + // Proxy specifies a function to return a proxy for a given Request. + // If the function returns a non-nil error, the request is aborted with the + // provided error. If function returns a nil *URL, no proxy is used. + // If Proxy is nil then http.ProxyFromEnvironment will be used. + Proxy func(*http.Request) (*url.URL, error) + // NetDialContext specifies the dial function for creating TCP connections. If + // NetDialContext is nil, net.DialContext is used. + NetDialContext func(ctx context.Context, network, addr string) (net.Conn, error) + // ReadTimeout is how long to wait read operations to complete. + // Zero value means 5 * time.Second. + ReadTimeout time.Duration + // WriteTimeout is Websocket write timeout. + // Zero value means 1 * time.Second. + WriteTimeout time.Duration + // HandshakeTimeout specifies the duration for the handshake to complete. + // Zero value means 1 * time.Second. + HandshakeTimeout time.Duration + // MaxServerPingDelay used to set maximum delay of ping from server. + // Zero value means 10 * time.Second. + MaxServerPingDelay time.Duration + // MinReconnectDelay is the minimum delay between reconnection attempts. + // This delay is jittered. + // Zero value means 200 * time.Millisecond. + MinReconnectDelay time.Duration + // MaxReconnectDelay is the maximum delay between reconnection attempts. + // Zero value means 20 * time.Second. + MaxReconnectDelay time.Duration + // TLSConfig specifies the TLS configuration to use with tls.Client. + // If nil, the default configuration is used. + TLSConfig *tls.Config + // EnableCompression specifies if the client should attempt to negotiate + // per message compression (RFC 7692). Setting this value to true does not + // guarantee that compression will be supported. Currently, only "no context + // takeover" modes are supported. + EnableCompression bool + // LogLevel to use, by default no logs will be exposed by centrifuge-go. Most of the + // time available protocol callbacks cover all necessary information about client-server + // communication. + LogLevel LogLevel + // LogHandler is a function that will be called for each log entry. Log entries + // are sent asynchronously and from a separate goroutine by centrifuge-go with an + // intermediary channel buffer (fixed capacity 256). If your LogHandler is not + // processing log entries fast enough, centrifuge-go will drop log entries. + LogHandler func(LogEntry) +} diff --git a/vendor/github.com/centrifugal/centrifuge-go/errors.go b/vendor/github.com/centrifugal/centrifuge-go/errors.go new file mode 100644 index 0000000000..6b9ff9360f --- /dev/null +++ b/vendor/github.com/centrifugal/centrifuge-go/errors.go @@ -0,0 +1,98 @@ +package centrifuge + +import ( + "errors" + "fmt" +) + +var ( + // ErrTimeout returned if operation timed out. + ErrTimeout = errors.New("timeout") + // ErrClientDisconnected can be returned if client goes to + // disconnected state while operation in progress. + ErrClientDisconnected = errors.New("client disconnected") + // ErrClientClosed can be returned if client is closed. + ErrClientClosed = errors.New("client closed") + // ErrSubscriptionUnsubscribed returned if Subscription is unsubscribed. + ErrSubscriptionUnsubscribed = errors.New("subscription unsubscribed") + // ErrDuplicateSubscription returned if subscription to the same channel + // already registered in current client instance. This is due to the fact + // that server does not allow subscribing to the same channel twice for + // the same connection. + ErrDuplicateSubscription = errors.New("duplicate subscription") + // ErrUnauthorized is a special error which may be returned by application + // from GetToken function to indicate lack of operation permission. + ErrUnauthorized = errors.New("unauthorized") +) + +type TransportError struct { + Err error +} + +func (t TransportError) Error() string { + return fmt.Sprintf("transport error: %v", t.Err) +} + +func (t TransportError) Unwrap() error { + return t.Err +} + +type ConnectError struct { + Err error +} + +func (c ConnectError) Error() string { + return fmt.Sprintf("connect error: %v", c.Err) +} + +func (c ConnectError) Unwrap() error { + return c.Err +} + +type RefreshError struct { + Err error +} + +func (r RefreshError) Error() string { + return fmt.Sprintf("refresh error: %v", r.Err) +} + +func (r RefreshError) Unwrap() error { + return r.Err +} + +type ConfigurationError struct { + Err error +} + +func (c ConfigurationError) Error() string { + return fmt.Sprintf("configuration error: %v", c.Err) +} + +func (c ConfigurationError) Unwrap() error { + return c.Err +} + +type SubscriptionSubscribeError struct { + Err error +} + +func (s SubscriptionSubscribeError) Error() string { + return fmt.Sprintf("subscribe error: %v", s.Err) +} + +func (s SubscriptionSubscribeError) Unwrap() error { + return s.Err +} + +type SubscriptionRefreshError struct { + Err error +} + +func (s SubscriptionRefreshError) Error() string { + return fmt.Sprintf("refresh error: %v", s.Err) +} + +func (s SubscriptionRefreshError) Unwrap() error { + return s.Err +} diff --git a/vendor/github.com/centrifugal/centrifuge-go/log.go b/vendor/github.com/centrifugal/centrifuge-go/log.go new file mode 100644 index 0000000000..ad22c34deb --- /dev/null +++ b/vendor/github.com/centrifugal/centrifuge-go/log.go @@ -0,0 +1,39 @@ +package centrifuge + +// LogHandler handles log entries - i.e. writes into correct destination if necessary. +type LogHandler func(LogEntry) + +// LogLevel describes the chosen log level. +type LogLevel int + +func (l LogLevel) String() string { + switch l { + case LogLevelNone: + return "NONE" + case LogLevelTrace: + return "TRACE" + case LogLevelDebug: + return "DEBUG" + default: + return "UNKNOWN" + } +} + +const ( + // LogLevelNone means no logging. + LogLevelNone LogLevel = iota + // LogLevelTrace turns on trace logs - should only be used during development. This + // log level shows all client-server communication (including token content) and + // affects performance significantly. + LogLevelTrace + // LogLevelDebug turns on debug logs - it's generally too much for production in normal + // conditions but can help when developing and investigating problems in production. + LogLevelDebug +) + +// LogEntry describes a log entry. +type LogEntry struct { + Level LogLevel + Message string + Fields map[string]string +} diff --git a/vendor/github.com/centrifugal/centrifuge-go/protocol.go b/vendor/github.com/centrifugal/centrifuge-go/protocol.go new file mode 100644 index 0000000000..2986be2b49 --- /dev/null +++ b/vendor/github.com/centrifugal/centrifuge-go/protocol.go @@ -0,0 +1,90 @@ +package centrifuge + +import ( + "fmt" + + "github.com/centrifugal/protocol" +) + +// Publication is a data sent to channel. +type Publication struct { + // Offset is an incremental position number inside history stream. + // Zero value means that channel does not maintain Publication stream. + Offset uint64 + // Data published to channel. + Data []byte + // Info is optional information about client connection published + // this data to channel. + Info *ClientInfo + // Tags contain custom key-value pairs attached to Publication. + Tags map[string]string +} + +// ClientInfo contains information about client connection. +type ClientInfo struct { + // Client is a client unique id. + Client string + // User is an ID of authenticated user. Zero value means anonymous user. + User string + // ConnInfo is additional information about connection. + ConnInfo []byte + // ChanInfo is additional information about connection in context of + // channel subscription. + ChanInfo []byte +} + +// Error represents protocol-level error. +type Error struct { + Code uint32 + Message string + Temporary bool +} + +func errorFromProto(err *protocol.Error) *Error { + return &Error{Code: err.Code, Message: err.Message, Temporary: err.Temporary} +} + +func (e Error) Error() string { + return fmt.Sprintf("%d: %s", e.Code, e.Message) +} + +func newReplyDecoder(enc protocol.Type, data []byte) protocol.ReplyDecoder { + if enc == protocol.TypeJSON { + return protocol.NewJSONReplyDecoder(data) + } + return protocol.NewProtobufReplyDecoder(data) +} + +func newCommandEncoder(enc protocol.Type) protocol.CommandEncoder { + if enc == protocol.TypeJSON { + return protocol.NewJSONCommandEncoder() + } + return protocol.NewProtobufCommandEncoder() +} + +func infoFromProto(v *protocol.ClientInfo) ClientInfo { + info := ClientInfo{ + Client: v.GetClient(), + User: v.GetUser(), + } + if len(v.ConnInfo) > 0 { + info.ConnInfo = v.ConnInfo + } + if len(v.ChanInfo) > 0 { + info.ChanInfo = v.ChanInfo + } + return info +} + +func pubFromProto(pub *protocol.Publication) Publication { + p := Publication{ + Offset: pub.GetOffset(), + Data: pub.Data, + Tags: pub.GetTags(), + } + if pub.GetInfo() != nil { + info := infoFromProto(pub.GetInfo()) + p.Info = &info + } + return p +} diff --git a/vendor/github.com/centrifugal/centrifuge-go/queue.go b/vendor/github.com/centrifugal/centrifuge-go/queue.go new file mode 100644 index 0000000000..20595e4c23 --- /dev/null +++ b/vendor/github.com/centrifugal/centrifuge-go/queue.go @@ -0,0 +1,96 @@ +package centrifuge + +import ( + "sync" + "time" +) + +// cbQueue allows processing callbacks in separate goroutine with +// preserved order. +// This queue implementation is a slightly modified code borrowed from +// https://github.com/nats-io/nats.go client released under Apache 2.0 +// license: see https://github.com/nats-io/nats.go/blob/master/LICENSE. +type cbQueue struct { + mu sync.Mutex + cond *sync.Cond + head *asyncCB + tail *asyncCB + closeCh chan struct{} + closed bool +} + +type asyncCB struct { + fn func(delay time.Duration) + tm time.Time + next *asyncCB +} + +// dispatch is responsible for calling async callbacks. Should be run +// in separate goroutine. +func (q *cbQueue) dispatch() { + for { + q.mu.Lock() + // Protect for spurious wake-ups. We should get out of the + // wait only if there is an element to pop from the list. + for q.head == nil { + q.cond.Wait() + } + curr := q.head + q.head = curr.next + if curr == q.tail { + q.tail = nil + } + q.mu.Unlock() + + // This signals that the dispatcher has been closed and all + // previous callbacks have been dispatched. + if curr.fn == nil { + close(q.closeCh) + return + } + curr.fn(time.Since(curr.tm)) + } +} + +// Push adds the given function to the tail of the list and +// signals the dispatcher. +func (q *cbQueue) push(f func(duration time.Duration)) { + q.pushOrClose(f, false) +} + +// Close signals that async queue must be closed. +// Queue won't accept any more callbacks after that – ignoring them if pushed. +func (q *cbQueue) close() { + q.pushOrClose(nil, true) + q.waitClose() +} + +func (q *cbQueue) waitClose() { + <-q.closeCh +} + +func (q *cbQueue) pushOrClose(f func(time.Duration), close bool) { + q.mu.Lock() + defer q.mu.Unlock() + if q.closed { + return + } + // Make sure that library is not calling push with nil function, + // since this is used to notify the dispatcher that it must stop. + if !close && f == nil { + panic("pushing a nil callback with false close") + } + cb := &asyncCB{fn: f, tm: time.Now()} + if q.tail != nil { + q.tail.next = cb + } else { + q.head = cb + } + q.tail = cb + if close { + q.closed = true + q.cond.Broadcast() + } else { + q.cond.Signal() + } +} diff --git a/vendor/github.com/centrifugal/centrifuge-go/reconnect.go b/vendor/github.com/centrifugal/centrifuge-go/reconnect.go new file mode 100644 index 0000000000..fa1880f88b --- /dev/null +++ b/vendor/github.com/centrifugal/centrifuge-go/reconnect.go @@ -0,0 +1,49 @@ +package centrifuge + +import ( + "time" + + "github.com/jpillora/backoff" +) + +type reconnectStrategy interface { + timeBeforeNextAttempt(attempt int) time.Duration +} + +type backoffReconnect struct { + // Factor is the multiplying factor for each increment step. + Factor float64 + // Jitter eases contention by randomizing backoff steps. + Jitter bool + // MinMilliseconds is a minimum value of reconnect interval. + MinDelay time.Duration + // MaxMilliseconds is a maximum value of reconnect interval. + MaxDelay time.Duration +} + +func (r *backoffReconnect) timeBeforeNextAttempt(attempt int) time.Duration { + b := &backoff.Backoff{ + Min: r.MinDelay, + Max: r.MaxDelay, + Factor: r.Factor, + Jitter: r.Jitter, + } + return b.ForAttempt(float64(attempt)) +} + +// newBackoffReconnect creates a new backoff reconnect strategy with custom min and max delays. +// If minDelay or maxDelay is zero, it uses the default values. +func newBackoffReconnect(minDelay, maxDelay time.Duration) reconnectStrategy { + if minDelay == 0 { + minDelay = 200 * time.Millisecond + } + if maxDelay == 0 { + maxDelay = 20 * time.Second + } + return &backoffReconnect{ + MinDelay: minDelay, + MaxDelay: maxDelay, + Factor: 2, + Jitter: true, + } +} diff --git a/vendor/github.com/centrifugal/centrifuge-go/subscription.go b/vendor/github.com/centrifugal/centrifuge-go/subscription.go new file mode 100644 index 0000000000..038d02eb17 --- /dev/null +++ b/vendor/github.com/centrifugal/centrifuge-go/subscription.go @@ -0,0 +1,862 @@ +package centrifuge + +import ( + "context" + "encoding/json" + "errors" + "sync" + "sync/atomic" + "time" + + "github.com/centrifugal/protocol" + fossil "github.com/shadowspore/fossil-delta" +) + +// SubState represents state of Subscription. +type SubState string + +// Different states of Subscription. +const ( + SubStateUnsubscribed SubState = "unsubscribed" + SubStateSubscribing SubState = "subscribing" + SubStateSubscribed SubState = "subscribed" +) + +// DeltaType represents type of delta used for Subscription. +type DeltaType string + +const ( + // DeltaTypeNone means that no delta is used for this subscription. + DeltaTypeNone DeltaType = "" + // DeltaTypeFossil means Fossil-based delta. + DeltaTypeFossil DeltaType = "fossil" +) + +// SubscriptionConfig allows setting Subscription options. +type SubscriptionConfig struct { + // Data is an arbitrary data to pass to a server in each subscribe request. + Data []byte + // Token for Subscription. + Token string + // GetToken called to get or refresh private channel subscription token. + GetToken func(SubscriptionTokenEvent) (string, error) + // Positioned flag asks server to make Subscription positioned. Only makes sense + // in channels with history stream on. + Positioned bool + // Recoverable flag asks server to make Subscription recoverable. Only makes sense + // in channels with history stream on. + Recoverable bool + // JoinLeave flag asks server to push join/leave messages. + JoinLeave bool + // Delta allows to specify delta type for the subscription. By default, no delta is used. + Delta DeltaType + // MinResubscribeDelay is the minimum delay between resubscription attempts. + // This delay is jittered. + // Zero value means 200 * time.Millisecond. + MinResubscribeDelay time.Duration + // MaxResubscribeDelay is the maximum delay between resubscription attempts. + // Zero value means 20 * time.Second. + MaxResubscribeDelay time.Duration +} + +func newSubscription(c *Client, channel string, config ...SubscriptionConfig) *Subscription { + var resubscribeStrategy reconnectStrategy + var minResubscribeDelay, maxResubscribeDelay time.Duration + if len(config) == 1 { + minResubscribeDelay = config[0].MinResubscribeDelay + maxResubscribeDelay = config[0].MaxResubscribeDelay + } + resubscribeStrategy = newBackoffReconnect(minResubscribeDelay, maxResubscribeDelay) + s := &Subscription{ + Channel: channel, + centrifuge: c, + state: SubStateUnsubscribed, + events: newSubscriptionEventHub(), + subFutures: make(map[uint64]subFuture), + resubscribeStrategy: resubscribeStrategy, + } + if len(config) == 1 { + cfg := config[0] + s.token = cfg.Token + s.getToken = cfg.GetToken + s.data = cfg.Data + s.positioned = cfg.Positioned + s.recoverable = cfg.Recoverable + s.joinLeave = cfg.JoinLeave + s.deltaType = cfg.Delta + } + return s +} + +// Subscription represents client subscription to channel. DO NOT initialize this struct +// directly, instead use Client.NewSubscription method to create channel subscriptions. +type Subscription struct { + futureID uint64 // Keep atomic on top! + + mu sync.RWMutex + centrifuge *Client + + // Channel for a subscription. + Channel string + + state SubState + + events *subscriptionEventHub + offset uint64 + epoch string + recover bool + subFutures map[uint64]subFuture + data []byte + + positioned bool + recoverable bool + joinLeave bool + + token string + getToken func(SubscriptionTokenEvent) (string, error) + + resubscribeAttempts int + resubscribeStrategy reconnectStrategy + + resubscribeTimer *time.Timer + refreshTimer *time.Timer + + deltaType DeltaType + deltaNegotiated bool + prevData []byte + + inflight atomic.Bool +} + +func (s *Subscription) State() SubState { + s.mu.RLock() + defer s.mu.RUnlock() + return s.state +} + +type subFuture struct { + fn func(error) + closeCh chan struct{} +} + +func newSubFuture(fn func(error)) subFuture { + return subFuture{fn: fn, closeCh: make(chan struct{})} +} + +func (s *Subscription) nextFutureID() uint64 { + return atomic.AddUint64(&s.futureID, 1) +} + +// Lock must be held outside. +func (s *Subscription) resolveSubFutures(err error) { + for _, fut := range s.subFutures { + fut.fn(err) + close(fut.closeCh) + } + s.subFutures = make(map[uint64]subFuture) +} + +// Publish allows publishing data to the subscription channel. +func (s *Subscription) Publish(ctx context.Context, data []byte) (PublishResult, error) { + s.mu.Lock() + if s.state == SubStateUnsubscribed { + s.mu.Unlock() + return PublishResult{}, ErrSubscriptionUnsubscribed + } + s.mu.Unlock() + + resCh := make(chan PublishResult, 1) + errCh := make(chan error, 1) + s.publish(ctx, data, func(result PublishResult, err error) { + resCh <- result + errCh <- err + }) + select { + case <-ctx.Done(): + return PublishResult{}, ctx.Err() + case res := <-resCh: + return res, <-errCh + } +} + +type HistoryOptions struct { + Limit int32 + Since *StreamPosition + Reverse bool +} + +type HistoryOption func(options *HistoryOptions) + +func WithHistorySince(sp *StreamPosition) HistoryOption { + return func(options *HistoryOptions) { + options.Since = sp + } +} + +func WithHistoryLimit(limit int32) HistoryOption { + return func(options *HistoryOptions) { + options.Limit = limit + } +} + +func WithHistoryReverse(reverse bool) HistoryOption { + return func(options *HistoryOptions) { + options.Reverse = reverse + } +} + +// History allows extracting channel history. By default, it returns current stream top +// position without publications. Use WithHistoryLimit with a value > 0 to make this func +// to return publications. +func (s *Subscription) History(ctx context.Context, opts ...HistoryOption) (HistoryResult, error) { + historyOpts := &HistoryOptions{} + for _, opt := range opts { + opt(historyOpts) + } + s.mu.Lock() + if s.state == SubStateUnsubscribed { + s.mu.Unlock() + return HistoryResult{}, ErrSubscriptionUnsubscribed + } + s.mu.Unlock() + + resCh := make(chan HistoryResult, 1) + errCh := make(chan error, 1) + s.history(ctx, *historyOpts, func(result HistoryResult, err error) { + resCh <- result + errCh <- err + }) + select { + case <-ctx.Done(): + return HistoryResult{}, ctx.Err() + case res := <-resCh: + return res, <-errCh + } +} + +// Presence allows extracting channel presence. +func (s *Subscription) Presence(ctx context.Context) (PresenceResult, error) { + s.mu.Lock() + if s.state == SubStateUnsubscribed { + s.mu.Unlock() + return PresenceResult{}, ErrSubscriptionUnsubscribed + } + s.mu.Unlock() + + resCh := make(chan PresenceResult, 1) + errCh := make(chan error, 1) + s.presence(ctx, func(result PresenceResult, err error) { + resCh <- result + errCh <- err + }) + select { + case <-ctx.Done(): + return PresenceResult{}, ctx.Err() + case res := <-resCh: + return res, <-errCh + } +} + +// PresenceStats allows extracting channel presence stats. +func (s *Subscription) PresenceStats(ctx context.Context) (PresenceStatsResult, error) { + s.mu.Lock() + if s.state == SubStateUnsubscribed { + s.mu.Unlock() + return PresenceStatsResult{}, ErrSubscriptionUnsubscribed + } + s.mu.Unlock() + + resCh := make(chan PresenceStatsResult, 1) + errCh := make(chan error, 1) + s.presenceStats(ctx, func(result PresenceStatsResult, err error) { + resCh <- result + errCh <- err + }) + select { + case <-ctx.Done(): + return PresenceStatsResult{}, ctx.Err() + case res := <-resCh: + return res, <-errCh + } +} + +func (s *Subscription) onSubscribe(fn func(err error)) { + s.mu.Lock() + defer s.mu.Unlock() + if s.state == SubStateSubscribed { + go fn(nil) + } else if s.state == SubStateUnsubscribed { + go fn(ErrSubscriptionUnsubscribed) + } else { + id := s.nextFutureID() + fut := newSubFuture(fn) + s.subFutures[id] = fut + go func() { + select { + case <-fut.closeCh: + case <-time.After(s.centrifuge.config.ReadTimeout): + s.mu.Lock() + defer s.mu.Unlock() + fut, ok := s.subFutures[id] + if !ok { + return + } + delete(s.subFutures, id) + fut.fn(ErrTimeout) + } + }() + } +} + +func (s *Subscription) publish(ctx context.Context, data []byte, fn func(PublishResult, error)) { + s.onSubscribe(func(err error) { + select { + case <-ctx.Done(): + fn(PublishResult{}, ctx.Err()) + return + default: + } + if err != nil { + fn(PublishResult{}, err) + return + } + s.centrifuge.publish(ctx, s.Channel, data, fn) + }) +} + +func (s *Subscription) history(ctx context.Context, opts HistoryOptions, fn func(HistoryResult, error)) { + s.onSubscribe(func(err error) { + select { + case <-ctx.Done(): + fn(HistoryResult{}, ctx.Err()) + return + default: + } + if err != nil { + fn(HistoryResult{}, err) + return + } + s.centrifuge.history(ctx, s.Channel, opts, fn) + }) +} + +func (s *Subscription) presence(ctx context.Context, fn func(PresenceResult, error)) { + s.onSubscribe(func(err error) { + select { + case <-ctx.Done(): + fn(PresenceResult{}, ctx.Err()) + return + default: + } + if err != nil { + fn(PresenceResult{}, err) + return + } + s.centrifuge.presence(ctx, s.Channel, fn) + }) +} + +func (s *Subscription) presenceStats(ctx context.Context, fn func(PresenceStatsResult, error)) { + s.onSubscribe(func(err error) { + select { + case <-ctx.Done(): + fn(PresenceStatsResult{}, ctx.Err()) + return + default: + } + if err != nil { + fn(PresenceStatsResult{}, err) + return + } + s.centrifuge.presenceStats(ctx, s.Channel, fn) + }) +} + +// Unsubscribe allows unsubscribing from channel. +func (s *Subscription) Unsubscribe() error { + if s.centrifuge.isClosed() { + return ErrClientClosed + } + s.unsubscribe(unsubscribedUnsubscribeCalled, "unsubscribe called", true) + return nil +} + +func (s *Subscription) unsubscribe(code uint32, reason string, sendUnsubscribe bool) { + s.moveToUnsubscribed(code, reason) + if sendUnsubscribe { + s.centrifuge.unsubscribe(s.Channel, func(result UnsubscribeResult, err error) { + if err != nil { + go s.centrifuge.handleDisconnect(&disconnect{Code: connectingUnsubscribeError, Reason: "unsubscribe error", Reconnect: true}) + return + } + }) + } +} + +// Subscribe allows initiating subscription process. +func (s *Subscription) Subscribe() error { + if s.centrifuge.isClosed() { + return ErrClientClosed + } + s.mu.Lock() + if s.state == SubStateSubscribed || s.state == SubStateSubscribing { + s.mu.Unlock() + return nil + } + s.state = SubStateSubscribing + s.mu.Unlock() + + if s.events != nil && s.events.onSubscribing != nil { + handler := s.events.onSubscribing + s.centrifuge.runHandlerAsync(func() { + handler(SubscribingEvent{ + Code: subscribingSubscribeCalled, + Reason: "subscribe called", + }) + }) + } + + if !s.centrifuge.isConnected() { + return nil + } + s.resubscribe() + return nil +} + +func (s *Subscription) moveToUnsubscribed(code uint32, reason string) { + s.mu.Lock() + s.resubscribeAttempts = 0 + if s.resubscribeTimer != nil { + s.resubscribeTimer.Stop() + } + if s.refreshTimer != nil { + s.refreshTimer.Stop() + } + + needEvent := s.state != SubStateUnsubscribed + s.state = SubStateUnsubscribed + s.mu.Unlock() + + if needEvent && s.events != nil && s.events.onUnsubscribe != nil { + handler := s.events.onUnsubscribe + s.centrifuge.runHandlerAsync(func() { + handler(UnsubscribedEvent{ + Code: code, + Reason: reason, + }) + }) + } +} + +func (s *Subscription) moveToSubscribing(code uint32, reason string) { + s.mu.Lock() + s.resubscribeAttempts = 0 + if s.resubscribeTimer != nil { + s.resubscribeTimer.Stop() + } + if s.refreshTimer != nil { + s.refreshTimer.Stop() + } + needEvent := s.state != SubStateSubscribing + s.state = SubStateSubscribing + s.mu.Unlock() + + if needEvent && s.events != nil && s.events.onSubscribing != nil { + handler := s.events.onSubscribing + s.centrifuge.runHandlerAsync(func() { + handler(SubscribingEvent{ + Code: code, + Reason: reason, + }) + }) + } +} + +func (s *Subscription) moveToSubscribed(res *protocol.SubscribeResult) { + s.mu.Lock() + if s.state != SubStateSubscribing { + s.mu.Unlock() + return + } + s.state = SubStateSubscribed + if res.Expires { + s.scheduleSubRefresh(res.Ttl) + } + if res.Recoverable { + s.recover = true + } + s.resubscribeAttempts = 0 + if s.resubscribeTimer != nil { + s.resubscribeTimer.Stop() + } + s.resolveSubFutures(nil) + s.offset = res.Offset + s.epoch = res.Epoch + s.deltaNegotiated = res.Delta + s.mu.Unlock() + + if s.events != nil && s.events.onSubscribed != nil { + handler := s.events.onSubscribed + ev := SubscribedEvent{ + Data: res.GetData(), + Recovered: res.GetRecovered(), + WasRecovering: res.GetWasRecovering(), + Recoverable: res.GetRecoverable(), + Positioned: res.GetPositioned(), + } + if ev.Positioned || ev.Recoverable { + ev.StreamPosition = &StreamPosition{ + Epoch: res.GetEpoch(), + Offset: res.GetOffset(), + } + } + s.centrifuge.runHandlerSync(func() { + handler(ev) + }) + } + + if len(res.Publications) > 0 { + s.centrifuge.runHandlerSync(func() { + pubs := res.Publications + for i := 0; i < len(pubs); i++ { + pub := res.Publications[i] + s.mu.Lock() + if s.state != SubStateSubscribed { + s.mu.Unlock() + return + } + if pub.Offset > 0 { + s.offset = pub.Offset + } + publicationEvent := PublicationEvent{Publication: pubFromProto(pub)} + publicationEvent = s.applyDeltaLocked(pub, publicationEvent) + s.mu.Unlock() + var handler PublicationHandler + if s.events != nil && s.events.onPublication != nil { + handler = s.events.onPublication + } + if handler != nil { + handler(publicationEvent) + } + } + }) + } +} + +func (s *Subscription) applyDeltaLocked(pub *protocol.Publication, event PublicationEvent) PublicationEvent { + if !s.deltaNegotiated { + return event + } + if s.centrifuge.protocolType == protocol.TypeJSON { + if pub.Delta { + // pub.Data is JSON string delta, let's decode to []byte and apply it to prevData. + var delta string + err := json.Unmarshal(pub.Data, &delta) + if err != nil { + panic(err) + } + newData, err := fossil.Apply(s.prevData, []byte(delta)) + if err != nil { + panic(err) + } + event.Data = newData + s.prevData = newData + } else { + // pub.Data is JSON string, let's decode to []byte and keep as prevData. + var data string + err := json.Unmarshal(pub.Data, &data) + if err != nil { + panic(err) + } + s.prevData = []byte(data) + event.Data = s.prevData + } + } else { + if pub.Delta { + newData, err := fossil.Apply(s.prevData, pub.Data) + if err != nil { + panic(err) + } + event.Data = newData + s.prevData = newData + } else { + s.prevData = pub.Data + } + } + return event +} + +// Lock must be held outside. +func (s *Subscription) scheduleResubscribe() { + if s.resubscribeTimer != nil { + if s.centrifuge.logLevelEnabled(LogLevelDebug) { + s.centrifuge.log(LogLevelDebug, "stopping previous resubscribe timer", map[string]string{ + "channel": s.Channel, + }) + } + s.resubscribeTimer.Stop() + s.resubscribeTimer = nil + } + delay := s.resubscribeStrategy.timeBeforeNextAttempt(s.resubscribeAttempts) + s.resubscribeAttempts++ + s.resubscribeTimer = time.AfterFunc(delay, func() { + s.mu.Lock() + if s.state != SubStateSubscribing { + s.mu.Unlock() + return + } + s.mu.Unlock() + s.resubscribe() + }) +} + +func (s *Subscription) subscribeError(err error) { + s.mu.Lock() + if s.state != SubStateSubscribing { + s.mu.Unlock() + return + } + s.mu.Unlock() + + if errors.Is(err, ErrTimeout) { + go s.centrifuge.handleDisconnect(&disconnect{Code: connectingSubscribeTimeout, Reason: "subscribe timeout", Reconnect: true}) + return + } + + s.emitError(SubscriptionSubscribeError{Err: err}) + + var serverError *Error + if errors.As(err, &serverError) { + if serverError.Code == 109 { // Token expired. + s.mu.Lock() + s.token = "" + s.scheduleResubscribe() + s.mu.Unlock() + } else if serverError.Temporary { + s.mu.Lock() + s.scheduleResubscribe() + s.mu.Unlock() + } else { + s.mu.Lock() + s.resolveSubFutures(err) + s.mu.Unlock() + s.unsubscribe(serverError.Code, serverError.Message, false) + } + } else { + s.mu.Lock() + s.scheduleResubscribe() + s.mu.Unlock() + } +} + +// Lock must be held outside. +func (s *Subscription) emitError(err error) { + if s.events != nil && s.events.onError != nil { + handler := s.events.onError + s.centrifuge.runHandlerSync(func() { + handler(SubscriptionErrorEvent{Error: err}) + }) + } +} + +func (s *Subscription) handlePublication(pub *protocol.Publication) { + s.mu.Lock() + if s.state != SubStateSubscribed { + s.mu.Unlock() + return + } + if pub.Offset > 0 { + s.offset = pub.Offset + } + publicationEvent := PublicationEvent{Publication: pubFromProto(pub)} + publicationEvent = s.applyDeltaLocked(pub, publicationEvent) + s.mu.Unlock() + + var handler PublicationHandler + if s.events != nil && s.events.onPublication != nil { + handler = s.events.onPublication + } + if handler == nil { + return + } + s.centrifuge.runHandlerSync(func() { + handler(publicationEvent) + }) +} + +func (s *Subscription) handleJoin(info *protocol.ClientInfo) { + var handler JoinHandler + if s.events != nil && s.events.onJoin != nil { + handler = s.events.onJoin + } + if handler != nil { + s.centrifuge.runHandlerSync(func() { + handler(JoinEvent{ClientInfo: infoFromProto(info)}) + }) + } +} + +func (s *Subscription) handleLeave(info *protocol.ClientInfo) { + var handler LeaveHandler + if s.events != nil && s.events.onLeave != nil { + handler = s.events.onLeave + } + if handler != nil { + s.centrifuge.runHandlerSync(func() { + handler(LeaveEvent{ClientInfo: infoFromProto(info)}) + }) + } +} + +func (s *Subscription) handleUnsubscribe(unsubscribe *protocol.Unsubscribe) { + if unsubscribe.Code < 2500 { + s.moveToUnsubscribed(unsubscribe.Code, unsubscribe.Reason) + } else { + s.moveToSubscribing(unsubscribe.Code, unsubscribe.Reason) + s.resubscribe() + } +} + +func (s *Subscription) resubscribe() { + s.mu.Lock() + if s.state != SubStateSubscribing { + s.mu.Unlock() + return + } + if s.inflight.Load() { + s.mu.Unlock() + if s.centrifuge.logLevelEnabled(LogLevelDebug) { + s.centrifuge.log(LogLevelDebug, "avoid subscribe since inflight", map[string]string{ + "channel": s.Channel, + }) + } + return + } + token := s.token + s.inflight.Store(true) + s.mu.Unlock() + + if token == "" && s.getToken != nil { + var err error + token, err = s.getSubscriptionToken(s.Channel) + if err != nil { + if errors.Is(err, ErrUnauthorized) { + s.inflight.Store(false) + s.unsubscribe(unsubscribedUnauthorized, "unauthorized", false) + return + } + s.inflight.Store(false) + s.subscribeError(err) + return + } + s.mu.Lock() + if token == "" { + s.mu.Unlock() + s.inflight.Store(false) + s.unsubscribe(unsubscribedUnauthorized, "unauthorized", false) + return + } + s.token = token + s.mu.Unlock() + } + + s.mu.Lock() + defer s.mu.Unlock() + if s.state != SubStateSubscribing { + s.inflight.Store(false) + return + } + + var isRecover bool + var sp StreamPosition + if s.recover { + isRecover = true + sp.Offset = s.offset + sp.Epoch = s.epoch + } + + err := s.centrifuge.sendSubscribe(s.Channel, s.data, isRecover, sp, token, s.positioned, s.recoverable, s.joinLeave, s.deltaType, func(res *protocol.SubscribeResult, err error) { + if err != nil { + s.inflight.Store(false) + s.subscribeError(err) + return + } + s.inflight.Store(false) + s.moveToSubscribed(res) + }) + if err != nil { + s.inflight.Store(false) + s.scheduleResubscribe() + } +} + +func (s *Subscription) getSubscriptionToken(channel string) (string, error) { + handler := s.getToken + if handler != nil { + ev := SubscriptionTokenEvent{ + Channel: channel, + } + return handler(ev) + } + return "", errors.New("GetToken must be set to get subscription token") +} + +// Lock must be held outside. +func (s *Subscription) scheduleSubRefresh(ttl uint32) { + if s.state != SubStateSubscribed { + return + } + s.refreshTimer = time.AfterFunc(time.Duration(ttl)*time.Second, func() { + s.mu.Lock() + if s.state != SubStateSubscribed { + s.mu.Unlock() + return + } + s.mu.Unlock() + + token, err := s.getSubscriptionToken(s.Channel) + if err != nil { + if errors.Is(err, ErrUnauthorized) { + s.unsubscribe(unsubscribedUnauthorized, "unauthorized", true) + return + } + s.mu.Lock() + defer s.mu.Unlock() + s.emitError(SubscriptionRefreshError{Err: err}) + s.scheduleSubRefresh(10) + return + } + if token == "" { + s.unsubscribe(unsubscribedUnauthorized, "unauthorized", true) + return + } + + s.centrifuge.sendSubRefresh(s.Channel, token, func(result *protocol.SubRefreshResult, err error) { + if err != nil { + s.emitError(SubscriptionSubscribeError{Err: err}) + var serverError *Error + if errors.As(err, &serverError) { + if serverError.Temporary { + s.mu.Lock() + defer s.mu.Unlock() + s.scheduleSubRefresh(10) + return + } else { + s.unsubscribe(serverError.Code, serverError.Message, true) + return + } + } else { + s.mu.Lock() + defer s.mu.Unlock() + s.scheduleSubRefresh(10) + return + } + } + if result.Expires { + s.mu.Lock() + s.scheduleSubRefresh(result.Ttl) + s.mu.Unlock() + } + }) + }) +} diff --git a/vendor/github.com/centrifugal/centrifuge-go/subscription_events.go b/vendor/github.com/centrifugal/centrifuge-go/subscription_events.go new file mode 100644 index 0000000000..b9dca8bbf3 --- /dev/null +++ b/vendor/github.com/centrifugal/centrifuge-go/subscription_events.go @@ -0,0 +1,119 @@ +package centrifuge + +// SubscribedEvent is an event context passed +// to subscribe success callback. +type SubscribedEvent struct { + Positioned bool + Recoverable bool + StreamPosition *StreamPosition + WasRecovering bool + Recovered bool + Data []byte +} + +// SubscriptionErrorEvent is a subscribe error event context passed to +// event callback. +type SubscriptionErrorEvent struct { + Error error +} + +// SubscribingEvent is an event passed to subscribing event handler. +type SubscribingEvent struct { + Code uint32 + Reason string +} + +// UnsubscribedEvent is an event passed to unsubscribe event handler. +type UnsubscribedEvent struct { + Code uint32 + Reason string +} + +// LeaveEvent has info about user who left channel. +type LeaveEvent struct { + ClientInfo +} + +// JoinEvent has info about user who joined channel. +type JoinEvent struct { + ClientInfo +} + +// PublicationEvent has info about received channel Publication. +type PublicationEvent struct { + Publication +} + +// PublicationHandler is a function to handle messages published in +// channels. +type PublicationHandler func(PublicationEvent) + +// JoinHandler is a function to handle join messages. +type JoinHandler func(JoinEvent) + +// LeaveHandler is a function to handle leave messages. +type LeaveHandler func(LeaveEvent) + +// UnsubscribedHandler is a function to handle unsubscribe event. +type UnsubscribedHandler func(UnsubscribedEvent) + +// SubscribingHandler is a function to handle subscribe success event. +type SubscribingHandler func(SubscribingEvent) + +// SubscribedHandler is a function to handle subscribe success event. +type SubscribedHandler func(SubscribedEvent) + +// SubscriptionErrorHandler is a function to handle subscribe error event. +type SubscriptionErrorHandler func(SubscriptionErrorEvent) + +// subscriptionEventHub contains callback functions that will be called when +// corresponding event happens with subscription to channel. +type subscriptionEventHub struct { + onSubscribing SubscribingHandler + onSubscribed SubscribedHandler + onUnsubscribe UnsubscribedHandler + onError SubscriptionErrorHandler + onPublication PublicationHandler + onJoin JoinHandler + onLeave LeaveHandler +} + +// newSubscriptionEventHub initializes new subscriptionEventHub. +func newSubscriptionEventHub() *subscriptionEventHub { + return &subscriptionEventHub{} +} + +// OnSubscribing allows setting SubscribingHandler to SubEventHandler. +func (s *Subscription) OnSubscribing(handler SubscribingHandler) { + s.events.onSubscribing = handler +} + +// OnSubscribed allows setting SubscribedHandler to SubEventHandler. +func (s *Subscription) OnSubscribed(handler SubscribedHandler) { + s.events.onSubscribed = handler +} + +// OnUnsubscribed allows setting UnsubscribedHandler to SubEventHandler. +func (s *Subscription) OnUnsubscribed(handler UnsubscribedHandler) { + s.events.onUnsubscribe = handler +} + +// OnError allows setting SubscriptionErrorHandler to SubEventHandler. +func (s *Subscription) OnError(handler SubscriptionErrorHandler) { + s.events.onError = handler +} + +// OnPublication allows setting PublicationHandler to SubEventHandler. +func (s *Subscription) OnPublication(handler PublicationHandler) { + s.events.onPublication = handler +} + +// OnJoin allows setting JoinHandler to SubEventHandler. +func (s *Subscription) OnJoin(handler JoinHandler) { + s.events.onJoin = handler +} + +// OnLeave allows setting LeaveHandler to SubEventHandler. +func (s *Subscription) OnLeave(handler LeaveHandler) { + s.events.onLeave = handler +} diff --git a/vendor/github.com/centrifugal/centrifuge-go/transport.go b/vendor/github.com/centrifugal/centrifuge-go/transport.go new file mode 100644 index 0000000000..b225281d20 --- /dev/null +++ b/vendor/github.com/centrifugal/centrifuge-go/transport.go @@ -0,0 +1,20 @@ +package centrifuge + +import ( + "time" + + "github.com/centrifugal/protocol" +) + +type transport interface { + // Read should read new Reply messages from connection. + // It should not be thread-safe as we will call it from one goroutine. + Read() (*protocol.Reply, *disconnect, error) + // Write should write Command to connection with specified write timeout. + // It should not be thread-safe as we will call it from one goroutine. + Write(cmd *protocol.Command, timeout time.Duration) error + // Close should close connection and do all cleanups required. + // It must be safe to call Close several times and concurrently with Read + // and Write methods. + Close() error +} diff --git a/vendor/github.com/centrifugal/centrifuge-go/transport_websocket.go b/vendor/github.com/centrifugal/centrifuge-go/transport_websocket.go new file mode 100644 index 0000000000..a0f39dd18e --- /dev/null +++ b/vendor/github.com/centrifugal/centrifuge-go/transport_websocket.go @@ -0,0 +1,220 @@ +package centrifuge + +import ( + "context" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "net" + "net/http" + "net/url" + "sync" + "time" + + "github.com/centrifugal/protocol" + "github.com/gorilla/websocket" +) + +func extractDisconnectWebsocket(err error) *disconnect { + if err != nil { + if closeErr, ok := err.(*websocket.CloseError); ok { + var d disconnect + err := json.Unmarshal([]byte(closeErr.Text), &d) + if err == nil { + return &d + } else { + code := uint32(closeErr.Code) + reason := closeErr.Text + reconnect := code < 3500 || code >= 5000 || (code >= 4000 && code < 4500) + if code < 3000 { + switch code { + case websocket.CloseMessageTooBig: + code = disconnectMessageSizeLimit + default: + // We expose codes defined by Centrifuge protocol, hiding + // details about transport-specific error codes. We may have extra + // optional transportCode field in the future. + code = connectingTransportClosed + } + } + return &disconnect{ + Code: code, + Reason: reason, + Reconnect: reconnect, + } + } + } + } + return nil +} + +type websocketTransport struct { + mu sync.Mutex + conn *websocket.Conn + protocolType protocol.Type + commandEncoder protocol.CommandEncoder + replyCh chan *protocol.Reply + config websocketConfig + disconnect *disconnect + closed bool + closeCh chan struct{} +} + +// websocketConfig configures Websocket transport. +type websocketConfig struct { + // Proxy specifies a function to return a proxy for a given Request. + // If the function returns a non-nil error, the request is aborted with the + // provided error. If function returns a nil *URL, no proxy is used. + // If Proxy is nil then http.ProxyFromEnvironment will be used. + Proxy func(*http.Request) (*url.URL, error) + + // NetDialContext specifies the dial function for creating TCP connections. If + // NetDialContext is nil, net.DialContext is used. + NetDialContext func(ctx context.Context, network, addr string) (net.Conn, error) + + // TLSConfig specifies the TLS configuration to use with tls.Client. + // If nil, the default configuration is used. + TLSConfig *tls.Config + + // HandshakeTimeout specifies the duration for the handshake to complete. + HandshakeTimeout time.Duration + + // EnableCompression specifies if the client should attempt to negotiate + // per message compression (RFC 7692). Setting this value to true does not + // guarantee that compression will be supported. Currently only "no context + // takeover" modes are supported. + EnableCompression bool + + // CookieJar specifies the cookie jar. + // If CookieJar is nil, cookies are not sent in requests and ignored + // in responses. + CookieJar http.CookieJar + + // Header specifies custom HTTP Header to send. + Header http.Header +} + +func newWebsocketTransport(url string, protocolType protocol.Type, config websocketConfig) (transport, error) { + wsHeaders := config.Header + + dialer := &websocket.Dialer{} + if config.Proxy != nil { + dialer.Proxy = config.Proxy + } else { + dialer.Proxy = http.ProxyFromEnvironment + } + dialer.NetDialContext = config.NetDialContext + + dialer.HandshakeTimeout = config.HandshakeTimeout + dialer.EnableCompression = config.EnableCompression + dialer.TLSClientConfig = config.TLSConfig + dialer.Jar = config.CookieJar + + if protocolType == protocol.TypeProtobuf { + dialer.Subprotocols = []string{"centrifuge-protobuf"} + } + + conn, resp, err := dialer.Dial(url, wsHeaders) + if err != nil { + return nil, fmt.Errorf("error dial: %v", err) + } + if resp.StatusCode != http.StatusSwitchingProtocols { + return nil, fmt.Errorf("wrong status code while connecting to server: %d", resp.StatusCode) + } + + t := &websocketTransport{ + conn: conn, + replyCh: make(chan *protocol.Reply), + config: config, + closeCh: make(chan struct{}), + commandEncoder: newCommandEncoder(protocolType), + protocolType: protocolType, + } + go t.reader() + return t, nil +} + +func (t *websocketTransport) Close() error { + t.mu.Lock() + defer t.mu.Unlock() + if t.closed { + return nil + } + t.closed = true + close(t.closeCh) + _ = t.conn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""), time.Now().Add(time.Second)) + return t.conn.Close() +} + +func (t *websocketTransport) reader() { + defer func() { _ = t.Close() }() + defer close(t.replyCh) + + for { + _, data, err := t.conn.ReadMessage() + if err != nil { + disconnect := extractDisconnectWebsocket(err) + t.disconnect = disconnect + return + } + //println("<----", strings.Trim(string(data), "\n")) + loop: + for { + decoder := newReplyDecoder(t.protocolType, data) + for { + reply, err := decoder.Decode() + if err != nil { + if err == io.EOF { + break loop + } + t.disconnect = &disconnect{Code: disconnectBadProtocol, Reason: "decode error", Reconnect: false} + return + } + select { + case <-t.closeCh: + return + case t.replyCh <- reply: + // Send is blocking here, but slow client will be disconnected + // eventually with `no ping` reason – so we will exit from this + // goroutine. + } + } + } + } +} + +func (t *websocketTransport) Write(cmd *protocol.Command, timeout time.Duration) error { + data, err := t.commandEncoder.Encode(cmd) + if err != nil { + return err + } + return t.writeData(data, timeout) +} + +func (t *websocketTransport) writeData(data []byte, timeout time.Duration) error { + t.mu.Lock() + defer t.mu.Unlock() + if timeout > 0 { + _ = t.conn.SetWriteDeadline(time.Now().Add(timeout)) + } + //println("---->", strings.Trim(string(data), "\n")) + var err error + if t.protocolType == protocol.TypeJSON { + err = t.conn.WriteMessage(websocket.TextMessage, data) + } else { + err = t.conn.WriteMessage(websocket.BinaryMessage, data) + } + if timeout > 0 { + _ = t.conn.SetWriteDeadline(time.Time{}) + } + return err +} + +func (t *websocketTransport) Read() (*protocol.Reply, *disconnect, error) { + reply, ok := <-t.replyCh + if !ok { + return nil, t.disconnect, io.EOF + } + return reply, nil, nil +} diff --git a/vendor/github.com/centrifugal/protocol/.gitignore b/vendor/github.com/centrifugal/protocol/.gitignore new file mode 100644 index 0000000000..178b098c53 --- /dev/null +++ b/vendor/github.com/centrifugal/protocol/.gitignore @@ -0,0 +1,5 @@ +.vscode/ +.idea/ +vendor/ +build/ +*.orig diff --git a/vendor/github.com/centrifugal/protocol/LICENSE b/vendor/github.com/centrifugal/protocol/LICENSE new file mode 100644 index 0000000000..624d2f8f73 --- /dev/null +++ b/vendor/github.com/centrifugal/protocol/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Centrifugal Labs LTD + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/centrifugal/protocol/Makefile b/vendor/github.com/centrifugal/protocol/Makefile new file mode 100644 index 0000000000..c6a6091d8d --- /dev/null +++ b/vendor/github.com/centrifugal/protocol/Makefile @@ -0,0 +1,7 @@ +all: generate + +generate: + bash generate.sh + +test: + go test -v ./... diff --git a/vendor/github.com/centrifugal/protocol/bpool.go b/vendor/github.com/centrifugal/protocol/bpool.go new file mode 100644 index 0000000000..3578944b74 --- /dev/null +++ b/vendor/github.com/centrifugal/protocol/bpool.go @@ -0,0 +1,81 @@ +package protocol + +import ( + "io" + "math/bits" + "sync" +) + +var ( + // Verify ByteBuffer implements io.Writer. + _ io.Writer = &ByteBuffer{} +) + +// ByteBuffer implements a simple byte buffer. +type ByteBuffer struct { + // B is the underlying byte slice. + B []byte +} + +// Reset resets bb. +func (bb *ByteBuffer) Reset() { + bb.B = bb.B[:0] +} + +// Write appends p to bb. +func (bb *ByteBuffer) Write(p []byte) (int, error) { + bb.B = append(bb.B, p...) + return len(p), nil +} + +// pools contain pools for byte slices of various capacities. +var pools [19]sync.Pool + +// maxBufferLength is the maximum length of an element that can be added to the Pool. +const maxBufferLength = 262144 // 2^18 + +// Log of base two, round up (for v > 0). +func nextLogBase2(v uint32) uint32 { + return uint32(bits.Len32(v - 1)) +} + +// Log of base two, round down (for v > 0) +func prevLogBase2(num uint32) uint32 { + next := nextLogBase2(num) + if num == (1 << next) { + return next + } + return next - 1 +} + +// getByteBuffer returns byte buffer with the given capacity. +func getByteBuffer(length int) *ByteBuffer { + if length == 0 { + return &ByteBuffer{ + B: nil, + } + } + if length > maxBufferLength { + return &ByteBuffer{ + B: make([]byte, 0, length), + } + } + idx := nextLogBase2(uint32(length)) + if v := pools[idx].Get(); v != nil { + return v.(*ByteBuffer) + } + return &ByteBuffer{ + B: make([]byte, 0, 1< maxBufferLength { + return // drop. + } + idx := prevLogBase2(uint32(capacity)) + bb.Reset() + pools[idx].Put(bb) +} diff --git a/vendor/github.com/centrifugal/protocol/client.pb.go b/vendor/github.com/centrifugal/protocol/client.pb.go new file mode 100644 index 0000000000..39307bfbc0 --- /dev/null +++ b/vendor/github.com/centrifugal/protocol/client.pb.go @@ -0,0 +1,3811 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.26.1 +// source: client.proto + +package protocol + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Error struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + Temporary bool `protobuf:"varint,3,opt,name=temporary,proto3" json:"temporary,omitempty"` +} + +func (x *Error) Reset() { + *x = Error{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Error) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Error) ProtoMessage() {} + +func (x *Error) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Error.ProtoReflect.Descriptor instead. +func (*Error) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{0} +} + +func (x *Error) GetCode() uint32 { + if x != nil { + return x.Code + } + return 0 +} + +func (x *Error) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *Error) GetTemporary() bool { + if x != nil { + return x.Temporary + } + return false +} + +type EmulationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Node string `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` + Session string `protobuf:"bytes,2,opt,name=session,proto3" json:"session,omitempty"` + Data Raw `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *EmulationRequest) Reset() { + *x = EmulationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EmulationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EmulationRequest) ProtoMessage() {} + +func (x *EmulationRequest) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EmulationRequest.ProtoReflect.Descriptor instead. +func (*EmulationRequest) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{1} +} + +func (x *EmulationRequest) GetNode() string { + if x != nil { + return x.Node + } + return "" +} + +func (x *EmulationRequest) GetSession() string { + if x != nil { + return x.Session + } + return "" +} + +func (x *EmulationRequest) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +// Command sent from a client to a server. +// ProtocolVersion2 uses id and one of the possible request messages. +type Command struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Id of command to let client match replies to commands. + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + // ProtocolVersion2 client can send one of the following requests. Server will + // only take the first non-null request out of these and may return an error if + // client passed more than one request. We are not using oneof here due to JSON + // interoperability concerns. + Connect *ConnectRequest `protobuf:"bytes,4,opt,name=connect,proto3" json:"connect,omitempty"` + Subscribe *SubscribeRequest `protobuf:"bytes,5,opt,name=subscribe,proto3" json:"subscribe,omitempty"` + Unsubscribe *UnsubscribeRequest `protobuf:"bytes,6,opt,name=unsubscribe,proto3" json:"unsubscribe,omitempty"` + Publish *PublishRequest `protobuf:"bytes,7,opt,name=publish,proto3" json:"publish,omitempty"` + Presence *PresenceRequest `protobuf:"bytes,8,opt,name=presence,proto3" json:"presence,omitempty"` + PresenceStats *PresenceStatsRequest `protobuf:"bytes,9,opt,name=presence_stats,json=presenceStats,proto3" json:"presence_stats,omitempty"` + History *HistoryRequest `protobuf:"bytes,10,opt,name=history,proto3" json:"history,omitempty"` + Ping *PingRequest `protobuf:"bytes,11,opt,name=ping,proto3" json:"ping,omitempty"` + Send *SendRequest `protobuf:"bytes,12,opt,name=send,proto3" json:"send,omitempty"` + Rpc *RPCRequest `protobuf:"bytes,13,opt,name=rpc,proto3" json:"rpc,omitempty"` + Refresh *RefreshRequest `protobuf:"bytes,14,opt,name=refresh,proto3" json:"refresh,omitempty"` + SubRefresh *SubRefreshRequest `protobuf:"bytes,15,opt,name=sub_refresh,json=subRefresh,proto3" json:"sub_refresh,omitempty"` +} + +func (x *Command) Reset() { + *x = Command{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Command) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Command) ProtoMessage() {} + +func (x *Command) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Command.ProtoReflect.Descriptor instead. +func (*Command) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{2} +} + +func (x *Command) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Command) GetConnect() *ConnectRequest { + if x != nil { + return x.Connect + } + return nil +} + +func (x *Command) GetSubscribe() *SubscribeRequest { + if x != nil { + return x.Subscribe + } + return nil +} + +func (x *Command) GetUnsubscribe() *UnsubscribeRequest { + if x != nil { + return x.Unsubscribe + } + return nil +} + +func (x *Command) GetPublish() *PublishRequest { + if x != nil { + return x.Publish + } + return nil +} + +func (x *Command) GetPresence() *PresenceRequest { + if x != nil { + return x.Presence + } + return nil +} + +func (x *Command) GetPresenceStats() *PresenceStatsRequest { + if x != nil { + return x.PresenceStats + } + return nil +} + +func (x *Command) GetHistory() *HistoryRequest { + if x != nil { + return x.History + } + return nil +} + +func (x *Command) GetPing() *PingRequest { + if x != nil { + return x.Ping + } + return nil +} + +func (x *Command) GetSend() *SendRequest { + if x != nil { + return x.Send + } + return nil +} + +func (x *Command) GetRpc() *RPCRequest { + if x != nil { + return x.Rpc + } + return nil +} + +func (x *Command) GetRefresh() *RefreshRequest { + if x != nil { + return x.Refresh + } + return nil +} + +func (x *Command) GetSubRefresh() *SubRefreshRequest { + if x != nil { + return x.SubRefresh + } + return nil +} + +// Reply sent from a server to a client. +// ProtocolVersion2 uses id and one of the possible concrete result messages. +type Reply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Id will only be set to a value > 0 for replies to commands. For pushes + // it will have zero value. + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + // Error can only be set in replies to commands. For pushes it will have zero value. + Error *Error `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` + // ProtocolVersion2 server can send one of the following fields. We are not using + // oneof here due to JSON interoperability concerns. + Push *Push `protobuf:"bytes,4,opt,name=push,proto3" json:"push,omitempty"` + Connect *ConnectResult `protobuf:"bytes,5,opt,name=connect,proto3" json:"connect,omitempty"` + Subscribe *SubscribeResult `protobuf:"bytes,6,opt,name=subscribe,proto3" json:"subscribe,omitempty"` + Unsubscribe *UnsubscribeResult `protobuf:"bytes,7,opt,name=unsubscribe,proto3" json:"unsubscribe,omitempty"` + Publish *PublishResult `protobuf:"bytes,8,opt,name=publish,proto3" json:"publish,omitempty"` + Presence *PresenceResult `protobuf:"bytes,9,opt,name=presence,proto3" json:"presence,omitempty"` + PresenceStats *PresenceStatsResult `protobuf:"bytes,10,opt,name=presence_stats,json=presenceStats,proto3" json:"presence_stats,omitempty"` + History *HistoryResult `protobuf:"bytes,11,opt,name=history,proto3" json:"history,omitempty"` + Ping *PingResult `protobuf:"bytes,12,opt,name=ping,proto3" json:"ping,omitempty"` + Rpc *RPCResult `protobuf:"bytes,13,opt,name=rpc,proto3" json:"rpc,omitempty"` + Refresh *RefreshResult `protobuf:"bytes,14,opt,name=refresh,proto3" json:"refresh,omitempty"` + SubRefresh *SubRefreshResult `protobuf:"bytes,15,opt,name=sub_refresh,json=subRefresh,proto3" json:"sub_refresh,omitempty"` +} + +func (x *Reply) Reset() { + *x = Reply{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Reply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Reply) ProtoMessage() {} + +func (x *Reply) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Reply.ProtoReflect.Descriptor instead. +func (*Reply) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{3} +} + +func (x *Reply) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Reply) GetError() *Error { + if x != nil { + return x.Error + } + return nil +} + +func (x *Reply) GetPush() *Push { + if x != nil { + return x.Push + } + return nil +} + +func (x *Reply) GetConnect() *ConnectResult { + if x != nil { + return x.Connect + } + return nil +} + +func (x *Reply) GetSubscribe() *SubscribeResult { + if x != nil { + return x.Subscribe + } + return nil +} + +func (x *Reply) GetUnsubscribe() *UnsubscribeResult { + if x != nil { + return x.Unsubscribe + } + return nil +} + +func (x *Reply) GetPublish() *PublishResult { + if x != nil { + return x.Publish + } + return nil +} + +func (x *Reply) GetPresence() *PresenceResult { + if x != nil { + return x.Presence + } + return nil +} + +func (x *Reply) GetPresenceStats() *PresenceStatsResult { + if x != nil { + return x.PresenceStats + } + return nil +} + +func (x *Reply) GetHistory() *HistoryResult { + if x != nil { + return x.History + } + return nil +} + +func (x *Reply) GetPing() *PingResult { + if x != nil { + return x.Ping + } + return nil +} + +func (x *Reply) GetRpc() *RPCResult { + if x != nil { + return x.Rpc + } + return nil +} + +func (x *Reply) GetRefresh() *RefreshResult { + if x != nil { + return x.Refresh + } + return nil +} + +func (x *Reply) GetSubRefresh() *SubRefreshResult { + if x != nil { + return x.SubRefresh + } + return nil +} + +// Push can be sent to a client as part of Reply in case of bidirectional transport or +// without additional wrapping in case of unidirectional transports. +// ProtocolVersion2 uses channel and one of the possible concrete push messages. +type Push struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Channel string `protobuf:"bytes,2,opt,name=channel,proto3" json:"channel,omitempty"` + // ProtocolVersion2 server can push one of the following fields to the client. We are + // not using oneof here due to JSON interoperability concerns. + Pub *Publication `protobuf:"bytes,4,opt,name=pub,proto3" json:"pub,omitempty"` + Join *Join `protobuf:"bytes,5,opt,name=join,proto3" json:"join,omitempty"` + Leave *Leave `protobuf:"bytes,6,opt,name=leave,proto3" json:"leave,omitempty"` + Unsubscribe *Unsubscribe `protobuf:"bytes,7,opt,name=unsubscribe,proto3" json:"unsubscribe,omitempty"` + Message *Message `protobuf:"bytes,8,opt,name=message,proto3" json:"message,omitempty"` + Subscribe *Subscribe `protobuf:"bytes,9,opt,name=subscribe,proto3" json:"subscribe,omitempty"` + Connect *Connect `protobuf:"bytes,10,opt,name=connect,proto3" json:"connect,omitempty"` + Disconnect *Disconnect `protobuf:"bytes,11,opt,name=disconnect,proto3" json:"disconnect,omitempty"` + Refresh *Refresh `protobuf:"bytes,12,opt,name=refresh,proto3" json:"refresh,omitempty"` +} + +func (x *Push) Reset() { + *x = Push{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Push) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Push) ProtoMessage() {} + +func (x *Push) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Push.ProtoReflect.Descriptor instead. +func (*Push) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{4} +} + +func (x *Push) GetChannel() string { + if x != nil { + return x.Channel + } + return "" +} + +func (x *Push) GetPub() *Publication { + if x != nil { + return x.Pub + } + return nil +} + +func (x *Push) GetJoin() *Join { + if x != nil { + return x.Join + } + return nil +} + +func (x *Push) GetLeave() *Leave { + if x != nil { + return x.Leave + } + return nil +} + +func (x *Push) GetUnsubscribe() *Unsubscribe { + if x != nil { + return x.Unsubscribe + } + return nil +} + +func (x *Push) GetMessage() *Message { + if x != nil { + return x.Message + } + return nil +} + +func (x *Push) GetSubscribe() *Subscribe { + if x != nil { + return x.Subscribe + } + return nil +} + +func (x *Push) GetConnect() *Connect { + if x != nil { + return x.Connect + } + return nil +} + +func (x *Push) GetDisconnect() *Disconnect { + if x != nil { + return x.Disconnect + } + return nil +} + +func (x *Push) GetRefresh() *Refresh { + if x != nil { + return x.Refresh + } + return nil +} + +type ClientInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + User string `protobuf:"bytes,1,opt,name=user,proto3" json:"user"` + Client string `protobuf:"bytes,2,opt,name=client,proto3" json:"client"` + ConnInfo Raw `protobuf:"bytes,3,opt,name=conn_info,json=connInfo,proto3" json:"conn_info,omitempty"` + ChanInfo Raw `protobuf:"bytes,4,opt,name=chan_info,json=chanInfo,proto3" json:"chan_info,omitempty"` +} + +func (x *ClientInfo) Reset() { + *x = ClientInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClientInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClientInfo) ProtoMessage() {} + +func (x *ClientInfo) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClientInfo.ProtoReflect.Descriptor instead. +func (*ClientInfo) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{5} +} + +func (x *ClientInfo) GetUser() string { + if x != nil { + return x.User + } + return "" +} + +func (x *ClientInfo) GetClient() string { + if x != nil { + return x.Client + } + return "" +} + +func (x *ClientInfo) GetConnInfo() []byte { + if x != nil { + return x.ConnInfo + } + return nil +} + +func (x *ClientInfo) GetChanInfo() []byte { + if x != nil { + return x.ChanInfo + } + return nil +} + +type Publication struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Data Raw `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` + Info *ClientInfo `protobuf:"bytes,5,opt,name=info,proto3" json:"info,omitempty"` + Offset uint64 `protobuf:"varint,6,opt,name=offset,proto3" json:"offset,omitempty"` + Tags map[string]string `protobuf:"bytes,7,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Delta bool `protobuf:"varint,8,opt,name=delta,proto3" json:"delta,omitempty"` // When set indicates that data in Publication is a delta from previous data. + Time int64 `protobuf:"varint,9,opt,name=time,proto3" json:"time,omitempty"` // Optional time of publication as Unix timestamp milliseconds. + Channel string `protobuf:"bytes,10,opt,name=channel,proto3" json:"channel,omitempty"` // Optional channel name if Publication relates to wildcard subscription. +} + +func (x *Publication) Reset() { + *x = Publication{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Publication) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Publication) ProtoMessage() {} + +func (x *Publication) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Publication.ProtoReflect.Descriptor instead. +func (*Publication) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{6} +} + +func (x *Publication) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *Publication) GetInfo() *ClientInfo { + if x != nil { + return x.Info + } + return nil +} + +func (x *Publication) GetOffset() uint64 { + if x != nil { + return x.Offset + } + return 0 +} + +func (x *Publication) GetTags() map[string]string { + if x != nil { + return x.Tags + } + return nil +} + +func (x *Publication) GetDelta() bool { + if x != nil { + return x.Delta + } + return false +} + +func (x *Publication) GetTime() int64 { + if x != nil { + return x.Time + } + return 0 +} + +func (x *Publication) GetChannel() string { + if x != nil { + return x.Channel + } + return "" +} + +type Join struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Info *ClientInfo `protobuf:"bytes,1,opt,name=info,proto3" json:"info,omitempty"` +} + +func (x *Join) Reset() { + *x = Join{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Join) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Join) ProtoMessage() {} + +func (x *Join) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Join.ProtoReflect.Descriptor instead. +func (*Join) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{7} +} + +func (x *Join) GetInfo() *ClientInfo { + if x != nil { + return x.Info + } + return nil +} + +type Leave struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Info *ClientInfo `protobuf:"bytes,1,opt,name=info,proto3" json:"info,omitempty"` +} + +func (x *Leave) Reset() { + *x = Leave{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Leave) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Leave) ProtoMessage() {} + +func (x *Leave) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Leave.ProtoReflect.Descriptor instead. +func (*Leave) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{8} +} + +func (x *Leave) GetInfo() *ClientInfo { + if x != nil { + return x.Info + } + return nil +} + +type Unsubscribe struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Code uint32 `protobuf:"varint,2,opt,name=code,proto3" json:"code,omitempty"` + Reason string `protobuf:"bytes,3,opt,name=reason,proto3" json:"reason,omitempty"` +} + +func (x *Unsubscribe) Reset() { + *x = Unsubscribe{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Unsubscribe) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Unsubscribe) ProtoMessage() {} + +func (x *Unsubscribe) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Unsubscribe.ProtoReflect.Descriptor instead. +func (*Unsubscribe) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{9} +} + +func (x *Unsubscribe) GetCode() uint32 { + if x != nil { + return x.Code + } + return 0 +} + +func (x *Unsubscribe) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +type Subscribe struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Recoverable bool `protobuf:"varint,1,opt,name=recoverable,proto3" json:"recoverable,omitempty"` + Epoch string `protobuf:"bytes,4,opt,name=epoch,proto3" json:"epoch,omitempty"` + Offset uint64 `protobuf:"varint,5,opt,name=offset,proto3" json:"offset,omitempty"` + Positioned bool `protobuf:"varint,6,opt,name=positioned,proto3" json:"positioned,omitempty"` + Data Raw `protobuf:"bytes,7,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *Subscribe) Reset() { + *x = Subscribe{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Subscribe) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Subscribe) ProtoMessage() {} + +func (x *Subscribe) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Subscribe.ProtoReflect.Descriptor instead. +func (*Subscribe) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{10} +} + +func (x *Subscribe) GetRecoverable() bool { + if x != nil { + return x.Recoverable + } + return false +} + +func (x *Subscribe) GetEpoch() string { + if x != nil { + return x.Epoch + } + return "" +} + +func (x *Subscribe) GetOffset() uint64 { + if x != nil { + return x.Offset + } + return 0 +} + +func (x *Subscribe) GetPositioned() bool { + if x != nil { + return x.Positioned + } + return false +} + +func (x *Subscribe) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +type Message struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Data Raw `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *Message) Reset() { + *x = Message{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Message) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Message) ProtoMessage() {} + +func (x *Message) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Message.ProtoReflect.Descriptor instead. +func (*Message) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{11} +} + +func (x *Message) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +type Connect struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Client string `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"` + Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` + Data Raw `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + Subs map[string]*SubscribeResult `protobuf:"bytes,4,rep,name=subs,proto3" json:"subs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Expires bool `protobuf:"varint,5,opt,name=expires,proto3" json:"expires,omitempty"` + Ttl uint32 `protobuf:"varint,6,opt,name=ttl,proto3" json:"ttl,omitempty"` + Ping uint32 `protobuf:"varint,7,opt,name=ping,proto3" json:"ping,omitempty"` + Pong bool `protobuf:"varint,8,opt,name=pong,proto3" json:"pong,omitempty"` + Session string `protobuf:"bytes,9,opt,name=session,proto3" json:"session,omitempty"` + Node string `protobuf:"bytes,10,opt,name=node,proto3" json:"node,omitempty"` + Time int64 `protobuf:"varint,11,opt,name=time,proto3" json:"time,omitempty"` // Server time as Unix timestamp in milliseconds (not sent by default). +} + +func (x *Connect) Reset() { + *x = Connect{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Connect) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Connect) ProtoMessage() {} + +func (x *Connect) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Connect.ProtoReflect.Descriptor instead. +func (*Connect) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{12} +} + +func (x *Connect) GetClient() string { + if x != nil { + return x.Client + } + return "" +} + +func (x *Connect) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *Connect) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *Connect) GetSubs() map[string]*SubscribeResult { + if x != nil { + return x.Subs + } + return nil +} + +func (x *Connect) GetExpires() bool { + if x != nil { + return x.Expires + } + return false +} + +func (x *Connect) GetTtl() uint32 { + if x != nil { + return x.Ttl + } + return 0 +} + +func (x *Connect) GetPing() uint32 { + if x != nil { + return x.Ping + } + return 0 +} + +func (x *Connect) GetPong() bool { + if x != nil { + return x.Pong + } + return false +} + +func (x *Connect) GetSession() string { + if x != nil { + return x.Session + } + return "" +} + +func (x *Connect) GetNode() string { + if x != nil { + return x.Node + } + return "" +} + +func (x *Connect) GetTime() int64 { + if x != nil { + return x.Time + } + return 0 +} + +type Disconnect struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` + Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"` + Reconnect bool `protobuf:"varint,3,opt,name=reconnect,proto3" json:"reconnect,omitempty"` +} + +func (x *Disconnect) Reset() { + *x = Disconnect{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Disconnect) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Disconnect) ProtoMessage() {} + +func (x *Disconnect) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Disconnect.ProtoReflect.Descriptor instead. +func (*Disconnect) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{13} +} + +func (x *Disconnect) GetCode() uint32 { + if x != nil { + return x.Code + } + return 0 +} + +func (x *Disconnect) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +func (x *Disconnect) GetReconnect() bool { + if x != nil { + return x.Reconnect + } + return false +} + +type Refresh struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Expires bool `protobuf:"varint,1,opt,name=expires,proto3" json:"expires,omitempty"` + Ttl uint32 `protobuf:"varint,2,opt,name=ttl,proto3" json:"ttl,omitempty"` +} + +func (x *Refresh) Reset() { + *x = Refresh{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Refresh) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Refresh) ProtoMessage() {} + +func (x *Refresh) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Refresh.ProtoReflect.Descriptor instead. +func (*Refresh) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{14} +} + +func (x *Refresh) GetExpires() bool { + if x != nil { + return x.Expires + } + return false +} + +func (x *Refresh) GetTtl() uint32 { + if x != nil { + return x.Ttl + } + return 0 +} + +type ConnectRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` + Data Raw `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + Subs map[string]*SubscribeRequest `protobuf:"bytes,3,rep,name=subs,proto3" json:"subs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` + Version string `protobuf:"bytes,5,opt,name=version,proto3" json:"version,omitempty"` + Headers map[string]string `protobuf:"bytes,6,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *ConnectRequest) Reset() { + *x = ConnectRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConnectRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectRequest) ProtoMessage() {} + +func (x *ConnectRequest) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConnectRequest.ProtoReflect.Descriptor instead. +func (*ConnectRequest) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{15} +} + +func (x *ConnectRequest) GetToken() string { + if x != nil { + return x.Token + } + return "" +} + +func (x *ConnectRequest) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *ConnectRequest) GetSubs() map[string]*SubscribeRequest { + if x != nil { + return x.Subs + } + return nil +} + +func (x *ConnectRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ConnectRequest) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *ConnectRequest) GetHeaders() map[string]string { + if x != nil { + return x.Headers + } + return nil +} + +type ConnectResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Client string `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"` + Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` + Expires bool `protobuf:"varint,3,opt,name=expires,proto3" json:"expires,omitempty"` + Ttl uint32 `protobuf:"varint,4,opt,name=ttl,proto3" json:"ttl,omitempty"` + Data Raw `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` + Subs map[string]*SubscribeResult `protobuf:"bytes,6,rep,name=subs,proto3" json:"subs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Ping uint32 `protobuf:"varint,7,opt,name=ping,proto3" json:"ping,omitempty"` + Pong bool `protobuf:"varint,8,opt,name=pong,proto3" json:"pong,omitempty"` + Session string `protobuf:"bytes,9,opt,name=session,proto3" json:"session,omitempty"` + Node string `protobuf:"bytes,10,opt,name=node,proto3" json:"node,omitempty"` + Time int64 `protobuf:"varint,11,opt,name=time,proto3" json:"time,omitempty"` // Server time as Unix timestamp in milliseconds (not sent by default). +} + +func (x *ConnectResult) Reset() { + *x = ConnectResult{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConnectResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectResult) ProtoMessage() {} + +func (x *ConnectResult) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConnectResult.ProtoReflect.Descriptor instead. +func (*ConnectResult) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{16} +} + +func (x *ConnectResult) GetClient() string { + if x != nil { + return x.Client + } + return "" +} + +func (x *ConnectResult) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *ConnectResult) GetExpires() bool { + if x != nil { + return x.Expires + } + return false +} + +func (x *ConnectResult) GetTtl() uint32 { + if x != nil { + return x.Ttl + } + return 0 +} + +func (x *ConnectResult) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *ConnectResult) GetSubs() map[string]*SubscribeResult { + if x != nil { + return x.Subs + } + return nil +} + +func (x *ConnectResult) GetPing() uint32 { + if x != nil { + return x.Ping + } + return 0 +} + +func (x *ConnectResult) GetPong() bool { + if x != nil { + return x.Pong + } + return false +} + +func (x *ConnectResult) GetSession() string { + if x != nil { + return x.Session + } + return "" +} + +func (x *ConnectResult) GetNode() string { + if x != nil { + return x.Node + } + return "" +} + +func (x *ConnectResult) GetTime() int64 { + if x != nil { + return x.Time + } + return 0 +} + +type RefreshRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` +} + +func (x *RefreshRequest) Reset() { + *x = RefreshRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RefreshRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RefreshRequest) ProtoMessage() {} + +func (x *RefreshRequest) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RefreshRequest.ProtoReflect.Descriptor instead. +func (*RefreshRequest) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{17} +} + +func (x *RefreshRequest) GetToken() string { + if x != nil { + return x.Token + } + return "" +} + +type RefreshResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Client string `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"` + Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` + Expires bool `protobuf:"varint,3,opt,name=expires,proto3" json:"expires,omitempty"` + Ttl uint32 `protobuf:"varint,4,opt,name=ttl,proto3" json:"ttl,omitempty"` +} + +func (x *RefreshResult) Reset() { + *x = RefreshResult{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RefreshResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RefreshResult) ProtoMessage() {} + +func (x *RefreshResult) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RefreshResult.ProtoReflect.Descriptor instead. +func (*RefreshResult) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{18} +} + +func (x *RefreshResult) GetClient() string { + if x != nil { + return x.Client + } + return "" +} + +func (x *RefreshResult) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *RefreshResult) GetExpires() bool { + if x != nil { + return x.Expires + } + return false +} + +func (x *RefreshResult) GetTtl() uint32 { + if x != nil { + return x.Ttl + } + return 0 +} + +type SubscribeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Channel string `protobuf:"bytes,1,opt,name=channel,proto3" json:"channel,omitempty"` + Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` + Recover bool `protobuf:"varint,3,opt,name=recover,proto3" json:"recover,omitempty"` + Epoch string `protobuf:"bytes,6,opt,name=epoch,proto3" json:"epoch,omitempty"` + Offset uint64 `protobuf:"varint,7,opt,name=offset,proto3" json:"offset,omitempty"` + Data Raw `protobuf:"bytes,8,opt,name=data,proto3" json:"data,omitempty"` + Positioned bool `protobuf:"varint,9,opt,name=positioned,proto3" json:"positioned,omitempty"` + Recoverable bool `protobuf:"varint,10,opt,name=recoverable,proto3" json:"recoverable,omitempty"` + JoinLeave bool `protobuf:"varint,11,opt,name=join_leave,json=joinLeave,proto3" json:"join_leave,omitempty"` + Delta string `protobuf:"bytes,12,opt,name=delta,proto3" json:"delta,omitempty"` +} + +func (x *SubscribeRequest) Reset() { + *x = SubscribeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubscribeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubscribeRequest) ProtoMessage() {} + +func (x *SubscribeRequest) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubscribeRequest.ProtoReflect.Descriptor instead. +func (*SubscribeRequest) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{19} +} + +func (x *SubscribeRequest) GetChannel() string { + if x != nil { + return x.Channel + } + return "" +} + +func (x *SubscribeRequest) GetToken() string { + if x != nil { + return x.Token + } + return "" +} + +func (x *SubscribeRequest) GetRecover() bool { + if x != nil { + return x.Recover + } + return false +} + +func (x *SubscribeRequest) GetEpoch() string { + if x != nil { + return x.Epoch + } + return "" +} + +func (x *SubscribeRequest) GetOffset() uint64 { + if x != nil { + return x.Offset + } + return 0 +} + +func (x *SubscribeRequest) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *SubscribeRequest) GetPositioned() bool { + if x != nil { + return x.Positioned + } + return false +} + +func (x *SubscribeRequest) GetRecoverable() bool { + if x != nil { + return x.Recoverable + } + return false +} + +func (x *SubscribeRequest) GetJoinLeave() bool { + if x != nil { + return x.JoinLeave + } + return false +} + +func (x *SubscribeRequest) GetDelta() string { + if x != nil { + return x.Delta + } + return "" +} + +type SubscribeResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Expires bool `protobuf:"varint,1,opt,name=expires,proto3" json:"expires,omitempty"` + Ttl uint32 `protobuf:"varint,2,opt,name=ttl,proto3" json:"ttl,omitempty"` + Recoverable bool `protobuf:"varint,3,opt,name=recoverable,proto3" json:"recoverable,omitempty"` + Epoch string `protobuf:"bytes,6,opt,name=epoch,proto3" json:"epoch,omitempty"` + Publications []*Publication `protobuf:"bytes,7,rep,name=publications,proto3" json:"publications,omitempty"` + Recovered bool `protobuf:"varint,8,opt,name=recovered,proto3" json:"recovered,omitempty"` + Offset uint64 `protobuf:"varint,9,opt,name=offset,proto3" json:"offset,omitempty"` + Positioned bool `protobuf:"varint,10,opt,name=positioned,proto3" json:"positioned,omitempty"` + Data Raw `protobuf:"bytes,11,opt,name=data,proto3" json:"data,omitempty"` + WasRecovering bool `protobuf:"varint,12,opt,name=was_recovering,json=wasRecovering,proto3" json:"was_recovering,omitempty"` + Delta bool `protobuf:"varint,13,opt,name=delta,proto3" json:"delta,omitempty"` +} + +func (x *SubscribeResult) Reset() { + *x = SubscribeResult{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubscribeResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubscribeResult) ProtoMessage() {} + +func (x *SubscribeResult) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubscribeResult.ProtoReflect.Descriptor instead. +func (*SubscribeResult) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{20} +} + +func (x *SubscribeResult) GetExpires() bool { + if x != nil { + return x.Expires + } + return false +} + +func (x *SubscribeResult) GetTtl() uint32 { + if x != nil { + return x.Ttl + } + return 0 +} + +func (x *SubscribeResult) GetRecoverable() bool { + if x != nil { + return x.Recoverable + } + return false +} + +func (x *SubscribeResult) GetEpoch() string { + if x != nil { + return x.Epoch + } + return "" +} + +func (x *SubscribeResult) GetPublications() []*Publication { + if x != nil { + return x.Publications + } + return nil +} + +func (x *SubscribeResult) GetRecovered() bool { + if x != nil { + return x.Recovered + } + return false +} + +func (x *SubscribeResult) GetOffset() uint64 { + if x != nil { + return x.Offset + } + return 0 +} + +func (x *SubscribeResult) GetPositioned() bool { + if x != nil { + return x.Positioned + } + return false +} + +func (x *SubscribeResult) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *SubscribeResult) GetWasRecovering() bool { + if x != nil { + return x.WasRecovering + } + return false +} + +func (x *SubscribeResult) GetDelta() bool { + if x != nil { + return x.Delta + } + return false +} + +type SubRefreshRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Channel string `protobuf:"bytes,1,opt,name=channel,proto3" json:"channel,omitempty"` + Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` +} + +func (x *SubRefreshRequest) Reset() { + *x = SubRefreshRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubRefreshRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubRefreshRequest) ProtoMessage() {} + +func (x *SubRefreshRequest) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubRefreshRequest.ProtoReflect.Descriptor instead. +func (*SubRefreshRequest) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{21} +} + +func (x *SubRefreshRequest) GetChannel() string { + if x != nil { + return x.Channel + } + return "" +} + +func (x *SubRefreshRequest) GetToken() string { + if x != nil { + return x.Token + } + return "" +} + +type SubRefreshResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Expires bool `protobuf:"varint,1,opt,name=expires,proto3" json:"expires,omitempty"` + Ttl uint32 `protobuf:"varint,2,opt,name=ttl,proto3" json:"ttl,omitempty"` +} + +func (x *SubRefreshResult) Reset() { + *x = SubRefreshResult{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubRefreshResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubRefreshResult) ProtoMessage() {} + +func (x *SubRefreshResult) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubRefreshResult.ProtoReflect.Descriptor instead. +func (*SubRefreshResult) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{22} +} + +func (x *SubRefreshResult) GetExpires() bool { + if x != nil { + return x.Expires + } + return false +} + +func (x *SubRefreshResult) GetTtl() uint32 { + if x != nil { + return x.Ttl + } + return 0 +} + +type UnsubscribeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Channel string `protobuf:"bytes,1,opt,name=channel,proto3" json:"channel,omitempty"` +} + +func (x *UnsubscribeRequest) Reset() { + *x = UnsubscribeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UnsubscribeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnsubscribeRequest) ProtoMessage() {} + +func (x *UnsubscribeRequest) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UnsubscribeRequest.ProtoReflect.Descriptor instead. +func (*UnsubscribeRequest) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{23} +} + +func (x *UnsubscribeRequest) GetChannel() string { + if x != nil { + return x.Channel + } + return "" +} + +type UnsubscribeResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *UnsubscribeResult) Reset() { + *x = UnsubscribeResult{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UnsubscribeResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnsubscribeResult) ProtoMessage() {} + +func (x *UnsubscribeResult) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UnsubscribeResult.ProtoReflect.Descriptor instead. +func (*UnsubscribeResult) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{24} +} + +type PublishRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Channel string `protobuf:"bytes,1,opt,name=channel,proto3" json:"channel,omitempty"` + Data Raw `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *PublishRequest) Reset() { + *x = PublishRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PublishRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PublishRequest) ProtoMessage() {} + +func (x *PublishRequest) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PublishRequest.ProtoReflect.Descriptor instead. +func (*PublishRequest) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{25} +} + +func (x *PublishRequest) GetChannel() string { + if x != nil { + return x.Channel + } + return "" +} + +func (x *PublishRequest) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +type PublishResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *PublishResult) Reset() { + *x = PublishResult{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PublishResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PublishResult) ProtoMessage() {} + +func (x *PublishResult) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[26] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PublishResult.ProtoReflect.Descriptor instead. +func (*PublishResult) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{26} +} + +type PresenceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Channel string `protobuf:"bytes,1,opt,name=channel,proto3" json:"channel,omitempty"` +} + +func (x *PresenceRequest) Reset() { + *x = PresenceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PresenceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PresenceRequest) ProtoMessage() {} + +func (x *PresenceRequest) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[27] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PresenceRequest.ProtoReflect.Descriptor instead. +func (*PresenceRequest) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{27} +} + +func (x *PresenceRequest) GetChannel() string { + if x != nil { + return x.Channel + } + return "" +} + +type PresenceResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Presence map[string]*ClientInfo `protobuf:"bytes,1,rep,name=presence,proto3" json:"presence" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *PresenceResult) Reset() { + *x = PresenceResult{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PresenceResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PresenceResult) ProtoMessage() {} + +func (x *PresenceResult) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PresenceResult.ProtoReflect.Descriptor instead. +func (*PresenceResult) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{28} +} + +func (x *PresenceResult) GetPresence() map[string]*ClientInfo { + if x != nil { + return x.Presence + } + return nil +} + +type PresenceStatsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Channel string `protobuf:"bytes,1,opt,name=channel,proto3" json:"channel,omitempty"` +} + +func (x *PresenceStatsRequest) Reset() { + *x = PresenceStatsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PresenceStatsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PresenceStatsRequest) ProtoMessage() {} + +func (x *PresenceStatsRequest) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PresenceStatsRequest.ProtoReflect.Descriptor instead. +func (*PresenceStatsRequest) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{29} +} + +func (x *PresenceStatsRequest) GetChannel() string { + if x != nil { + return x.Channel + } + return "" +} + +type PresenceStatsResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NumClients uint32 `protobuf:"varint,1,opt,name=num_clients,json=numClients,proto3" json:"num_clients"` + NumUsers uint32 `protobuf:"varint,2,opt,name=num_users,json=numUsers,proto3" json:"num_users"` +} + +func (x *PresenceStatsResult) Reset() { + *x = PresenceStatsResult{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PresenceStatsResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PresenceStatsResult) ProtoMessage() {} + +func (x *PresenceStatsResult) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PresenceStatsResult.ProtoReflect.Descriptor instead. +func (*PresenceStatsResult) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{30} +} + +func (x *PresenceStatsResult) GetNumClients() uint32 { + if x != nil { + return x.NumClients + } + return 0 +} + +func (x *PresenceStatsResult) GetNumUsers() uint32 { + if x != nil { + return x.NumUsers + } + return 0 +} + +type StreamPosition struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Offset uint64 `protobuf:"varint,1,opt,name=offset,proto3" json:"offset,omitempty"` + Epoch string `protobuf:"bytes,2,opt,name=epoch,proto3" json:"epoch,omitempty"` +} + +func (x *StreamPosition) Reset() { + *x = StreamPosition{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StreamPosition) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamPosition) ProtoMessage() {} + +func (x *StreamPosition) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[31] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamPosition.ProtoReflect.Descriptor instead. +func (*StreamPosition) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{31} +} + +func (x *StreamPosition) GetOffset() uint64 { + if x != nil { + return x.Offset + } + return 0 +} + +func (x *StreamPosition) GetEpoch() string { + if x != nil { + return x.Epoch + } + return "" +} + +type HistoryRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Channel string `protobuf:"bytes,1,opt,name=channel,proto3" json:"channel,omitempty"` + Limit int32 `protobuf:"varint,7,opt,name=limit,proto3" json:"limit,omitempty"` + Since *StreamPosition `protobuf:"bytes,8,opt,name=since,proto3" json:"since,omitempty"` + Reverse bool `protobuf:"varint,9,opt,name=reverse,proto3" json:"reverse,omitempty"` +} + +func (x *HistoryRequest) Reset() { + *x = HistoryRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HistoryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HistoryRequest) ProtoMessage() {} + +func (x *HistoryRequest) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[32] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HistoryRequest.ProtoReflect.Descriptor instead. +func (*HistoryRequest) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{32} +} + +func (x *HistoryRequest) GetChannel() string { + if x != nil { + return x.Channel + } + return "" +} + +func (x *HistoryRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *HistoryRequest) GetSince() *StreamPosition { + if x != nil { + return x.Since + } + return nil +} + +func (x *HistoryRequest) GetReverse() bool { + if x != nil { + return x.Reverse + } + return false +} + +type HistoryResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Publications []*Publication `protobuf:"bytes,1,rep,name=publications,proto3" json:"publications"` + Epoch string `protobuf:"bytes,2,opt,name=epoch,proto3" json:"epoch"` + Offset uint64 `protobuf:"varint,3,opt,name=offset,proto3" json:"offset"` +} + +func (x *HistoryResult) Reset() { + *x = HistoryResult{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HistoryResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HistoryResult) ProtoMessage() {} + +func (x *HistoryResult) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[33] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HistoryResult.ProtoReflect.Descriptor instead. +func (*HistoryResult) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{33} +} + +func (x *HistoryResult) GetPublications() []*Publication { + if x != nil { + return x.Publications + } + return nil +} + +func (x *HistoryResult) GetEpoch() string { + if x != nil { + return x.Epoch + } + return "" +} + +func (x *HistoryResult) GetOffset() uint64 { + if x != nil { + return x.Offset + } + return 0 +} + +type PingRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *PingRequest) Reset() { + *x = PingRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PingRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PingRequest) ProtoMessage() {} + +func (x *PingRequest) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[34] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PingRequest.ProtoReflect.Descriptor instead. +func (*PingRequest) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{34} +} + +type PingResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *PingResult) Reset() { + *x = PingResult{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PingResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PingResult) ProtoMessage() {} + +func (x *PingResult) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[35] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PingResult.ProtoReflect.Descriptor instead. +func (*PingResult) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{35} +} + +type RPCRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Data Raw `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + Method string `protobuf:"bytes,2,opt,name=method,proto3" json:"method,omitempty"` +} + +func (x *RPCRequest) Reset() { + *x = RPCRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RPCRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RPCRequest) ProtoMessage() {} + +func (x *RPCRequest) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[36] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RPCRequest.ProtoReflect.Descriptor instead. +func (*RPCRequest) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{36} +} + +func (x *RPCRequest) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *RPCRequest) GetMethod() string { + if x != nil { + return x.Method + } + return "" +} + +type RPCResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Data Raw `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *RPCResult) Reset() { + *x = RPCResult{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RPCResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RPCResult) ProtoMessage() {} + +func (x *RPCResult) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[37] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RPCResult.ProtoReflect.Descriptor instead. +func (*RPCResult) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{37} +} + +func (x *RPCResult) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +type SendRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Data Raw `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *SendRequest) Reset() { + *x = SendRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SendRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendRequest) ProtoMessage() {} + +func (x *SendRequest) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[38] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendRequest.ProtoReflect.Descriptor instead. +func (*SendRequest) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{38} +} + +func (x *SendRequest) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +var File_client_proto protoreflect.FileDescriptor + +var file_client_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, + 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, + 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, + 0x53, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, + 0x61, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6f, + 0x72, 0x61, 0x72, 0x79, 0x22, 0x54, 0x0a, 0x10, 0x45, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xbd, 0x07, 0x0a, 0x07, 0x43, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x49, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, + 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x12, 0x4f, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, + 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x09, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x62, 0x65, 0x12, 0x55, 0x0a, 0x0b, 0x75, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, + 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0b, 0x75, 0x6e, + 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x49, 0x0a, 0x07, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x65, 0x6e, + 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, + 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x73, 0x68, 0x12, 0x4c, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, + 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x08, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, + 0x63, 0x65, 0x12, 0x5c, 0x0a, 0x0e, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x73, + 0x74, 0x61, 0x74, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x65, 0x6e, + 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, + 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x50, 0x72, 0x65, + 0x73, 0x65, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x52, 0x0d, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, + 0x12, 0x49, 0x0a, 0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, + 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6c, 0x2e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x52, 0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x40, 0x0a, 0x04, 0x70, + 0x69, 0x6e, 0x67, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x63, 0x65, 0x6e, 0x74, + 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, + 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x50, 0x69, 0x6e, 0x67, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x04, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x40, 0x0a, + 0x04, 0x73, 0x65, 0x6e, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x63, 0x65, + 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, + 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, + 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x04, 0x73, 0x65, 0x6e, 0x64, 0x12, + 0x3d, 0x0a, 0x03, 0x72, 0x70, 0x63, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, + 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, + 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x52, + 0x50, 0x43, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x03, 0x72, 0x70, 0x63, 0x12, 0x49, + 0x0a, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2f, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, + 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x52, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x12, 0x53, 0x0a, 0x0b, 0x73, 0x75, 0x62, + 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, + 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, + 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, + 0x2e, 0x53, 0x75, 0x62, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x04, + 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0xe1, 0x07, 0x0a, 0x05, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x3c, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, + 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x12, 0x39, 0x0a, 0x04, 0x70, 0x75, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x25, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, + 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x52, 0x04, 0x70, 0x75, 0x73, 0x68, 0x12, 0x48, 0x0a, + 0x07, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, + 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, + 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, + 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, + 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, 0x4e, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x63, 0x65, 0x6e, + 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, + 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x09, 0x73, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x54, 0x0a, 0x0b, 0x75, 0x6e, 0x73, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x63, + 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, + 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x55, + 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x52, 0x0b, 0x75, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x48, 0x0a, + 0x07, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, + 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, + 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, + 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, + 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x12, 0x4b, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x73, 0x65, + 0x6e, 0x63, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x65, 0x6e, 0x74, + 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, + 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x50, 0x72, 0x65, 0x73, + 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x08, 0x70, 0x72, 0x65, 0x73, + 0x65, 0x6e, 0x63, 0x65, 0x12, 0x5b, 0x0a, 0x0e, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x63, + 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, + 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x50, + 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x52, 0x0d, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x73, 0x12, 0x48, 0x0a, 0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, + 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x52, 0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x3f, 0x0a, 0x04, 0x70, + 0x69, 0x6e, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x65, 0x6e, 0x74, + 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, + 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x50, 0x69, 0x6e, 0x67, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x04, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x3c, 0x0a, 0x03, + 0x72, 0x70, 0x63, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x65, 0x6e, 0x74, + 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, + 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x52, 0x50, 0x43, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x03, 0x72, 0x70, 0x63, 0x12, 0x48, 0x0a, 0x07, 0x72, 0x65, + 0x66, 0x72, 0x65, 0x73, 0x68, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x63, 0x65, + 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, + 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x52, 0x65, + 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x66, + 0x72, 0x65, 0x73, 0x68, 0x12, 0x52, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x5f, 0x72, 0x65, 0x66, 0x72, + 0x65, 0x73, 0x68, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x63, 0x65, 0x6e, 0x74, + 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, + 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x75, 0x62, 0x52, + 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0a, 0x73, 0x75, + 0x62, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x98, + 0x05, 0x0a, 0x04, 0x50, 0x75, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x12, 0x3e, 0x0a, 0x03, 0x70, 0x75, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, + 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, + 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, + 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x70, 0x75, + 0x62, 0x12, 0x39, 0x0a, 0x04, 0x6a, 0x6f, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x25, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, + 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x04, 0x6a, 0x6f, 0x69, 0x6e, 0x12, 0x3c, 0x0a, 0x05, + 0x6c, 0x65, 0x61, 0x76, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x63, 0x65, + 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, + 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x4c, 0x65, + 0x61, 0x76, 0x65, 0x52, 0x05, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x12, 0x4e, 0x0a, 0x0b, 0x75, 0x6e, + 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, + 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x2e, 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x0b, 0x75, + 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x42, 0x0a, 0x07, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x65, + 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, + 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x48, + 0x0a, 0x09, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, + 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x09, 0x73, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x42, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x65, 0x6e, 0x74, + 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, + 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, 0x4b, 0x0a, 0x0a, + 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2b, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, + 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x0a, 0x64, + 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, 0x42, 0x0a, 0x07, 0x72, 0x65, 0x66, + 0x72, 0x65, 0x73, 0x68, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x65, 0x6e, + 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, + 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x52, 0x65, 0x66, + 0x72, 0x65, 0x73, 0x68, 0x52, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x04, 0x08, + 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x72, 0x0a, 0x0a, 0x43, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x6e, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xd5, 0x02, + 0x0a, 0x0b, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2b, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, + 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, + 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x4a, 0x0a, 0x04, 0x74, 0x61, + 0x67, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, + 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x1a, 0x37, 0x0a, 0x09, 0x54, 0x61, + 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, + 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x47, 0x0a, 0x04, 0x4a, 0x6f, 0x69, 0x6e, 0x12, 0x3f, 0x0a, + 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x65, + 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, + 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x43, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x48, + 0x0a, 0x05, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x12, 0x3f, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, + 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x3f, 0x0a, 0x0b, 0x55, 0x6e, 0x73, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0x9b, 0x01, 0x0a, 0x09, 0x53, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x63, 0x6f, 0x76, + 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x72, 0x65, + 0x63, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x70, 0x6f, + 0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, + 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x02, 0x10, + 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x1d, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x98, 0x03, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x46, 0x0a, 0x04, 0x73, 0x75, 0x62, 0x73, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, + 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x2e, 0x53, 0x75, 0x62, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x73, 0x75, 0x62, 0x73, + 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, + 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x12, 0x12, 0x0a, 0x04, + 0x70, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x69, 0x6e, 0x67, + 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x6e, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, + 0x70, 0x6f, 0x6e, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, + 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x1a, 0x69, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x46, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, + 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x56, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x63, + 0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x72, + 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x22, 0x35, 0x0a, 0x07, 0x52, 0x65, 0x66, + 0x72, 0x65, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x12, 0x10, + 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x74, 0x74, 0x6c, + 0x22, 0xb7, 0x03, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4d, 0x0a, + 0x04, 0x73, 0x75, 0x62, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x63, 0x65, + 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, + 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x75, 0x62, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x73, 0x75, 0x62, 0x73, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x56, 0x0a, 0x07, 0x68, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x63, 0x65, + 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, + 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x73, 0x1a, 0x6a, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x47, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x31, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, + 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3a, + 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xa4, 0x03, 0x0a, 0x0d, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, + 0x0a, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4c, + 0x0a, 0x04, 0x73, 0x75, 0x62, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x63, + 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, + 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x53, 0x75, 0x62, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x73, 0x75, 0x62, 0x73, 0x12, 0x12, 0x0a, 0x04, + 0x70, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x69, 0x6e, 0x67, + 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x6e, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, + 0x70, 0x6f, 0x6e, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, + 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x1a, 0x69, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x46, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, + 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x26, 0x0a, 0x0e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x6d, 0x0a, 0x0d, 0x52, 0x65, 0x66, + 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, + 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, + 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, 0x6c, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x22, 0xa1, 0x02, 0x0a, 0x10, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, + 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x18, 0x0a, + 0x07, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x16, 0x0a, + 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x63, + 0x6f, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, + 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6a, + 0x6f, 0x69, 0x6e, 0x5f, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, + 0x6c, 0x74, 0x61, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x74, 0x61, + 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0xfa, 0x02, 0x0a, + 0x0f, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x74, + 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x12, 0x20, 0x0a, 0x0b, + 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0b, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, + 0x70, 0x6f, 0x63, 0x68, 0x12, 0x50, 0x0a, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x63, 0x65, 0x6e, + 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, + 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, + 0x72, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x76, + 0x65, 0x72, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x1e, 0x0a, 0x0a, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0a, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x61, 0x73, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x69, + 0x6e, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x77, 0x61, 0x73, 0x52, 0x65, 0x63, + 0x6f, 0x76, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x74, 0x61, + 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x4a, 0x04, 0x08, + 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0x43, 0x0a, 0x11, 0x53, 0x75, 0x62, + 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, + 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x3e, + 0x0a, 0x10, 0x53, 0x75, 0x62, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, + 0x74, 0x74, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x74, 0x74, 0x6c, 0x22, 0x2e, + 0x0a, 0x12, 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x22, 0x13, + 0x0a, 0x11, 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x22, 0x3e, 0x0a, 0x0e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, + 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x22, 0x0f, 0x0a, 0x0d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x22, 0x2b, 0x0a, 0x0f, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x22, 0xd5, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x12, 0x59, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, + 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, + 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x1a, + 0x68, 0x0a, 0x0d, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x41, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, + 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6c, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x30, 0x0a, 0x14, 0x50, 0x72, 0x65, + 0x73, 0x65, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x22, 0x53, 0x0a, 0x13, 0x50, + 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6e, 0x75, 0x6d, 0x43, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x75, 0x6d, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6e, 0x75, 0x6d, 0x55, 0x73, 0x65, 0x72, 0x73, + 0x22, 0x3e, 0x0a, 0x0e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x70, + 0x6f, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, + 0x22, 0xbf, 0x01, 0x0a, 0x0e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x14, 0x0a, + 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x12, 0x45, 0x0a, 0x05, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, + 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, + 0x76, 0x65, 0x72, 0x73, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x76, + 0x65, 0x72, 0x73, 0x65, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, + 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x06, + 0x10, 0x07, 0x22, 0x8f, 0x01, 0x0a, 0x0d, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x12, 0x50, 0x0a, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x63, 0x65, 0x6e, + 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2e, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x66, + 0x75, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x16, 0x0a, 0x06, + 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x22, 0x0d, 0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x22, 0x0c, 0x0a, 0x0a, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x22, 0x38, 0x0a, 0x0a, 0x52, 0x50, 0x43, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x22, 0x1f, 0x0a, 0x09, 0x52, + 0x50, 0x43, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x21, 0x0a, 0x0b, + 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x42, + 0x21, 0x5a, 0x1f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x65, + 0x6e, 0x74, 0x72, 0x69, 0x66, 0x75, 0x67, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_client_proto_rawDescOnce sync.Once + file_client_proto_rawDescData = file_client_proto_rawDesc +) + +func file_client_proto_rawDescGZIP() []byte { + file_client_proto_rawDescOnce.Do(func() { + file_client_proto_rawDescData = protoimpl.X.CompressGZIP(file_client_proto_rawDescData) + }) + return file_client_proto_rawDescData +} + +var file_client_proto_msgTypes = make([]protoimpl.MessageInfo, 45) +var file_client_proto_goTypes = []interface{}{ + (*Error)(nil), // 0: centrifugal.centrifuge.protocol.Error + (*EmulationRequest)(nil), // 1: centrifugal.centrifuge.protocol.EmulationRequest + (*Command)(nil), // 2: centrifugal.centrifuge.protocol.Command + (*Reply)(nil), // 3: centrifugal.centrifuge.protocol.Reply + (*Push)(nil), // 4: centrifugal.centrifuge.protocol.Push + (*ClientInfo)(nil), // 5: centrifugal.centrifuge.protocol.ClientInfo + (*Publication)(nil), // 6: centrifugal.centrifuge.protocol.Publication + (*Join)(nil), // 7: centrifugal.centrifuge.protocol.Join + (*Leave)(nil), // 8: centrifugal.centrifuge.protocol.Leave + (*Unsubscribe)(nil), // 9: centrifugal.centrifuge.protocol.Unsubscribe + (*Subscribe)(nil), // 10: centrifugal.centrifuge.protocol.Subscribe + (*Message)(nil), // 11: centrifugal.centrifuge.protocol.Message + (*Connect)(nil), // 12: centrifugal.centrifuge.protocol.Connect + (*Disconnect)(nil), // 13: centrifugal.centrifuge.protocol.Disconnect + (*Refresh)(nil), // 14: centrifugal.centrifuge.protocol.Refresh + (*ConnectRequest)(nil), // 15: centrifugal.centrifuge.protocol.ConnectRequest + (*ConnectResult)(nil), // 16: centrifugal.centrifuge.protocol.ConnectResult + (*RefreshRequest)(nil), // 17: centrifugal.centrifuge.protocol.RefreshRequest + (*RefreshResult)(nil), // 18: centrifugal.centrifuge.protocol.RefreshResult + (*SubscribeRequest)(nil), // 19: centrifugal.centrifuge.protocol.SubscribeRequest + (*SubscribeResult)(nil), // 20: centrifugal.centrifuge.protocol.SubscribeResult + (*SubRefreshRequest)(nil), // 21: centrifugal.centrifuge.protocol.SubRefreshRequest + (*SubRefreshResult)(nil), // 22: centrifugal.centrifuge.protocol.SubRefreshResult + (*UnsubscribeRequest)(nil), // 23: centrifugal.centrifuge.protocol.UnsubscribeRequest + (*UnsubscribeResult)(nil), // 24: centrifugal.centrifuge.protocol.UnsubscribeResult + (*PublishRequest)(nil), // 25: centrifugal.centrifuge.protocol.PublishRequest + (*PublishResult)(nil), // 26: centrifugal.centrifuge.protocol.PublishResult + (*PresenceRequest)(nil), // 27: centrifugal.centrifuge.protocol.PresenceRequest + (*PresenceResult)(nil), // 28: centrifugal.centrifuge.protocol.PresenceResult + (*PresenceStatsRequest)(nil), // 29: centrifugal.centrifuge.protocol.PresenceStatsRequest + (*PresenceStatsResult)(nil), // 30: centrifugal.centrifuge.protocol.PresenceStatsResult + (*StreamPosition)(nil), // 31: centrifugal.centrifuge.protocol.StreamPosition + (*HistoryRequest)(nil), // 32: centrifugal.centrifuge.protocol.HistoryRequest + (*HistoryResult)(nil), // 33: centrifugal.centrifuge.protocol.HistoryResult + (*PingRequest)(nil), // 34: centrifugal.centrifuge.protocol.PingRequest + (*PingResult)(nil), // 35: centrifugal.centrifuge.protocol.PingResult + (*RPCRequest)(nil), // 36: centrifugal.centrifuge.protocol.RPCRequest + (*RPCResult)(nil), // 37: centrifugal.centrifuge.protocol.RPCResult + (*SendRequest)(nil), // 38: centrifugal.centrifuge.protocol.SendRequest + nil, // 39: centrifugal.centrifuge.protocol.Publication.TagsEntry + nil, // 40: centrifugal.centrifuge.protocol.Connect.SubsEntry + nil, // 41: centrifugal.centrifuge.protocol.ConnectRequest.SubsEntry + nil, // 42: centrifugal.centrifuge.protocol.ConnectRequest.HeadersEntry + nil, // 43: centrifugal.centrifuge.protocol.ConnectResult.SubsEntry + nil, // 44: centrifugal.centrifuge.protocol.PresenceResult.PresenceEntry +} +var file_client_proto_depIdxs = []int32{ + 15, // 0: centrifugal.centrifuge.protocol.Command.connect:type_name -> centrifugal.centrifuge.protocol.ConnectRequest + 19, // 1: centrifugal.centrifuge.protocol.Command.subscribe:type_name -> centrifugal.centrifuge.protocol.SubscribeRequest + 23, // 2: centrifugal.centrifuge.protocol.Command.unsubscribe:type_name -> centrifugal.centrifuge.protocol.UnsubscribeRequest + 25, // 3: centrifugal.centrifuge.protocol.Command.publish:type_name -> centrifugal.centrifuge.protocol.PublishRequest + 27, // 4: centrifugal.centrifuge.protocol.Command.presence:type_name -> centrifugal.centrifuge.protocol.PresenceRequest + 29, // 5: centrifugal.centrifuge.protocol.Command.presence_stats:type_name -> centrifugal.centrifuge.protocol.PresenceStatsRequest + 32, // 6: centrifugal.centrifuge.protocol.Command.history:type_name -> centrifugal.centrifuge.protocol.HistoryRequest + 34, // 7: centrifugal.centrifuge.protocol.Command.ping:type_name -> centrifugal.centrifuge.protocol.PingRequest + 38, // 8: centrifugal.centrifuge.protocol.Command.send:type_name -> centrifugal.centrifuge.protocol.SendRequest + 36, // 9: centrifugal.centrifuge.protocol.Command.rpc:type_name -> centrifugal.centrifuge.protocol.RPCRequest + 17, // 10: centrifugal.centrifuge.protocol.Command.refresh:type_name -> centrifugal.centrifuge.protocol.RefreshRequest + 21, // 11: centrifugal.centrifuge.protocol.Command.sub_refresh:type_name -> centrifugal.centrifuge.protocol.SubRefreshRequest + 0, // 12: centrifugal.centrifuge.protocol.Reply.error:type_name -> centrifugal.centrifuge.protocol.Error + 4, // 13: centrifugal.centrifuge.protocol.Reply.push:type_name -> centrifugal.centrifuge.protocol.Push + 16, // 14: centrifugal.centrifuge.protocol.Reply.connect:type_name -> centrifugal.centrifuge.protocol.ConnectResult + 20, // 15: centrifugal.centrifuge.protocol.Reply.subscribe:type_name -> centrifugal.centrifuge.protocol.SubscribeResult + 24, // 16: centrifugal.centrifuge.protocol.Reply.unsubscribe:type_name -> centrifugal.centrifuge.protocol.UnsubscribeResult + 26, // 17: centrifugal.centrifuge.protocol.Reply.publish:type_name -> centrifugal.centrifuge.protocol.PublishResult + 28, // 18: centrifugal.centrifuge.protocol.Reply.presence:type_name -> centrifugal.centrifuge.protocol.PresenceResult + 30, // 19: centrifugal.centrifuge.protocol.Reply.presence_stats:type_name -> centrifugal.centrifuge.protocol.PresenceStatsResult + 33, // 20: centrifugal.centrifuge.protocol.Reply.history:type_name -> centrifugal.centrifuge.protocol.HistoryResult + 35, // 21: centrifugal.centrifuge.protocol.Reply.ping:type_name -> centrifugal.centrifuge.protocol.PingResult + 37, // 22: centrifugal.centrifuge.protocol.Reply.rpc:type_name -> centrifugal.centrifuge.protocol.RPCResult + 18, // 23: centrifugal.centrifuge.protocol.Reply.refresh:type_name -> centrifugal.centrifuge.protocol.RefreshResult + 22, // 24: centrifugal.centrifuge.protocol.Reply.sub_refresh:type_name -> centrifugal.centrifuge.protocol.SubRefreshResult + 6, // 25: centrifugal.centrifuge.protocol.Push.pub:type_name -> centrifugal.centrifuge.protocol.Publication + 7, // 26: centrifugal.centrifuge.protocol.Push.join:type_name -> centrifugal.centrifuge.protocol.Join + 8, // 27: centrifugal.centrifuge.protocol.Push.leave:type_name -> centrifugal.centrifuge.protocol.Leave + 9, // 28: centrifugal.centrifuge.protocol.Push.unsubscribe:type_name -> centrifugal.centrifuge.protocol.Unsubscribe + 11, // 29: centrifugal.centrifuge.protocol.Push.message:type_name -> centrifugal.centrifuge.protocol.Message + 10, // 30: centrifugal.centrifuge.protocol.Push.subscribe:type_name -> centrifugal.centrifuge.protocol.Subscribe + 12, // 31: centrifugal.centrifuge.protocol.Push.connect:type_name -> centrifugal.centrifuge.protocol.Connect + 13, // 32: centrifugal.centrifuge.protocol.Push.disconnect:type_name -> centrifugal.centrifuge.protocol.Disconnect + 14, // 33: centrifugal.centrifuge.protocol.Push.refresh:type_name -> centrifugal.centrifuge.protocol.Refresh + 5, // 34: centrifugal.centrifuge.protocol.Publication.info:type_name -> centrifugal.centrifuge.protocol.ClientInfo + 39, // 35: centrifugal.centrifuge.protocol.Publication.tags:type_name -> centrifugal.centrifuge.protocol.Publication.TagsEntry + 5, // 36: centrifugal.centrifuge.protocol.Join.info:type_name -> centrifugal.centrifuge.protocol.ClientInfo + 5, // 37: centrifugal.centrifuge.protocol.Leave.info:type_name -> centrifugal.centrifuge.protocol.ClientInfo + 40, // 38: centrifugal.centrifuge.protocol.Connect.subs:type_name -> centrifugal.centrifuge.protocol.Connect.SubsEntry + 41, // 39: centrifugal.centrifuge.protocol.ConnectRequest.subs:type_name -> centrifugal.centrifuge.protocol.ConnectRequest.SubsEntry + 42, // 40: centrifugal.centrifuge.protocol.ConnectRequest.headers:type_name -> centrifugal.centrifuge.protocol.ConnectRequest.HeadersEntry + 43, // 41: centrifugal.centrifuge.protocol.ConnectResult.subs:type_name -> centrifugal.centrifuge.protocol.ConnectResult.SubsEntry + 6, // 42: centrifugal.centrifuge.protocol.SubscribeResult.publications:type_name -> centrifugal.centrifuge.protocol.Publication + 44, // 43: centrifugal.centrifuge.protocol.PresenceResult.presence:type_name -> centrifugal.centrifuge.protocol.PresenceResult.PresenceEntry + 31, // 44: centrifugal.centrifuge.protocol.HistoryRequest.since:type_name -> centrifugal.centrifuge.protocol.StreamPosition + 6, // 45: centrifugal.centrifuge.protocol.HistoryResult.publications:type_name -> centrifugal.centrifuge.protocol.Publication + 20, // 46: centrifugal.centrifuge.protocol.Connect.SubsEntry.value:type_name -> centrifugal.centrifuge.protocol.SubscribeResult + 19, // 47: centrifugal.centrifuge.protocol.ConnectRequest.SubsEntry.value:type_name -> centrifugal.centrifuge.protocol.SubscribeRequest + 20, // 48: centrifugal.centrifuge.protocol.ConnectResult.SubsEntry.value:type_name -> centrifugal.centrifuge.protocol.SubscribeResult + 5, // 49: centrifugal.centrifuge.protocol.PresenceResult.PresenceEntry.value:type_name -> centrifugal.centrifuge.protocol.ClientInfo + 50, // [50:50] is the sub-list for method output_type + 50, // [50:50] is the sub-list for method input_type + 50, // [50:50] is the sub-list for extension type_name + 50, // [50:50] is the sub-list for extension extendee + 0, // [0:50] is the sub-list for field type_name +} + +func init() { file_client_proto_init() } +func file_client_proto_init() { + if File_client_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_client_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Error); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EmulationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Command); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Reply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Push); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClientInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Publication); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Join); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Leave); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Unsubscribe); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Subscribe); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Message); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Connect); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Disconnect); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Refresh); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConnectRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConnectResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RefreshRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RefreshResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubscribeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubscribeResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubRefreshRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubRefreshResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UnsubscribeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UnsubscribeResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PublishRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PublishResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PresenceRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PresenceResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PresenceStatsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PresenceStatsResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StreamPosition); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HistoryRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HistoryResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PingRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PingResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RPCRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RPCResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_client_proto_rawDesc, + NumEnums: 0, + NumMessages: 45, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_client_proto_goTypes, + DependencyIndexes: file_client_proto_depIdxs, + MessageInfos: file_client_proto_msgTypes, + }.Build() + File_client_proto = out.File + file_client_proto_rawDesc = nil + file_client_proto_goTypes = nil + file_client_proto_depIdxs = nil +} diff --git a/vendor/github.com/centrifugal/protocol/client.pb_easyjson.go b/vendor/github.com/centrifugal/protocol/client.pb_easyjson.go new file mode 100644 index 0000000000..f4df945408 --- /dev/null +++ b/vendor/github.com/centrifugal/protocol/client.pb_easyjson.go @@ -0,0 +1,4034 @@ +// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. + +package protocol + +import ( + json "encoding/json" + + easyjson "github.com/mailru/easyjson" + jlexer "github.com/mailru/easyjson/jlexer" +) + +// suppress unused package warning +var ( + _ *json.RawMessage + _ *jlexer.Lexer + _ *writer + _ easyjson.Marshaler +) + +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild(in *jlexer.Lexer, out *UnsubscribeResult) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild(out *writer, in UnsubscribeResult) { + out.RawByte('{') + first := true + _ = first + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v UnsubscribeResult) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *UnsubscribeResult) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild1(in *jlexer.Lexer, out *UnsubscribeRequest) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "channel": + out.Channel = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild1(out *writer, in UnsubscribeRequest) { + out.RawByte('{') + first := true + _ = first + if in.Channel != "" { + const prefix string = ",\"channel\":" + first = false + out.RawString(prefix[1:]) + out.String(string(in.Channel)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v UnsubscribeRequest) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild1(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *UnsubscribeRequest) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild1(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild2(in *jlexer.Lexer, out *Unsubscribe) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "code": + out.Code = uint32(in.Uint32()) + case "reason": + out.Reason = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild2(out *writer, in Unsubscribe) { + out.RawByte('{') + first := true + _ = first + if in.Code != 0 { + const prefix string = ",\"code\":" + first = false + out.RawString(prefix[1:]) + out.Uint32(uint32(in.Code)) + } + if in.Reason != "" { + const prefix string = ",\"reason\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Reason)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Unsubscribe) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild2(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Unsubscribe) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild2(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild3(in *jlexer.Lexer, out *SubscribeResult) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "expires": + out.Expires = bool(in.Bool()) + case "ttl": + out.Ttl = uint32(in.Uint32()) + case "recoverable": + out.Recoverable = bool(in.Bool()) + case "epoch": + out.Epoch = string(in.String()) + case "publications": + if in.IsNull() { + in.Skip() + out.Publications = nil + } else { + in.Delim('[') + if out.Publications == nil { + if !in.IsDelim(']') { + out.Publications = make([]*Publication, 0, 8) + } else { + out.Publications = []*Publication{} + } + } else { + out.Publications = (out.Publications)[:0] + } + for !in.IsDelim(']') { + var v1 *Publication + if in.IsNull() { + in.Skip() + v1 = nil + } else { + if v1 == nil { + v1 = new(Publication) + } + (*v1).UnmarshalEasyJSON(in) + } + out.Publications = append(out.Publications, v1) + in.WantComma() + } + in.Delim(']') + } + case "recovered": + out.Recovered = bool(in.Bool()) + case "offset": + out.Offset = uint64(in.Uint64()) + case "positioned": + out.Positioned = bool(in.Bool()) + case "data": + if data := in.Raw(); in.Ok() { + in.AddError((out.Data).UnmarshalJSON(data)) + } + case "was_recovering": + out.WasRecovering = bool(in.Bool()) + case "delta": + out.Delta = bool(in.Bool()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild3(out *writer, in SubscribeResult) { + out.RawByte('{') + first := true + _ = first + if in.Expires { + const prefix string = ",\"expires\":" + first = false + out.RawString(prefix[1:]) + out.Bool(bool(in.Expires)) + } + if in.Ttl != 0 { + const prefix string = ",\"ttl\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Uint32(uint32(in.Ttl)) + } + if in.Recoverable { + const prefix string = ",\"recoverable\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.Recoverable)) + } + if in.Epoch != "" { + const prefix string = ",\"epoch\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Epoch)) + } + if len(in.Publications) != 0 { + const prefix string = ",\"publications\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + { + out.RawByte('[') + for v2, v3 := range in.Publications { + if v2 > 0 { + out.RawByte(',') + } + if v3 == nil { + out.RawString("null") + } else { + (*v3).MarshalEasyJSON(out) + } + } + out.RawByte(']') + } + } + if in.Recovered { + const prefix string = ",\"recovered\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.Recovered)) + } + if in.Offset != 0 { + const prefix string = ",\"offset\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Uint64(uint64(in.Offset)) + } + if in.Positioned { + const prefix string = ",\"positioned\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.Positioned)) + } + if len(in.Data) != 0 { + const prefix string = ",\"data\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Raw((in.Data).MarshalJSON()) + } + if in.WasRecovering { + const prefix string = ",\"was_recovering\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.WasRecovering)) + } + if in.Delta { + const prefix string = ",\"delta\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.Delta)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v SubscribeResult) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild3(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *SubscribeResult) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild3(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild4(in *jlexer.Lexer, out *SubscribeRequest) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "channel": + out.Channel = string(in.String()) + case "token": + out.Token = string(in.String()) + case "recover": + out.Recover = bool(in.Bool()) + case "epoch": + out.Epoch = string(in.String()) + case "offset": + out.Offset = uint64(in.Uint64()) + case "data": + if data := in.Raw(); in.Ok() { + in.AddError((out.Data).UnmarshalJSON(data)) + } + case "positioned": + out.Positioned = bool(in.Bool()) + case "recoverable": + out.Recoverable = bool(in.Bool()) + case "join_leave": + out.JoinLeave = bool(in.Bool()) + case "delta": + out.Delta = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild4(out *writer, in SubscribeRequest) { + out.RawByte('{') + first := true + _ = first + if in.Channel != "" { + const prefix string = ",\"channel\":" + first = false + out.RawString(prefix[1:]) + out.String(string(in.Channel)) + } + if in.Token != "" { + const prefix string = ",\"token\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Token)) + } + if in.Recover { + const prefix string = ",\"recover\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.Recover)) + } + if in.Epoch != "" { + const prefix string = ",\"epoch\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Epoch)) + } + if in.Offset != 0 { + const prefix string = ",\"offset\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Uint64(uint64(in.Offset)) + } + if len(in.Data) != 0 { + const prefix string = ",\"data\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Raw((in.Data).MarshalJSON()) + } + if in.Positioned { + const prefix string = ",\"positioned\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.Positioned)) + } + if in.Recoverable { + const prefix string = ",\"recoverable\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.Recoverable)) + } + if in.JoinLeave { + const prefix string = ",\"join_leave\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.JoinLeave)) + } + if in.Delta != "" { + const prefix string = ",\"delta\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Delta)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v SubscribeRequest) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild4(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *SubscribeRequest) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild4(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild5(in *jlexer.Lexer, out *Subscribe) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "recoverable": + out.Recoverable = bool(in.Bool()) + case "epoch": + out.Epoch = string(in.String()) + case "offset": + out.Offset = uint64(in.Uint64()) + case "positioned": + out.Positioned = bool(in.Bool()) + case "data": + if data := in.Raw(); in.Ok() { + in.AddError((out.Data).UnmarshalJSON(data)) + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild5(out *writer, in Subscribe) { + out.RawByte('{') + first := true + _ = first + if in.Recoverable { + const prefix string = ",\"recoverable\":" + first = false + out.RawString(prefix[1:]) + out.Bool(bool(in.Recoverable)) + } + if in.Epoch != "" { + const prefix string = ",\"epoch\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Epoch)) + } + if in.Offset != 0 { + const prefix string = ",\"offset\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Uint64(uint64(in.Offset)) + } + if in.Positioned { + const prefix string = ",\"positioned\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.Positioned)) + } + if len(in.Data) != 0 { + const prefix string = ",\"data\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Raw((in.Data).MarshalJSON()) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Subscribe) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild5(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Subscribe) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild5(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild6(in *jlexer.Lexer, out *SubRefreshResult) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "expires": + out.Expires = bool(in.Bool()) + case "ttl": + out.Ttl = uint32(in.Uint32()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild6(out *writer, in SubRefreshResult) { + out.RawByte('{') + first := true + _ = first + if in.Expires { + const prefix string = ",\"expires\":" + first = false + out.RawString(prefix[1:]) + out.Bool(bool(in.Expires)) + } + if in.Ttl != 0 { + const prefix string = ",\"ttl\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Uint32(uint32(in.Ttl)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v SubRefreshResult) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild6(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *SubRefreshResult) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild6(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild7(in *jlexer.Lexer, out *SubRefreshRequest) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "channel": + out.Channel = string(in.String()) + case "token": + out.Token = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild7(out *writer, in SubRefreshRequest) { + out.RawByte('{') + first := true + _ = first + if in.Channel != "" { + const prefix string = ",\"channel\":" + first = false + out.RawString(prefix[1:]) + out.String(string(in.Channel)) + } + if in.Token != "" { + const prefix string = ",\"token\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Token)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v SubRefreshRequest) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild7(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *SubRefreshRequest) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild7(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild8(in *jlexer.Lexer, out *StreamPosition) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "offset": + out.Offset = uint64(in.Uint64()) + case "epoch": + out.Epoch = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild8(out *writer, in StreamPosition) { + out.RawByte('{') + first := true + _ = first + if in.Offset != 0 { + const prefix string = ",\"offset\":" + first = false + out.RawString(prefix[1:]) + out.Uint64(uint64(in.Offset)) + } + if in.Epoch != "" { + const prefix string = ",\"epoch\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Epoch)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v StreamPosition) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild8(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *StreamPosition) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild8(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild9(in *jlexer.Lexer, out *SendRequest) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "data": + if data := in.Raw(); in.Ok() { + in.AddError((out.Data).UnmarshalJSON(data)) + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild9(out *writer, in SendRequest) { + out.RawByte('{') + first := true + _ = first + if len(in.Data) != 0 { + const prefix string = ",\"data\":" + first = false + out.RawString(prefix[1:]) + out.Raw((in.Data).MarshalJSON()) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v SendRequest) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild9(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *SendRequest) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild9(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild10(in *jlexer.Lexer, out *Reply) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "id": + out.Id = uint32(in.Uint32()) + case "error": + if in.IsNull() { + in.Skip() + out.Error = nil + } else { + if out.Error == nil { + out.Error = new(Error) + } + (*out.Error).UnmarshalEasyJSON(in) + } + case "push": + if in.IsNull() { + in.Skip() + out.Push = nil + } else { + if out.Push == nil { + out.Push = new(Push) + } + (*out.Push).UnmarshalEasyJSON(in) + } + case "connect": + if in.IsNull() { + in.Skip() + out.Connect = nil + } else { + if out.Connect == nil { + out.Connect = new(ConnectResult) + } + (*out.Connect).UnmarshalEasyJSON(in) + } + case "subscribe": + if in.IsNull() { + in.Skip() + out.Subscribe = nil + } else { + if out.Subscribe == nil { + out.Subscribe = new(SubscribeResult) + } + (*out.Subscribe).UnmarshalEasyJSON(in) + } + case "unsubscribe": + if in.IsNull() { + in.Skip() + out.Unsubscribe = nil + } else { + if out.Unsubscribe == nil { + out.Unsubscribe = new(UnsubscribeResult) + } + (*out.Unsubscribe).UnmarshalEasyJSON(in) + } + case "publish": + if in.IsNull() { + in.Skip() + out.Publish = nil + } else { + if out.Publish == nil { + out.Publish = new(PublishResult) + } + (*out.Publish).UnmarshalEasyJSON(in) + } + case "presence": + if in.IsNull() { + in.Skip() + out.Presence = nil + } else { + if out.Presence == nil { + out.Presence = new(PresenceResult) + } + (*out.Presence).UnmarshalEasyJSON(in) + } + case "presence_stats": + if in.IsNull() { + in.Skip() + out.PresenceStats = nil + } else { + if out.PresenceStats == nil { + out.PresenceStats = new(PresenceStatsResult) + } + (*out.PresenceStats).UnmarshalEasyJSON(in) + } + case "history": + if in.IsNull() { + in.Skip() + out.History = nil + } else { + if out.History == nil { + out.History = new(HistoryResult) + } + (*out.History).UnmarshalEasyJSON(in) + } + case "ping": + if in.IsNull() { + in.Skip() + out.Ping = nil + } else { + if out.Ping == nil { + out.Ping = new(PingResult) + } + (*out.Ping).UnmarshalEasyJSON(in) + } + case "rpc": + if in.IsNull() { + in.Skip() + out.Rpc = nil + } else { + if out.Rpc == nil { + out.Rpc = new(RPCResult) + } + (*out.Rpc).UnmarshalEasyJSON(in) + } + case "refresh": + if in.IsNull() { + in.Skip() + out.Refresh = nil + } else { + if out.Refresh == nil { + out.Refresh = new(RefreshResult) + } + (*out.Refresh).UnmarshalEasyJSON(in) + } + case "sub_refresh": + if in.IsNull() { + in.Skip() + out.SubRefresh = nil + } else { + if out.SubRefresh == nil { + out.SubRefresh = new(SubRefreshResult) + } + (*out.SubRefresh).UnmarshalEasyJSON(in) + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild10(out *writer, in Reply) { + out.RawByte('{') + first := true + _ = first + if in.Id != 0 { + const prefix string = ",\"id\":" + first = false + out.RawString(prefix[1:]) + out.Uint32(uint32(in.Id)) + } + if in.Error != nil { + const prefix string = ",\"error\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Error).MarshalEasyJSON(out) + } + if in.Push != nil { + const prefix string = ",\"push\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Push).MarshalEasyJSON(out) + } + if in.Connect != nil { + const prefix string = ",\"connect\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Connect).MarshalEasyJSON(out) + } + if in.Subscribe != nil { + const prefix string = ",\"subscribe\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Subscribe).MarshalEasyJSON(out) + } + if in.Unsubscribe != nil { + const prefix string = ",\"unsubscribe\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Unsubscribe).MarshalEasyJSON(out) + } + if in.Publish != nil { + const prefix string = ",\"publish\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Publish).MarshalEasyJSON(out) + } + if in.Presence != nil { + const prefix string = ",\"presence\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Presence).MarshalEasyJSON(out) + } + if in.PresenceStats != nil { + const prefix string = ",\"presence_stats\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.PresenceStats).MarshalEasyJSON(out) + } + if in.History != nil { + const prefix string = ",\"history\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.History).MarshalEasyJSON(out) + } + if in.Ping != nil { + const prefix string = ",\"ping\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Ping).MarshalEasyJSON(out) + } + if in.Rpc != nil { + const prefix string = ",\"rpc\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Rpc).MarshalEasyJSON(out) + } + if in.Refresh != nil { + const prefix string = ",\"refresh\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Refresh).MarshalEasyJSON(out) + } + if in.SubRefresh != nil { + const prefix string = ",\"sub_refresh\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.SubRefresh).MarshalEasyJSON(out) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Reply) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild10(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Reply) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild10(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild11(in *jlexer.Lexer, out *RefreshResult) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "client": + out.Client = string(in.String()) + case "version": + out.Version = string(in.String()) + case "expires": + out.Expires = bool(in.Bool()) + case "ttl": + out.Ttl = uint32(in.Uint32()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild11(out *writer, in RefreshResult) { + out.RawByte('{') + first := true + _ = first + if in.Client != "" { + const prefix string = ",\"client\":" + first = false + out.RawString(prefix[1:]) + out.String(string(in.Client)) + } + if in.Version != "" { + const prefix string = ",\"version\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Version)) + } + if in.Expires { + const prefix string = ",\"expires\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.Expires)) + } + if in.Ttl != 0 { + const prefix string = ",\"ttl\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Uint32(uint32(in.Ttl)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v RefreshResult) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild11(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *RefreshResult) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild11(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild12(in *jlexer.Lexer, out *RefreshRequest) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "token": + out.Token = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild12(out *writer, in RefreshRequest) { + out.RawByte('{') + first := true + _ = first + if in.Token != "" { + const prefix string = ",\"token\":" + first = false + out.RawString(prefix[1:]) + out.String(string(in.Token)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v RefreshRequest) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild12(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *RefreshRequest) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild12(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild13(in *jlexer.Lexer, out *Refresh) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "expires": + out.Expires = bool(in.Bool()) + case "ttl": + out.Ttl = uint32(in.Uint32()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild13(out *writer, in Refresh) { + out.RawByte('{') + first := true + _ = first + if in.Expires { + const prefix string = ",\"expires\":" + first = false + out.RawString(prefix[1:]) + out.Bool(bool(in.Expires)) + } + if in.Ttl != 0 { + const prefix string = ",\"ttl\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Uint32(uint32(in.Ttl)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Refresh) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild13(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Refresh) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild13(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild14(in *jlexer.Lexer, out *RPCResult) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "data": + if data := in.Raw(); in.Ok() { + in.AddError((out.Data).UnmarshalJSON(data)) + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild14(out *writer, in RPCResult) { + out.RawByte('{') + first := true + _ = first + if len(in.Data) != 0 { + const prefix string = ",\"data\":" + first = false + out.RawString(prefix[1:]) + out.Raw((in.Data).MarshalJSON()) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v RPCResult) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild14(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *RPCResult) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild14(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild15(in *jlexer.Lexer, out *RPCRequest) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "data": + if data := in.Raw(); in.Ok() { + in.AddError((out.Data).UnmarshalJSON(data)) + } + case "method": + out.Method = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild15(out *writer, in RPCRequest) { + out.RawByte('{') + first := true + _ = first + if len(in.Data) != 0 { + const prefix string = ",\"data\":" + first = false + out.RawString(prefix[1:]) + out.Raw((in.Data).MarshalJSON()) + } + if in.Method != "" { + const prefix string = ",\"method\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Method)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v RPCRequest) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild15(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *RPCRequest) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild15(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild16(in *jlexer.Lexer, out *Push) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "channel": + out.Channel = string(in.String()) + case "pub": + if in.IsNull() { + in.Skip() + out.Pub = nil + } else { + if out.Pub == nil { + out.Pub = new(Publication) + } + (*out.Pub).UnmarshalEasyJSON(in) + } + case "join": + if in.IsNull() { + in.Skip() + out.Join = nil + } else { + if out.Join == nil { + out.Join = new(Join) + } + (*out.Join).UnmarshalEasyJSON(in) + } + case "leave": + if in.IsNull() { + in.Skip() + out.Leave = nil + } else { + if out.Leave == nil { + out.Leave = new(Leave) + } + (*out.Leave).UnmarshalEasyJSON(in) + } + case "unsubscribe": + if in.IsNull() { + in.Skip() + out.Unsubscribe = nil + } else { + if out.Unsubscribe == nil { + out.Unsubscribe = new(Unsubscribe) + } + (*out.Unsubscribe).UnmarshalEasyJSON(in) + } + case "message": + if in.IsNull() { + in.Skip() + out.Message = nil + } else { + if out.Message == nil { + out.Message = new(Message) + } + (*out.Message).UnmarshalEasyJSON(in) + } + case "subscribe": + if in.IsNull() { + in.Skip() + out.Subscribe = nil + } else { + if out.Subscribe == nil { + out.Subscribe = new(Subscribe) + } + (*out.Subscribe).UnmarshalEasyJSON(in) + } + case "connect": + if in.IsNull() { + in.Skip() + out.Connect = nil + } else { + if out.Connect == nil { + out.Connect = new(Connect) + } + (*out.Connect).UnmarshalEasyJSON(in) + } + case "disconnect": + if in.IsNull() { + in.Skip() + out.Disconnect = nil + } else { + if out.Disconnect == nil { + out.Disconnect = new(Disconnect) + } + (*out.Disconnect).UnmarshalEasyJSON(in) + } + case "refresh": + if in.IsNull() { + in.Skip() + out.Refresh = nil + } else { + if out.Refresh == nil { + out.Refresh = new(Refresh) + } + (*out.Refresh).UnmarshalEasyJSON(in) + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild16(out *writer, in Push) { + out.RawByte('{') + first := true + _ = first + if in.Channel != "" { + const prefix string = ",\"channel\":" + first = false + out.RawString(prefix[1:]) + out.String(string(in.Channel)) + } + if in.Pub != nil { + const prefix string = ",\"pub\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Pub).MarshalEasyJSON(out) + } + if in.Join != nil { + const prefix string = ",\"join\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Join).MarshalEasyJSON(out) + } + if in.Leave != nil { + const prefix string = ",\"leave\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Leave).MarshalEasyJSON(out) + } + if in.Unsubscribe != nil { + const prefix string = ",\"unsubscribe\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Unsubscribe).MarshalEasyJSON(out) + } + if in.Message != nil { + const prefix string = ",\"message\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Message).MarshalEasyJSON(out) + } + if in.Subscribe != nil { + const prefix string = ",\"subscribe\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Subscribe).MarshalEasyJSON(out) + } + if in.Connect != nil { + const prefix string = ",\"connect\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Connect).MarshalEasyJSON(out) + } + if in.Disconnect != nil { + const prefix string = ",\"disconnect\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Disconnect).MarshalEasyJSON(out) + } + if in.Refresh != nil { + const prefix string = ",\"refresh\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Refresh).MarshalEasyJSON(out) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Push) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild16(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Push) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild16(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild17(in *jlexer.Lexer, out *PublishResult) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild17(out *writer, in PublishResult) { + out.RawByte('{') + first := true + _ = first + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v PublishResult) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild17(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *PublishResult) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild17(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild18(in *jlexer.Lexer, out *PublishRequest) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "channel": + out.Channel = string(in.String()) + case "data": + if data := in.Raw(); in.Ok() { + in.AddError((out.Data).UnmarshalJSON(data)) + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild18(out *writer, in PublishRequest) { + out.RawByte('{') + first := true + _ = first + if in.Channel != "" { + const prefix string = ",\"channel\":" + first = false + out.RawString(prefix[1:]) + out.String(string(in.Channel)) + } + if len(in.Data) != 0 { + const prefix string = ",\"data\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Raw((in.Data).MarshalJSON()) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v PublishRequest) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild18(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *PublishRequest) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild18(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild19(in *jlexer.Lexer, out *Publication) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "data": + if data := in.Raw(); in.Ok() { + in.AddError((out.Data).UnmarshalJSON(data)) + } + case "info": + if in.IsNull() { + in.Skip() + out.Info = nil + } else { + if out.Info == nil { + out.Info = new(ClientInfo) + } + (*out.Info).UnmarshalEasyJSON(in) + } + case "offset": + out.Offset = uint64(in.Uint64()) + case "tags": + if in.IsNull() { + in.Skip() + } else { + in.Delim('{') + if !in.IsDelim('}') { + out.Tags = make(map[string]string) + } else { + out.Tags = nil + } + for !in.IsDelim('}') { + key := string(in.String()) + in.WantColon() + var v4 string + v4 = string(in.String()) + (out.Tags)[key] = v4 + in.WantComma() + } + in.Delim('}') + } + case "delta": + out.Delta = bool(in.Bool()) + case "time": + out.Time = int64(in.Int64()) + case "channel": + out.Channel = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild19(out *writer, in Publication) { + out.RawByte('{') + first := true + _ = first + if len(in.Data) != 0 { + const prefix string = ",\"data\":" + first = false + out.RawString(prefix[1:]) + out.Raw((in.Data).MarshalJSON()) + } + if in.Info != nil { + const prefix string = ",\"info\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Info).MarshalEasyJSON(out) + } + if in.Offset != 0 { + const prefix string = ",\"offset\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Uint64(uint64(in.Offset)) + } + if len(in.Tags) != 0 { + const prefix string = ",\"tags\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + { + out.RawByte('{') + v5First := true + for v5Name, v5Value := range in.Tags { + if v5First { + v5First = false + } else { + out.RawByte(',') + } + out.String(string(v5Name)) + out.RawByte(':') + out.String(string(v5Value)) + } + out.RawByte('}') + } + } + if in.Delta { + const prefix string = ",\"delta\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.Delta)) + } + if in.Time != 0 { + const prefix string = ",\"time\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int64(int64(in.Time)) + } + if in.Channel != "" { + const prefix string = ",\"channel\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Channel)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Publication) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild19(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Publication) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild19(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild20(in *jlexer.Lexer, out *PresenceStatsResult) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "num_clients": + out.NumClients = uint32(in.Uint32()) + case "num_users": + out.NumUsers = uint32(in.Uint32()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild20(out *writer, in PresenceStatsResult) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"num_clients\":" + out.RawString(prefix[1:]) + out.Uint32(uint32(in.NumClients)) + } + { + const prefix string = ",\"num_users\":" + out.RawString(prefix) + out.Uint32(uint32(in.NumUsers)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v PresenceStatsResult) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild20(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *PresenceStatsResult) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild20(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild21(in *jlexer.Lexer, out *PresenceStatsRequest) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "channel": + out.Channel = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild21(out *writer, in PresenceStatsRequest) { + out.RawByte('{') + first := true + _ = first + if in.Channel != "" { + const prefix string = ",\"channel\":" + first = false + out.RawString(prefix[1:]) + out.String(string(in.Channel)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v PresenceStatsRequest) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild21(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *PresenceStatsRequest) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild21(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild22(in *jlexer.Lexer, out *PresenceResult) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "presence": + if in.IsNull() { + in.Skip() + } else { + in.Delim('{') + out.Presence = make(map[string]*ClientInfo) + for !in.IsDelim('}') { + key := string(in.String()) + in.WantColon() + var v6 *ClientInfo + if in.IsNull() { + in.Skip() + v6 = nil + } else { + if v6 == nil { + v6 = new(ClientInfo) + } + (*v6).UnmarshalEasyJSON(in) + } + (out.Presence)[key] = v6 + in.WantComma() + } + in.Delim('}') + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild22(out *writer, in PresenceResult) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"presence\":" + out.RawString(prefix[1:]) + if in.Presence == nil && (out.Flags&nilMapAsEmpty) == 0 { + out.RawString(`null`) + } else { + out.RawByte('{') + v7First := true + for v7Name, v7Value := range in.Presence { + if v7First { + v7First = false + } else { + out.RawByte(',') + } + out.String(string(v7Name)) + out.RawByte(':') + if v7Value == nil { + out.RawString("null") + } else { + (*v7Value).MarshalEasyJSON(out) + } + } + out.RawByte('}') + } + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v PresenceResult) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild22(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *PresenceResult) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild22(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild23(in *jlexer.Lexer, out *PresenceRequest) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "channel": + out.Channel = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild23(out *writer, in PresenceRequest) { + out.RawByte('{') + first := true + _ = first + if in.Channel != "" { + const prefix string = ",\"channel\":" + first = false + out.RawString(prefix[1:]) + out.String(string(in.Channel)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v PresenceRequest) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild23(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *PresenceRequest) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild23(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild24(in *jlexer.Lexer, out *PingResult) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild24(out *writer, in PingResult) { + out.RawByte('{') + first := true + _ = first + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v PingResult) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild24(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *PingResult) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild24(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild25(in *jlexer.Lexer, out *PingRequest) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild25(out *writer, in PingRequest) { + out.RawByte('{') + first := true + _ = first + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v PingRequest) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild25(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *PingRequest) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild25(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild26(in *jlexer.Lexer, out *Message) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "data": + if data := in.Raw(); in.Ok() { + in.AddError((out.Data).UnmarshalJSON(data)) + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild26(out *writer, in Message) { + out.RawByte('{') + first := true + _ = first + if len(in.Data) != 0 { + const prefix string = ",\"data\":" + first = false + out.RawString(prefix[1:]) + out.Raw((in.Data).MarshalJSON()) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Message) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild26(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Message) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild26(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild27(in *jlexer.Lexer, out *Leave) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "info": + if in.IsNull() { + in.Skip() + out.Info = nil + } else { + if out.Info == nil { + out.Info = new(ClientInfo) + } + (*out.Info).UnmarshalEasyJSON(in) + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild27(out *writer, in Leave) { + out.RawByte('{') + first := true + _ = first + if in.Info != nil { + const prefix string = ",\"info\":" + first = false + out.RawString(prefix[1:]) + (*in.Info).MarshalEasyJSON(out) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Leave) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild27(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Leave) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild27(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild28(in *jlexer.Lexer, out *Join) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "info": + if in.IsNull() { + in.Skip() + out.Info = nil + } else { + if out.Info == nil { + out.Info = new(ClientInfo) + } + (*out.Info).UnmarshalEasyJSON(in) + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild28(out *writer, in Join) { + out.RawByte('{') + first := true + _ = first + if in.Info != nil { + const prefix string = ",\"info\":" + first = false + out.RawString(prefix[1:]) + (*in.Info).MarshalEasyJSON(out) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Join) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild28(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Join) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild28(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild29(in *jlexer.Lexer, out *HistoryResult) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "publications": + if in.IsNull() { + in.Skip() + out.Publications = nil + } else { + in.Delim('[') + if out.Publications == nil { + if !in.IsDelim(']') { + out.Publications = make([]*Publication, 0, 8) + } else { + out.Publications = []*Publication{} + } + } else { + out.Publications = (out.Publications)[:0] + } + for !in.IsDelim(']') { + var v8 *Publication + if in.IsNull() { + in.Skip() + v8 = nil + } else { + if v8 == nil { + v8 = new(Publication) + } + (*v8).UnmarshalEasyJSON(in) + } + out.Publications = append(out.Publications, v8) + in.WantComma() + } + in.Delim(']') + } + case "epoch": + out.Epoch = string(in.String()) + case "offset": + out.Offset = uint64(in.Uint64()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild29(out *writer, in HistoryResult) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"publications\":" + out.RawString(prefix[1:]) + if in.Publications == nil && (out.Flags&nilSliceAsEmpty) == 0 { + out.RawString("null") + } else { + out.RawByte('[') + for v9, v10 := range in.Publications { + if v9 > 0 { + out.RawByte(',') + } + if v10 == nil { + out.RawString("null") + } else { + (*v10).MarshalEasyJSON(out) + } + } + out.RawByte(']') + } + } + { + const prefix string = ",\"epoch\":" + out.RawString(prefix) + out.String(string(in.Epoch)) + } + { + const prefix string = ",\"offset\":" + out.RawString(prefix) + out.Uint64(uint64(in.Offset)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v HistoryResult) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild29(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *HistoryResult) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild29(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild30(in *jlexer.Lexer, out *HistoryRequest) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "channel": + out.Channel = string(in.String()) + case "limit": + out.Limit = int32(in.Int32()) + case "since": + if in.IsNull() { + in.Skip() + out.Since = nil + } else { + if out.Since == nil { + out.Since = new(StreamPosition) + } + (*out.Since).UnmarshalEasyJSON(in) + } + case "reverse": + out.Reverse = bool(in.Bool()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild30(out *writer, in HistoryRequest) { + out.RawByte('{') + first := true + _ = first + if in.Channel != "" { + const prefix string = ",\"channel\":" + first = false + out.RawString(prefix[1:]) + out.String(string(in.Channel)) + } + if in.Limit != 0 { + const prefix string = ",\"limit\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int32(int32(in.Limit)) + } + if in.Since != nil { + const prefix string = ",\"since\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Since).MarshalEasyJSON(out) + } + if in.Reverse { + const prefix string = ",\"reverse\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.Reverse)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v HistoryRequest) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild30(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *HistoryRequest) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild30(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild31(in *jlexer.Lexer, out *Error) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "code": + out.Code = uint32(in.Uint32()) + case "message": + out.Message = string(in.String()) + case "temporary": + out.Temporary = bool(in.Bool()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild31(out *writer, in Error) { + out.RawByte('{') + first := true + _ = first + if in.Code != 0 { + const prefix string = ",\"code\":" + first = false + out.RawString(prefix[1:]) + out.Uint32(uint32(in.Code)) + } + if in.Message != "" { + const prefix string = ",\"message\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Message)) + } + if in.Temporary { + const prefix string = ",\"temporary\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.Temporary)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Error) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild31(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Error) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild31(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild32(in *jlexer.Lexer, out *EmulationRequest) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "node": + out.Node = string(in.String()) + case "session": + out.Session = string(in.String()) + case "data": + if data := in.Raw(); in.Ok() { + in.AddError((out.Data).UnmarshalJSON(data)) + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild32(out *writer, in EmulationRequest) { + out.RawByte('{') + first := true + _ = first + if in.Node != "" { + const prefix string = ",\"node\":" + first = false + out.RawString(prefix[1:]) + out.String(string(in.Node)) + } + if in.Session != "" { + const prefix string = ",\"session\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Session)) + } + if len(in.Data) != 0 { + const prefix string = ",\"data\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Raw((in.Data).MarshalJSON()) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v EmulationRequest) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild32(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *EmulationRequest) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild32(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild33(in *jlexer.Lexer, out *Disconnect) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "code": + out.Code = uint32(in.Uint32()) + case "reason": + out.Reason = string(in.String()) + case "reconnect": + out.Reconnect = bool(in.Bool()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild33(out *writer, in Disconnect) { + out.RawByte('{') + first := true + _ = first + if in.Code != 0 { + const prefix string = ",\"code\":" + first = false + out.RawString(prefix[1:]) + out.Uint32(uint32(in.Code)) + } + if in.Reason != "" { + const prefix string = ",\"reason\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Reason)) + } + if in.Reconnect { + const prefix string = ",\"reconnect\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.Reconnect)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Disconnect) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild33(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Disconnect) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild33(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild34(in *jlexer.Lexer, out *ConnectResult) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "client": + out.Client = string(in.String()) + case "version": + out.Version = string(in.String()) + case "expires": + out.Expires = bool(in.Bool()) + case "ttl": + out.Ttl = uint32(in.Uint32()) + case "data": + if data := in.Raw(); in.Ok() { + in.AddError((out.Data).UnmarshalJSON(data)) + } + case "subs": + if in.IsNull() { + in.Skip() + } else { + in.Delim('{') + if !in.IsDelim('}') { + out.Subs = make(map[string]*SubscribeResult) + } else { + out.Subs = nil + } + for !in.IsDelim('}') { + key := string(in.String()) + in.WantColon() + var v11 *SubscribeResult + if in.IsNull() { + in.Skip() + v11 = nil + } else { + if v11 == nil { + v11 = new(SubscribeResult) + } + (*v11).UnmarshalEasyJSON(in) + } + (out.Subs)[key] = v11 + in.WantComma() + } + in.Delim('}') + } + case "ping": + out.Ping = uint32(in.Uint32()) + case "pong": + out.Pong = bool(in.Bool()) + case "session": + out.Session = string(in.String()) + case "node": + out.Node = string(in.String()) + case "time": + out.Time = int64(in.Int64()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild34(out *writer, in ConnectResult) { + out.RawByte('{') + first := true + _ = first + if in.Client != "" { + const prefix string = ",\"client\":" + first = false + out.RawString(prefix[1:]) + out.String(string(in.Client)) + } + if in.Version != "" { + const prefix string = ",\"version\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Version)) + } + if in.Expires { + const prefix string = ",\"expires\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.Expires)) + } + if in.Ttl != 0 { + const prefix string = ",\"ttl\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Uint32(uint32(in.Ttl)) + } + if len(in.Data) != 0 { + const prefix string = ",\"data\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Raw((in.Data).MarshalJSON()) + } + if len(in.Subs) != 0 { + const prefix string = ",\"subs\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + { + out.RawByte('{') + v12First := true + for v12Name, v12Value := range in.Subs { + if v12First { + v12First = false + } else { + out.RawByte(',') + } + out.String(string(v12Name)) + out.RawByte(':') + if v12Value == nil { + out.RawString("null") + } else { + (*v12Value).MarshalEasyJSON(out) + } + } + out.RawByte('}') + } + } + if in.Ping != 0 { + const prefix string = ",\"ping\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Uint32(uint32(in.Ping)) + } + if in.Pong { + const prefix string = ",\"pong\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.Pong)) + } + if in.Session != "" { + const prefix string = ",\"session\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Session)) + } + if in.Node != "" { + const prefix string = ",\"node\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Node)) + } + if in.Time != 0 { + const prefix string = ",\"time\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int64(int64(in.Time)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v ConnectResult) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild34(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *ConnectResult) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild34(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild35(in *jlexer.Lexer, out *ConnectRequest) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "token": + out.Token = string(in.String()) + case "data": + if data := in.Raw(); in.Ok() { + in.AddError((out.Data).UnmarshalJSON(data)) + } + case "subs": + if in.IsNull() { + in.Skip() + } else { + in.Delim('{') + if !in.IsDelim('}') { + out.Subs = make(map[string]*SubscribeRequest) + } else { + out.Subs = nil + } + for !in.IsDelim('}') { + key := string(in.String()) + in.WantColon() + var v13 *SubscribeRequest + if in.IsNull() { + in.Skip() + v13 = nil + } else { + if v13 == nil { + v13 = new(SubscribeRequest) + } + (*v13).UnmarshalEasyJSON(in) + } + (out.Subs)[key] = v13 + in.WantComma() + } + in.Delim('}') + } + case "name": + out.Name = string(in.String()) + case "version": + out.Version = string(in.String()) + case "headers": + if in.IsNull() { + in.Skip() + } else { + in.Delim('{') + if !in.IsDelim('}') { + out.Headers = make(map[string]string) + } else { + out.Headers = nil + } + for !in.IsDelim('}') { + key := string(in.String()) + in.WantColon() + var v14 string + v14 = string(in.String()) + (out.Headers)[key] = v14 + in.WantComma() + } + in.Delim('}') + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild35(out *writer, in ConnectRequest) { + out.RawByte('{') + first := true + _ = first + if in.Token != "" { + const prefix string = ",\"token\":" + first = false + out.RawString(prefix[1:]) + out.String(string(in.Token)) + } + if len(in.Data) != 0 { + const prefix string = ",\"data\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Raw((in.Data).MarshalJSON()) + } + if len(in.Subs) != 0 { + const prefix string = ",\"subs\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + { + out.RawByte('{') + v15First := true + for v15Name, v15Value := range in.Subs { + if v15First { + v15First = false + } else { + out.RawByte(',') + } + out.String(string(v15Name)) + out.RawByte(':') + if v15Value == nil { + out.RawString("null") + } else { + (*v15Value).MarshalEasyJSON(out) + } + } + out.RawByte('}') + } + } + if in.Name != "" { + const prefix string = ",\"name\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Name)) + } + if in.Version != "" { + const prefix string = ",\"version\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Version)) + } + if len(in.Headers) != 0 { + const prefix string = ",\"headers\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + { + out.RawByte('{') + v16First := true + for v16Name, v16Value := range in.Headers { + if v16First { + v16First = false + } else { + out.RawByte(',') + } + out.String(string(v16Name)) + out.RawByte(':') + out.String(string(v16Value)) + } + out.RawByte('}') + } + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v ConnectRequest) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild35(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *ConnectRequest) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild35(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild36(in *jlexer.Lexer, out *Connect) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "client": + out.Client = string(in.String()) + case "version": + out.Version = string(in.String()) + case "data": + if data := in.Raw(); in.Ok() { + in.AddError((out.Data).UnmarshalJSON(data)) + } + case "subs": + if in.IsNull() { + in.Skip() + } else { + in.Delim('{') + if !in.IsDelim('}') { + out.Subs = make(map[string]*SubscribeResult) + } else { + out.Subs = nil + } + for !in.IsDelim('}') { + key := string(in.String()) + in.WantColon() + var v17 *SubscribeResult + if in.IsNull() { + in.Skip() + v17 = nil + } else { + if v17 == nil { + v17 = new(SubscribeResult) + } + (*v17).UnmarshalEasyJSON(in) + } + (out.Subs)[key] = v17 + in.WantComma() + } + in.Delim('}') + } + case "expires": + out.Expires = bool(in.Bool()) + case "ttl": + out.Ttl = uint32(in.Uint32()) + case "ping": + out.Ping = uint32(in.Uint32()) + case "pong": + out.Pong = bool(in.Bool()) + case "session": + out.Session = string(in.String()) + case "node": + out.Node = string(in.String()) + case "time": + out.Time = int64(in.Int64()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild36(out *writer, in Connect) { + out.RawByte('{') + first := true + _ = first + if in.Client != "" { + const prefix string = ",\"client\":" + first = false + out.RawString(prefix[1:]) + out.String(string(in.Client)) + } + if in.Version != "" { + const prefix string = ",\"version\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Version)) + } + if len(in.Data) != 0 { + const prefix string = ",\"data\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Raw((in.Data).MarshalJSON()) + } + if len(in.Subs) != 0 { + const prefix string = ",\"subs\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + { + out.RawByte('{') + v18First := true + for v18Name, v18Value := range in.Subs { + if v18First { + v18First = false + } else { + out.RawByte(',') + } + out.String(string(v18Name)) + out.RawByte(':') + if v18Value == nil { + out.RawString("null") + } else { + (*v18Value).MarshalEasyJSON(out) + } + } + out.RawByte('}') + } + } + if in.Expires { + const prefix string = ",\"expires\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.Expires)) + } + if in.Ttl != 0 { + const prefix string = ",\"ttl\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Uint32(uint32(in.Ttl)) + } + if in.Ping != 0 { + const prefix string = ",\"ping\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Uint32(uint32(in.Ping)) + } + if in.Pong { + const prefix string = ",\"pong\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Bool(bool(in.Pong)) + } + if in.Session != "" { + const prefix string = ",\"session\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Session)) + } + if in.Node != "" { + const prefix string = ",\"node\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Node)) + } + if in.Time != 0 { + const prefix string = ",\"time\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int64(int64(in.Time)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Connect) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild36(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Connect) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild36(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild37(in *jlexer.Lexer, out *Command) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "id": + out.Id = uint32(in.Uint32()) + case "connect": + if in.IsNull() { + in.Skip() + out.Connect = nil + } else { + if out.Connect == nil { + out.Connect = new(ConnectRequest) + } + (*out.Connect).UnmarshalEasyJSON(in) + } + case "subscribe": + if in.IsNull() { + in.Skip() + out.Subscribe = nil + } else { + if out.Subscribe == nil { + out.Subscribe = new(SubscribeRequest) + } + (*out.Subscribe).UnmarshalEasyJSON(in) + } + case "unsubscribe": + if in.IsNull() { + in.Skip() + out.Unsubscribe = nil + } else { + if out.Unsubscribe == nil { + out.Unsubscribe = new(UnsubscribeRequest) + } + (*out.Unsubscribe).UnmarshalEasyJSON(in) + } + case "publish": + if in.IsNull() { + in.Skip() + out.Publish = nil + } else { + if out.Publish == nil { + out.Publish = new(PublishRequest) + } + (*out.Publish).UnmarshalEasyJSON(in) + } + case "presence": + if in.IsNull() { + in.Skip() + out.Presence = nil + } else { + if out.Presence == nil { + out.Presence = new(PresenceRequest) + } + (*out.Presence).UnmarshalEasyJSON(in) + } + case "presence_stats": + if in.IsNull() { + in.Skip() + out.PresenceStats = nil + } else { + if out.PresenceStats == nil { + out.PresenceStats = new(PresenceStatsRequest) + } + (*out.PresenceStats).UnmarshalEasyJSON(in) + } + case "history": + if in.IsNull() { + in.Skip() + out.History = nil + } else { + if out.History == nil { + out.History = new(HistoryRequest) + } + (*out.History).UnmarshalEasyJSON(in) + } + case "ping": + if in.IsNull() { + in.Skip() + out.Ping = nil + } else { + if out.Ping == nil { + out.Ping = new(PingRequest) + } + (*out.Ping).UnmarshalEasyJSON(in) + } + case "send": + if in.IsNull() { + in.Skip() + out.Send = nil + } else { + if out.Send == nil { + out.Send = new(SendRequest) + } + (*out.Send).UnmarshalEasyJSON(in) + } + case "rpc": + if in.IsNull() { + in.Skip() + out.Rpc = nil + } else { + if out.Rpc == nil { + out.Rpc = new(RPCRequest) + } + (*out.Rpc).UnmarshalEasyJSON(in) + } + case "refresh": + if in.IsNull() { + in.Skip() + out.Refresh = nil + } else { + if out.Refresh == nil { + out.Refresh = new(RefreshRequest) + } + (*out.Refresh).UnmarshalEasyJSON(in) + } + case "sub_refresh": + if in.IsNull() { + in.Skip() + out.SubRefresh = nil + } else { + if out.SubRefresh == nil { + out.SubRefresh = new(SubRefreshRequest) + } + (*out.SubRefresh).UnmarshalEasyJSON(in) + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild37(out *writer, in Command) { + out.RawByte('{') + first := true + _ = first + if in.Id != 0 { + const prefix string = ",\"id\":" + first = false + out.RawString(prefix[1:]) + out.Uint32(uint32(in.Id)) + } + if in.Connect != nil { + const prefix string = ",\"connect\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Connect).MarshalEasyJSON(out) + } + if in.Subscribe != nil { + const prefix string = ",\"subscribe\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Subscribe).MarshalEasyJSON(out) + } + if in.Unsubscribe != nil { + const prefix string = ",\"unsubscribe\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Unsubscribe).MarshalEasyJSON(out) + } + if in.Publish != nil { + const prefix string = ",\"publish\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Publish).MarshalEasyJSON(out) + } + if in.Presence != nil { + const prefix string = ",\"presence\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Presence).MarshalEasyJSON(out) + } + if in.PresenceStats != nil { + const prefix string = ",\"presence_stats\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.PresenceStats).MarshalEasyJSON(out) + } + if in.History != nil { + const prefix string = ",\"history\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.History).MarshalEasyJSON(out) + } + if in.Ping != nil { + const prefix string = ",\"ping\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Ping).MarshalEasyJSON(out) + } + if in.Send != nil { + const prefix string = ",\"send\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Send).MarshalEasyJSON(out) + } + if in.Rpc != nil { + const prefix string = ",\"rpc\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Rpc).MarshalEasyJSON(out) + } + if in.Refresh != nil { + const prefix string = ",\"refresh\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Refresh).MarshalEasyJSON(out) + } + if in.SubRefresh != nil { + const prefix string = ",\"sub_refresh\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.SubRefresh).MarshalEasyJSON(out) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Command) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild37(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Command) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild37(l, v) +} +func easyjson19c08265DecodeGithubComCentrifugalProtocolBuild38(in *jlexer.Lexer, out *ClientInfo) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "user": + out.User = string(in.String()) + case "client": + out.Client = string(in.String()) + case "conn_info": + if data := in.Raw(); in.Ok() { + in.AddError((out.ConnInfo).UnmarshalJSON(data)) + } + case "chan_info": + if data := in.Raw(); in.Ok() { + in.AddError((out.ChanInfo).UnmarshalJSON(data)) + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson19c08265EncodeGithubComCentrifugalProtocolBuild38(out *writer, in ClientInfo) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"user\":" + out.RawString(prefix[1:]) + out.String(string(in.User)) + } + { + const prefix string = ",\"client\":" + out.RawString(prefix) + out.String(string(in.Client)) + } + if len(in.ConnInfo) != 0 { + const prefix string = ",\"conn_info\":" + out.RawString(prefix) + out.Raw((in.ConnInfo).MarshalJSON()) + } + if len(in.ChanInfo) != 0 { + const prefix string = ",\"chan_info\":" + out.RawString(prefix) + out.Raw((in.ChanInfo).MarshalJSON()) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v ClientInfo) MarshalEasyJSON(w *writer) { + easyjson19c08265EncodeGithubComCentrifugalProtocolBuild38(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *ClientInfo) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson19c08265DecodeGithubComCentrifugalProtocolBuild38(l, v) +} diff --git a/vendor/github.com/centrifugal/protocol/client.proto b/vendor/github.com/centrifugal/protocol/client.proto new file mode 100644 index 0000000000..15fd696926 --- /dev/null +++ b/vendor/github.com/centrifugal/protocol/client.proto @@ -0,0 +1,299 @@ +syntax = "proto3"; + +package centrifugal.centrifuge.protocol; + +option go_package = "github.com/centrifugal/protocol"; + +message Error { + uint32 code = 1; + string message = 2; + bool temporary = 3; +} + +message EmulationRequest { + string node = 1; + string session = 2; + bytes data = 3; +} + +// Command sent from a client to a server. +// ProtocolVersion2 uses id and one of the possible request messages. +message Command { + // Id of command to let client match replies to commands. + uint32 id = 1; + + reserved 2, 3; + + // ProtocolVersion2 client can send one of the following requests. Server will + // only take the first non-null request out of these and may return an error if + // client passed more than one request. We are not using oneof here due to JSON + // interoperability concerns. + ConnectRequest connect = 4; + SubscribeRequest subscribe = 5; + UnsubscribeRequest unsubscribe = 6; + PublishRequest publish = 7; + PresenceRequest presence = 8; + PresenceStatsRequest presence_stats = 9; + HistoryRequest history = 10; + PingRequest ping = 11; + SendRequest send = 12; + RPCRequest rpc = 13; + RefreshRequest refresh = 14; + SubRefreshRequest sub_refresh = 15; +} + +// Reply sent from a server to a client. +// ProtocolVersion2 uses id and one of the possible concrete result messages. +message Reply { + // Id will only be set to a value > 0 for replies to commands. For pushes + // it will have zero value. + uint32 id = 1; + // Error can only be set in replies to commands. For pushes it will have zero value. + Error error = 2; + + reserved 3; + + // ProtocolVersion2 server can send one of the following fields. We are not using + // oneof here due to JSON interoperability concerns. + Push push = 4; + ConnectResult connect = 5; + SubscribeResult subscribe = 6; + UnsubscribeResult unsubscribe = 7; + PublishResult publish = 8; + PresenceResult presence = 9; + PresenceStatsResult presence_stats = 10; + HistoryResult history = 11; + PingResult ping = 12; + RPCResult rpc = 13; + RefreshResult refresh = 14; + SubRefreshResult sub_refresh = 15; +} + +// Push can be sent to a client as part of Reply in case of bidirectional transport or +// without additional wrapping in case of unidirectional transports. +// ProtocolVersion2 uses channel and one of the possible concrete push messages. +message Push { + reserved 1, 3; + string channel = 2; + + // ProtocolVersion2 server can push one of the following fields to the client. We are + // not using oneof here due to JSON interoperability concerns. + Publication pub = 4; + Join join = 5; + Leave leave = 6; + Unsubscribe unsubscribe = 7; + Message message = 8; + Subscribe subscribe = 9; + Connect connect = 10; + Disconnect disconnect = 11; + Refresh refresh = 12; +} + +message ClientInfo { + string user = 1; + string client = 2; + bytes conn_info = 3; + bytes chan_info = 4; +} + +message Publication { + reserved 1, 2, 3; + bytes data = 4; + ClientInfo info = 5; + uint64 offset = 6; + map tags = 7; + bool delta = 8; // When set indicates that data in Publication is a delta from previous data. + int64 time = 9; // Optional time of publication as Unix timestamp milliseconds. + string channel = 10; // Optional channel name if Publication relates to wildcard subscription. +} + +message Join { + ClientInfo info = 1; +} + +message Leave { + ClientInfo info = 1; +} + +message Unsubscribe { + reserved 1; + uint32 code = 2; + string reason = 3; +} + +message Subscribe { + bool recoverable = 1; + reserved 2, 3; + string epoch = 4; + uint64 offset = 5; + bool positioned = 6; + bytes data = 7; +} + +message Message { + bytes data = 1; +} + +message Connect { + string client = 1; + string version = 2; + bytes data = 3; + map subs = 4; + bool expires = 5; + uint32 ttl = 6; + uint32 ping = 7; + bool pong = 8; + string session = 9; + string node = 10; + int64 time = 11; // Server time as Unix timestamp in milliseconds (not sent by default). +} + +message Disconnect { + uint32 code = 1; + string reason = 2; + bool reconnect = 3; +} + +message Refresh { + bool expires = 1; + uint32 ttl = 2; +} + +message ConnectRequest { + string token = 1; + bytes data = 2; + map subs = 3; + string name = 4; + string version = 5; + map headers = 6; +} + +message ConnectResult { + string client = 1; + string version = 2; + bool expires = 3; + uint32 ttl = 4; + bytes data = 5; + map subs = 6; + uint32 ping = 7; + bool pong = 8; + string session = 9; + string node = 10; + int64 time = 11; // Server time as Unix timestamp in milliseconds (not sent by default). +} + +message RefreshRequest { + string token = 1; +} + +message RefreshResult { + string client = 1; + string version = 2; + bool expires = 3; + uint32 ttl = 4; +} + +message SubscribeRequest { + string channel = 1; + string token = 2; + bool recover = 3; + reserved 4, 5; + string epoch = 6; + uint64 offset = 7; + bytes data = 8; + bool positioned = 9; + bool recoverable = 10; + bool join_leave = 11; + string delta = 12; +} + +message SubscribeResult { + bool expires = 1; + uint32 ttl = 2; + bool recoverable = 3; + reserved 4, 5; + string epoch = 6; + repeated Publication publications = 7; + bool recovered = 8; + uint64 offset = 9; + bool positioned = 10; + bytes data = 11; + bool was_recovering = 12; + bool delta = 13; +} + +message SubRefreshRequest { + string channel = 1; + string token = 2; +} + +message SubRefreshResult { + bool expires = 1; + uint32 ttl = 2; +} + +message UnsubscribeRequest { + string channel = 1; +} + +message UnsubscribeResult {} + +message PublishRequest { + string channel = 1; + bytes data = 2; +} + +message PublishResult {} + +message PresenceRequest { + string channel = 1; +} + +message PresenceResult { + map presence = 1; +} + +message PresenceStatsRequest { + string channel = 1; +} + +message PresenceStatsResult { + uint32 num_clients = 1; + uint32 num_users = 2; +} + +message StreamPosition { + uint64 offset = 1; + string epoch = 2; +} + +message HistoryRequest { + string channel = 1; + reserved 2, 3, 4, 5, 6; + int32 limit = 7; + StreamPosition since = 8; + bool reverse = 9; +} + +message HistoryResult { + repeated Publication publications = 1; + string epoch = 2; + uint64 offset = 3; +} + +message PingRequest {} + +message PingResult {} + +message RPCRequest{ + bytes data = 1; + string method = 2; +} + +message RPCResult { + bytes data = 1 ; +} + +message SendRequest{ + bytes data = 1; +} diff --git a/vendor/github.com/centrifugal/protocol/client_vtproto.pb.go b/vendor/github.com/centrifugal/protocol/client_vtproto.pb.go new file mode 100644 index 0000000000..500e4df418 --- /dev/null +++ b/vendor/github.com/centrifugal/protocol/client_vtproto.pb.go @@ -0,0 +1,10440 @@ +// Code generated by protoc-gen-go-vtproto. DO NOT EDIT. +// protoc-gen-go-vtproto version: v0.6.0 +// source: client.proto + +package protocol + +import ( + fmt "fmt" + protohelpers "github.com/planetscale/vtprotobuf/protohelpers" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + io "io" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +func (m *Error) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Error) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Error) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Temporary { + i-- + if m.Temporary { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0x12 + } + if m.Code != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Code)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *EmulationRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EmulationRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *EmulationRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x1a + } + if len(m.Session) > 0 { + i -= len(m.Session) + copy(dAtA[i:], m.Session) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Session))) + i-- + dAtA[i] = 0x12 + } + if len(m.Node) > 0 { + i -= len(m.Node) + copy(dAtA[i:], m.Node) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Node))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Command) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Command) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Command) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.SubRefresh != nil { + size, err := m.SubRefresh.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x7a + } + if m.Refresh != nil { + size, err := m.Refresh.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x72 + } + if m.Rpc != nil { + size, err := m.Rpc.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x6a + } + if m.Send != nil { + size, err := m.Send.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x62 + } + if m.Ping != nil { + size, err := m.Ping.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x5a + } + if m.History != nil { + size, err := m.History.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x52 + } + if m.PresenceStats != nil { + size, err := m.PresenceStats.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x4a + } + if m.Presence != nil { + size, err := m.Presence.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x42 + } + if m.Publish != nil { + size, err := m.Publish.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x3a + } + if m.Unsubscribe != nil { + size, err := m.Unsubscribe.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x32 + } + if m.Subscribe != nil { + size, err := m.Subscribe.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x2a + } + if m.Connect != nil { + size, err := m.Connect.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x22 + } + if m.Id != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Reply) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Reply) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Reply) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.SubRefresh != nil { + size, err := m.SubRefresh.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x7a + } + if m.Refresh != nil { + size, err := m.Refresh.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x72 + } + if m.Rpc != nil { + size, err := m.Rpc.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x6a + } + if m.Ping != nil { + size, err := m.Ping.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x62 + } + if m.History != nil { + size, err := m.History.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x5a + } + if m.PresenceStats != nil { + size, err := m.PresenceStats.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x52 + } + if m.Presence != nil { + size, err := m.Presence.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x4a + } + if m.Publish != nil { + size, err := m.Publish.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x42 + } + if m.Unsubscribe != nil { + size, err := m.Unsubscribe.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x3a + } + if m.Subscribe != nil { + size, err := m.Subscribe.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x32 + } + if m.Connect != nil { + size, err := m.Connect.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x2a + } + if m.Push != nil { + size, err := m.Push.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x22 + } + if m.Error != nil { + size, err := m.Error.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + } + if m.Id != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Push) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Push) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Push) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Refresh != nil { + size, err := m.Refresh.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x62 + } + if m.Disconnect != nil { + size, err := m.Disconnect.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x5a + } + if m.Connect != nil { + size, err := m.Connect.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x52 + } + if m.Subscribe != nil { + size, err := m.Subscribe.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x4a + } + if m.Message != nil { + size, err := m.Message.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x42 + } + if m.Unsubscribe != nil { + size, err := m.Unsubscribe.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x3a + } + if m.Leave != nil { + size, err := m.Leave.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x32 + } + if m.Join != nil { + size, err := m.Join.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x2a + } + if m.Pub != nil { + size, err := m.Pub.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x22 + } + if len(m.Channel) > 0 { + i -= len(m.Channel) + copy(dAtA[i:], m.Channel) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Channel))) + i-- + dAtA[i] = 0x12 + } + return len(dAtA) - i, nil +} + +func (m *ClientInfo) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ClientInfo) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ClientInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.ChanInfo) > 0 { + i -= len(m.ChanInfo) + copy(dAtA[i:], m.ChanInfo) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ChanInfo))) + i-- + dAtA[i] = 0x22 + } + if len(m.ConnInfo) > 0 { + i -= len(m.ConnInfo) + copy(dAtA[i:], m.ConnInfo) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ConnInfo))) + i-- + dAtA[i] = 0x1a + } + if len(m.Client) > 0 { + i -= len(m.Client) + copy(dAtA[i:], m.Client) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Client))) + i-- + dAtA[i] = 0x12 + } + if len(m.User) > 0 { + i -= len(m.User) + copy(dAtA[i:], m.User) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.User))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Publication) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Publication) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Publication) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Channel) > 0 { + i -= len(m.Channel) + copy(dAtA[i:], m.Channel) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Channel))) + i-- + dAtA[i] = 0x52 + } + if m.Time != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Time)) + i-- + dAtA[i] = 0x48 + } + if m.Delta { + i-- + if m.Delta { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + } + if len(m.Tags) > 0 { + for k := range m.Tags { + v := m.Tags[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x3a + } + } + if m.Offset != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Offset)) + i-- + dAtA[i] = 0x30 + } + if m.Info != nil { + size, err := m.Info.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x2a + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x22 + } + return len(dAtA) - i, nil +} + +func (m *Join) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Join) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Join) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Info != nil { + size, err := m.Info.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Leave) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Leave) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Leave) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Info != nil { + size, err := m.Info.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Unsubscribe) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Unsubscribe) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Unsubscribe) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Reason) > 0 { + i -= len(m.Reason) + copy(dAtA[i:], m.Reason) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Reason))) + i-- + dAtA[i] = 0x1a + } + if m.Code != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Code)) + i-- + dAtA[i] = 0x10 + } + return len(dAtA) - i, nil +} + +func (m *Subscribe) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Subscribe) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Subscribe) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x3a + } + if m.Positioned { + i-- + if m.Positioned { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 + } + if m.Offset != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Offset)) + i-- + dAtA[i] = 0x28 + } + if len(m.Epoch) > 0 { + i -= len(m.Epoch) + copy(dAtA[i:], m.Epoch) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Epoch))) + i-- + dAtA[i] = 0x22 + } + if m.Recoverable { + i-- + if m.Recoverable { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Message) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Message) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Message) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Connect) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Connect) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Connect) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Time != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Time)) + i-- + dAtA[i] = 0x58 + } + if len(m.Node) > 0 { + i -= len(m.Node) + copy(dAtA[i:], m.Node) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Node))) + i-- + dAtA[i] = 0x52 + } + if len(m.Session) > 0 { + i -= len(m.Session) + copy(dAtA[i:], m.Session) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Session))) + i-- + dAtA[i] = 0x4a + } + if m.Pong { + i-- + if m.Pong { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + } + if m.Ping != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Ping)) + i-- + dAtA[i] = 0x38 + } + if m.Ttl != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Ttl)) + i-- + dAtA[i] = 0x30 + } + if m.Expires { + i-- + if m.Expires { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if len(m.Subs) > 0 { + for k := range m.Subs { + v := m.Subs[k] + baseI := i + size, err := v.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x1a + } + if len(m.Version) > 0 { + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0x12 + } + if len(m.Client) > 0 { + i -= len(m.Client) + copy(dAtA[i:], m.Client) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Client))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Disconnect) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Disconnect) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Disconnect) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Reconnect { + i-- + if m.Reconnect { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.Reason) > 0 { + i -= len(m.Reason) + copy(dAtA[i:], m.Reason) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Reason))) + i-- + dAtA[i] = 0x12 + } + if m.Code != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Code)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Refresh) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Refresh) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *Refresh) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Ttl != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Ttl)) + i-- + dAtA[i] = 0x10 + } + if m.Expires { + i-- + if m.Expires { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ConnectRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ConnectRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ConnectRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Headers) > 0 { + for k := range m.Headers { + v := m.Headers[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x32 + } + } + if len(m.Version) > 0 { + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0x2a + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x22 + } + if len(m.Subs) > 0 { + for k := range m.Subs { + v := m.Subs[k] + baseI := i + size, err := v.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x12 + } + if len(m.Token) > 0 { + i -= len(m.Token) + copy(dAtA[i:], m.Token) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Token))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ConnectResult) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ConnectResult) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *ConnectResult) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Time != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Time)) + i-- + dAtA[i] = 0x58 + } + if len(m.Node) > 0 { + i -= len(m.Node) + copy(dAtA[i:], m.Node) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Node))) + i-- + dAtA[i] = 0x52 + } + if len(m.Session) > 0 { + i -= len(m.Session) + copy(dAtA[i:], m.Session) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Session))) + i-- + dAtA[i] = 0x4a + } + if m.Pong { + i-- + if m.Pong { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + } + if m.Ping != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Ping)) + i-- + dAtA[i] = 0x38 + } + if len(m.Subs) > 0 { + for k := range m.Subs { + v := m.Subs[k] + baseI := i + size, err := v.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x32 + } + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x2a + } + if m.Ttl != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Ttl)) + i-- + dAtA[i] = 0x20 + } + if m.Expires { + i-- + if m.Expires { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.Version) > 0 { + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0x12 + } + if len(m.Client) > 0 { + i -= len(m.Client) + copy(dAtA[i:], m.Client) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Client))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RefreshRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RefreshRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *RefreshRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Token) > 0 { + i -= len(m.Token) + copy(dAtA[i:], m.Token) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Token))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RefreshResult) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RefreshResult) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *RefreshResult) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Ttl != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Ttl)) + i-- + dAtA[i] = 0x20 + } + if m.Expires { + i-- + if m.Expires { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.Version) > 0 { + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0x12 + } + if len(m.Client) > 0 { + i -= len(m.Client) + copy(dAtA[i:], m.Client) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Client))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SubscribeRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SubscribeRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *SubscribeRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Delta) > 0 { + i -= len(m.Delta) + copy(dAtA[i:], m.Delta) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Delta))) + i-- + dAtA[i] = 0x62 + } + if m.JoinLeave { + i-- + if m.JoinLeave { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x58 + } + if m.Recoverable { + i-- + if m.Recoverable { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x50 + } + if m.Positioned { + i-- + if m.Positioned { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x48 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x42 + } + if m.Offset != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Offset)) + i-- + dAtA[i] = 0x38 + } + if len(m.Epoch) > 0 { + i -= len(m.Epoch) + copy(dAtA[i:], m.Epoch) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Epoch))) + i-- + dAtA[i] = 0x32 + } + if m.Recover { + i-- + if m.Recover { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.Token) > 0 { + i -= len(m.Token) + copy(dAtA[i:], m.Token) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Token))) + i-- + dAtA[i] = 0x12 + } + if len(m.Channel) > 0 { + i -= len(m.Channel) + copy(dAtA[i:], m.Channel) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Channel))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SubscribeResult) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SubscribeResult) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *SubscribeResult) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Delta { + i-- + if m.Delta { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x68 + } + if m.WasRecovering { + i-- + if m.WasRecovering { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x60 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x5a + } + if m.Positioned { + i-- + if m.Positioned { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x50 + } + if m.Offset != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Offset)) + i-- + dAtA[i] = 0x48 + } + if m.Recovered { + i-- + if m.Recovered { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + } + if len(m.Publications) > 0 { + for iNdEx := len(m.Publications) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Publications[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x3a + } + } + if len(m.Epoch) > 0 { + i -= len(m.Epoch) + copy(dAtA[i:], m.Epoch) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Epoch))) + i-- + dAtA[i] = 0x32 + } + if m.Recoverable { + i-- + if m.Recoverable { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.Ttl != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Ttl)) + i-- + dAtA[i] = 0x10 + } + if m.Expires { + i-- + if m.Expires { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *SubRefreshRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SubRefreshRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *SubRefreshRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Token) > 0 { + i -= len(m.Token) + copy(dAtA[i:], m.Token) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Token))) + i-- + dAtA[i] = 0x12 + } + if len(m.Channel) > 0 { + i -= len(m.Channel) + copy(dAtA[i:], m.Channel) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Channel))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SubRefreshResult) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SubRefreshResult) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *SubRefreshResult) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Ttl != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Ttl)) + i-- + dAtA[i] = 0x10 + } + if m.Expires { + i-- + if m.Expires { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *UnsubscribeRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UnsubscribeRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *UnsubscribeRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Channel) > 0 { + i -= len(m.Channel) + copy(dAtA[i:], m.Channel) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Channel))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UnsubscribeResult) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UnsubscribeResult) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *UnsubscribeResult) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + return len(dAtA) - i, nil +} + +func (m *PublishRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PublishRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *PublishRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x12 + } + if len(m.Channel) > 0 { + i -= len(m.Channel) + copy(dAtA[i:], m.Channel) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Channel))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PublishResult) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PublishResult) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *PublishResult) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + return len(dAtA) - i, nil +} + +func (m *PresenceRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PresenceRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *PresenceRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Channel) > 0 { + i -= len(m.Channel) + copy(dAtA[i:], m.Channel) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Channel))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PresenceResult) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PresenceResult) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *PresenceResult) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Presence) > 0 { + for k := range m.Presence { + v := m.Presence[k] + baseI := i + size, err := v.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *PresenceStatsRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PresenceStatsRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *PresenceStatsRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Channel) > 0 { + i -= len(m.Channel) + copy(dAtA[i:], m.Channel) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Channel))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PresenceStatsResult) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PresenceStatsResult) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *PresenceStatsResult) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.NumUsers != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.NumUsers)) + i-- + dAtA[i] = 0x10 + } + if m.NumClients != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.NumClients)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *StreamPosition) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *StreamPosition) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *StreamPosition) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Epoch) > 0 { + i -= len(m.Epoch) + copy(dAtA[i:], m.Epoch) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Epoch))) + i-- + dAtA[i] = 0x12 + } + if m.Offset != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Offset)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *HistoryRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *HistoryRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *HistoryRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Reverse { + i-- + if m.Reverse { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x48 + } + if m.Since != nil { + size, err := m.Since.MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x42 + } + if m.Limit != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Limit)) + i-- + dAtA[i] = 0x38 + } + if len(m.Channel) > 0 { + i -= len(m.Channel) + copy(dAtA[i:], m.Channel) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Channel))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *HistoryResult) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *HistoryResult) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *HistoryResult) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.Offset != 0 { + i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Offset)) + i-- + dAtA[i] = 0x18 + } + if len(m.Epoch) > 0 { + i -= len(m.Epoch) + copy(dAtA[i:], m.Epoch) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Epoch))) + i-- + dAtA[i] = 0x12 + } + if len(m.Publications) > 0 { + for iNdEx := len(m.Publications) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Publications[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *PingRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PingRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *PingRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + return len(dAtA) - i, nil +} + +func (m *PingResult) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PingResult) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *PingResult) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + return len(dAtA) - i, nil +} + +func (m *RPCRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RPCRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *RPCRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Method) > 0 { + i -= len(m.Method) + copy(dAtA[i:], m.Method) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Method))) + i-- + dAtA[i] = 0x12 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RPCResult) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RPCResult) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *RPCResult) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SendRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SendRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *SendRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Error) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Code != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Code)) + } + l = len(m.Message) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Temporary { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *EmulationRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Node) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.Session) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Command) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Id)) + } + if m.Connect != nil { + l = m.Connect.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Subscribe != nil { + l = m.Subscribe.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Unsubscribe != nil { + l = m.Unsubscribe.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Publish != nil { + l = m.Publish.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Presence != nil { + l = m.Presence.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.PresenceStats != nil { + l = m.PresenceStats.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.History != nil { + l = m.History.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Ping != nil { + l = m.Ping.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Send != nil { + l = m.Send.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Rpc != nil { + l = m.Rpc.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Refresh != nil { + l = m.Refresh.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.SubRefresh != nil { + l = m.SubRefresh.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Reply) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Id)) + } + if m.Error != nil { + l = m.Error.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Push != nil { + l = m.Push.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Connect != nil { + l = m.Connect.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Subscribe != nil { + l = m.Subscribe.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Unsubscribe != nil { + l = m.Unsubscribe.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Publish != nil { + l = m.Publish.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Presence != nil { + l = m.Presence.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.PresenceStats != nil { + l = m.PresenceStats.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.History != nil { + l = m.History.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Ping != nil { + l = m.Ping.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Rpc != nil { + l = m.Rpc.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Refresh != nil { + l = m.Refresh.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.SubRefresh != nil { + l = m.SubRefresh.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Push) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Channel) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Pub != nil { + l = m.Pub.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Join != nil { + l = m.Join.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Leave != nil { + l = m.Leave.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Unsubscribe != nil { + l = m.Unsubscribe.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Message != nil { + l = m.Message.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Subscribe != nil { + l = m.Subscribe.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Connect != nil { + l = m.Connect.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Disconnect != nil { + l = m.Disconnect.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Refresh != nil { + l = m.Refresh.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *ClientInfo) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.User) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.Client) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.ConnInfo) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.ChanInfo) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Publication) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Info != nil { + l = m.Info.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Offset != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Offset)) + } + if len(m.Tags) > 0 { + for k, v := range m.Tags { + _ = k + _ = v + mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protohelpers.SizeOfVarint(uint64(len(v))) + n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) + } + } + if m.Delta { + n += 2 + } + if m.Time != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Time)) + } + l = len(m.Channel) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Join) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Info != nil { + l = m.Info.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Leave) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Info != nil { + l = m.Info.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Unsubscribe) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Code != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Code)) + } + l = len(m.Reason) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Subscribe) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Recoverable { + n += 2 + } + l = len(m.Epoch) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Offset != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Offset)) + } + if m.Positioned { + n += 2 + } + l = len(m.Data) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Message) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Connect) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Client) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.Version) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if len(m.Subs) > 0 { + for k, v := range m.Subs { + _ = k + _ = v + l = 0 + if v != nil { + l = v.SizeVT() + } + l += 1 + protohelpers.SizeOfVarint(uint64(l)) + mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + l + n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) + } + } + if m.Expires { + n += 2 + } + if m.Ttl != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Ttl)) + } + if m.Ping != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Ping)) + } + if m.Pong { + n += 2 + } + l = len(m.Session) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.Node) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Time != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Time)) + } + n += len(m.unknownFields) + return n +} + +func (m *Disconnect) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Code != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Code)) + } + l = len(m.Reason) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Reconnect { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *Refresh) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Expires { + n += 2 + } + if m.Ttl != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Ttl)) + } + n += len(m.unknownFields) + return n +} + +func (m *ConnectRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Token) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if len(m.Subs) > 0 { + for k, v := range m.Subs { + _ = k + _ = v + l = 0 + if v != nil { + l = v.SizeVT() + } + l += 1 + protohelpers.SizeOfVarint(uint64(l)) + mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + l + n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) + } + } + l = len(m.Name) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.Version) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if len(m.Headers) > 0 { + for k, v := range m.Headers { + _ = k + _ = v + mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protohelpers.SizeOfVarint(uint64(len(v))) + n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *ConnectResult) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Client) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.Version) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Expires { + n += 2 + } + if m.Ttl != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Ttl)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if len(m.Subs) > 0 { + for k, v := range m.Subs { + _ = k + _ = v + l = 0 + if v != nil { + l = v.SizeVT() + } + l += 1 + protohelpers.SizeOfVarint(uint64(l)) + mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + l + n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) + } + } + if m.Ping != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Ping)) + } + if m.Pong { + n += 2 + } + l = len(m.Session) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.Node) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Time != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Time)) + } + n += len(m.unknownFields) + return n +} + +func (m *RefreshRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Token) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *RefreshResult) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Client) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.Version) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Expires { + n += 2 + } + if m.Ttl != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Ttl)) + } + n += len(m.unknownFields) + return n +} + +func (m *SubscribeRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Channel) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.Token) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Recover { + n += 2 + } + l = len(m.Epoch) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Offset != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Offset)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Positioned { + n += 2 + } + if m.Recoverable { + n += 2 + } + if m.JoinLeave { + n += 2 + } + l = len(m.Delta) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *SubscribeResult) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Expires { + n += 2 + } + if m.Ttl != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Ttl)) + } + if m.Recoverable { + n += 2 + } + l = len(m.Epoch) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if len(m.Publications) > 0 { + for _, e := range m.Publications { + l = e.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + } + if m.Recovered { + n += 2 + } + if m.Offset != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Offset)) + } + if m.Positioned { + n += 2 + } + l = len(m.Data) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.WasRecovering { + n += 2 + } + if m.Delta { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *SubRefreshRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Channel) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.Token) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *SubRefreshResult) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Expires { + n += 2 + } + if m.Ttl != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Ttl)) + } + n += len(m.unknownFields) + return n +} + +func (m *UnsubscribeRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Channel) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *UnsubscribeResult) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + n += len(m.unknownFields) + return n +} + +func (m *PublishRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Channel) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *PublishResult) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + n += len(m.unknownFields) + return n +} + +func (m *PresenceRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Channel) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *PresenceResult) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Presence) > 0 { + for k, v := range m.Presence { + _ = k + _ = v + l = 0 + if v != nil { + l = v.SizeVT() + } + l += 1 + protohelpers.SizeOfVarint(uint64(l)) + mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + l + n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *PresenceStatsRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Channel) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *PresenceStatsResult) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.NumClients != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.NumClients)) + } + if m.NumUsers != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.NumUsers)) + } + n += len(m.unknownFields) + return n +} + +func (m *StreamPosition) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Offset != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Offset)) + } + l = len(m.Epoch) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *HistoryRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Channel) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Limit != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Limit)) + } + if m.Since != nil { + l = m.Since.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Reverse { + n += 2 + } + n += len(m.unknownFields) + return n +} + +func (m *HistoryResult) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Publications) > 0 { + for _, e := range m.Publications { + l = e.SizeVT() + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + } + l = len(m.Epoch) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + if m.Offset != 0 { + n += 1 + protohelpers.SizeOfVarint(uint64(m.Offset)) + } + n += len(m.unknownFields) + return n +} + +func (m *PingRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + n += len(m.unknownFields) + return n +} + +func (m *PingResult) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + n += len(m.unknownFields) + return n +} + +func (m *RPCRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.Method) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *RPCResult) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *SendRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *Error) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Error: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Error: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Code", wireType) + } + m.Code = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Code |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Message = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Temporary", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Temporary = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EmulationRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EmulationRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EmulationRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Node", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Node = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Session", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Session = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Command) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Command: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Command: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Connect", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Connect == nil { + m.Connect = &ConnectRequest{} + } + if err := m.Connect.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Subscribe", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Subscribe == nil { + m.Subscribe = &SubscribeRequest{} + } + if err := m.Subscribe.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Unsubscribe", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Unsubscribe == nil { + m.Unsubscribe = &UnsubscribeRequest{} + } + if err := m.Unsubscribe.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Publish", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Publish == nil { + m.Publish = &PublishRequest{} + } + if err := m.Publish.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Presence", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Presence == nil { + m.Presence = &PresenceRequest{} + } + if err := m.Presence.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PresenceStats", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.PresenceStats == nil { + m.PresenceStats = &PresenceStatsRequest{} + } + if err := m.PresenceStats.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field History", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.History == nil { + m.History = &HistoryRequest{} + } + if err := m.History.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Ping", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Ping == nil { + m.Ping = &PingRequest{} + } + if err := m.Ping.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Send", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Send == nil { + m.Send = &SendRequest{} + } + if err := m.Send.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rpc", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Rpc == nil { + m.Rpc = &RPCRequest{} + } + if err := m.Rpc.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 14: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Refresh", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Refresh == nil { + m.Refresh = &RefreshRequest{} + } + if err := m.Refresh.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SubRefresh", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SubRefresh == nil { + m.SubRefresh = &SubRefreshRequest{} + } + if err := m.SubRefresh.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Reply) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Reply: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Reply: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Error == nil { + m.Error = &Error{} + } + if err := m.Error.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Push", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Push == nil { + m.Push = &Push{} + } + if err := m.Push.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Connect", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Connect == nil { + m.Connect = &ConnectResult{} + } + if err := m.Connect.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Subscribe", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Subscribe == nil { + m.Subscribe = &SubscribeResult{} + } + if err := m.Subscribe.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Unsubscribe", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Unsubscribe == nil { + m.Unsubscribe = &UnsubscribeResult{} + } + if err := m.Unsubscribe.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Publish", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Publish == nil { + m.Publish = &PublishResult{} + } + if err := m.Publish.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Presence", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Presence == nil { + m.Presence = &PresenceResult{} + } + if err := m.Presence.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PresenceStats", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.PresenceStats == nil { + m.PresenceStats = &PresenceStatsResult{} + } + if err := m.PresenceStats.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field History", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.History == nil { + m.History = &HistoryResult{} + } + if err := m.History.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Ping", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Ping == nil { + m.Ping = &PingResult{} + } + if err := m.Ping.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rpc", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Rpc == nil { + m.Rpc = &RPCResult{} + } + if err := m.Rpc.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 14: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Refresh", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Refresh == nil { + m.Refresh = &RefreshResult{} + } + if err := m.Refresh.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SubRefresh", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SubRefresh == nil { + m.SubRefresh = &SubRefreshResult{} + } + if err := m.SubRefresh.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Push) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Push: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Push: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Channel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Channel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pub", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pub == nil { + m.Pub = &Publication{} + } + if err := m.Pub.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Join", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Join == nil { + m.Join = &Join{} + } + if err := m.Join.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Leave", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Leave == nil { + m.Leave = &Leave{} + } + if err := m.Leave.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Unsubscribe", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Unsubscribe == nil { + m.Unsubscribe = &Unsubscribe{} + } + if err := m.Unsubscribe.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Message == nil { + m.Message = &Message{} + } + if err := m.Message.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Subscribe", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Subscribe == nil { + m.Subscribe = &Subscribe{} + } + if err := m.Subscribe.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Connect", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Connect == nil { + m.Connect = &Connect{} + } + if err := m.Connect.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Disconnect", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Disconnect == nil { + m.Disconnect = &Disconnect{} + } + if err := m.Disconnect.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Refresh", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Refresh == nil { + m.Refresh = &Refresh{} + } + if err := m.Refresh.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ClientInfo) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ClientInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ClientInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field User", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.User = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Client", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Client = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnInfo", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConnInfo = append(m.ConnInfo[:0], dAtA[iNdEx:postIndex]...) + if m.ConnInfo == nil { + m.ConnInfo = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChanInfo", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChanInfo = append(m.ChanInfo[:0], dAtA[iNdEx:postIndex]...) + if m.ChanInfo == nil { + m.ChanInfo = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Publication) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Publication: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Publication: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Info", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Info == nil { + m.Info = &ClientInfo{} + } + if err := m.Info.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Offset", wireType) + } + m.Offset = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Offset |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tags", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Tags == nil { + m.Tags = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protohelpers.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protohelpers.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protohelpers.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protohelpers.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Tags[mapkey] = mapvalue + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Delta", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Delta = bool(v != 0) + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType) + } + m.Time = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Time |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Channel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Channel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Join) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Join: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Join: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Info", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Info == nil { + m.Info = &ClientInfo{} + } + if err := m.Info.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Leave) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Leave: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Leave: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Info", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Info == nil { + m.Info = &ClientInfo{} + } + if err := m.Info.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Unsubscribe) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Unsubscribe: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Unsubscribe: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Code", wireType) + } + m.Code = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Code |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Reason", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Reason = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Subscribe) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Subscribe: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Subscribe: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Recoverable", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Recoverable = bool(v != 0) + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Epoch", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Epoch = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Offset", wireType) + } + m.Offset = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Offset |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Positioned", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Positioned = bool(v != 0) + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Message) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Message: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Message: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Connect) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Connect: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Connect: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Client", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Client = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Version = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Subs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Subs == nil { + m.Subs = make(map[string]*SubscribeResult) + } + var mapkey string + var mapvalue *SubscribeResult + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protohelpers.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protohelpers.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return protohelpers.ErrInvalidLength + } + postmsgIndex := iNdEx + mapmsglen + if postmsgIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &SubscribeResult{} + if err := mapvalue.UnmarshalVT(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Subs[mapkey] = mapvalue + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Expires", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Expires = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ttl", wireType) + } + m.Ttl = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Ttl |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ping", wireType) + } + m.Ping = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Ping |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pong", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Pong = bool(v != 0) + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Session", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Session = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Node", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Node = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType) + } + m.Time = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Time |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Disconnect) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Disconnect: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Disconnect: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Code", wireType) + } + m.Code = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Code |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Reason", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Reason = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Reconnect", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Reconnect = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Refresh) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Refresh: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Refresh: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Expires", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Expires = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ttl", wireType) + } + m.Ttl = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Ttl |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConnectRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ConnectRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConnectRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Token", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Token = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Subs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Subs == nil { + m.Subs = make(map[string]*SubscribeRequest) + } + var mapkey string + var mapvalue *SubscribeRequest + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protohelpers.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protohelpers.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return protohelpers.ErrInvalidLength + } + postmsgIndex := iNdEx + mapmsglen + if postmsgIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &SubscribeRequest{} + if err := mapvalue.UnmarshalVT(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Subs[mapkey] = mapvalue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Version = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Headers", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Headers == nil { + m.Headers = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protohelpers.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protohelpers.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return protohelpers.ErrInvalidLength + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return protohelpers.ErrInvalidLength + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Headers[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConnectResult) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ConnectResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConnectResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Client", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Client = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Version = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Expires", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Expires = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ttl", wireType) + } + m.Ttl = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Ttl |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Subs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Subs == nil { + m.Subs = make(map[string]*SubscribeResult) + } + var mapkey string + var mapvalue *SubscribeResult + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protohelpers.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protohelpers.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return protohelpers.ErrInvalidLength + } + postmsgIndex := iNdEx + mapmsglen + if postmsgIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &SubscribeResult{} + if err := mapvalue.UnmarshalVT(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Subs[mapkey] = mapvalue + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ping", wireType) + } + m.Ping = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Ping |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pong", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Pong = bool(v != 0) + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Session", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Session = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Node", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Node = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType) + } + m.Time = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Time |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RefreshRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RefreshRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RefreshRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Token", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Token = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RefreshResult) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RefreshResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RefreshResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Client", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Client = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Version = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Expires", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Expires = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ttl", wireType) + } + m.Ttl = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Ttl |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SubscribeRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SubscribeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SubscribeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Channel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Channel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Token", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Token = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Recover", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Recover = bool(v != 0) + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Epoch", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Epoch = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Offset", wireType) + } + m.Offset = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Offset |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Positioned", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Positioned = bool(v != 0) + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Recoverable", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Recoverable = bool(v != 0) + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field JoinLeave", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.JoinLeave = bool(v != 0) + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Delta", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Delta = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SubscribeResult) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SubscribeResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SubscribeResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Expires", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Expires = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ttl", wireType) + } + m.Ttl = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Ttl |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Recoverable", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Recoverable = bool(v != 0) + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Epoch", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Epoch = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Publications", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Publications = append(m.Publications, &Publication{}) + if err := m.Publications[len(m.Publications)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Recovered", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Recovered = bool(v != 0) + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Offset", wireType) + } + m.Offset = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Offset |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Positioned", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Positioned = bool(v != 0) + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 12: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field WasRecovering", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.WasRecovering = bool(v != 0) + case 13: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Delta", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Delta = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SubRefreshRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SubRefreshRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SubRefreshRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Channel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Channel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Token", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Token = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SubRefreshResult) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SubRefreshResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SubRefreshResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Expires", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Expires = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ttl", wireType) + } + m.Ttl = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Ttl |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UnsubscribeRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UnsubscribeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UnsubscribeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Channel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Channel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UnsubscribeResult) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UnsubscribeResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UnsubscribeResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PublishRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PublishRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PublishRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Channel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Channel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PublishResult) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PublishResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PublishResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PresenceRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PresenceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PresenceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Channel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Channel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PresenceResult) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PresenceResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PresenceResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Presence", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Presence == nil { + m.Presence = make(map[string]*ClientInfo) + } + var mapkey string + var mapvalue *ClientInfo + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return protohelpers.ErrInvalidLength + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return protohelpers.ErrInvalidLength + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return protohelpers.ErrInvalidLength + } + postmsgIndex := iNdEx + mapmsglen + if postmsgIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &ClientInfo{} + if err := mapvalue.UnmarshalVT(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Presence[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PresenceStatsRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PresenceStatsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PresenceStatsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Channel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Channel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PresenceStatsResult) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PresenceStatsResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PresenceStatsResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NumClients", wireType) + } + m.NumClients = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NumClients |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NumUsers", wireType) + } + m.NumUsers = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NumUsers |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StreamPosition) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: StreamPosition: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StreamPosition: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Offset", wireType) + } + m.Offset = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Offset |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Epoch", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Epoch = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *HistoryRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: HistoryRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HistoryRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Channel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Channel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + m.Limit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Since", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Since == nil { + m.Since = &StreamPosition{} + } + if err := m.Since.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Reverse", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Reverse = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *HistoryResult) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: HistoryResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HistoryResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Publications", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Publications = append(m.Publications, &Publication{}) + if err := m.Publications[len(m.Publications)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Epoch", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Epoch = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Offset", wireType) + } + m.Offset = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Offset |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PingRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PingRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PingRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PingResult) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PingResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PingResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RPCRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RPCRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RPCRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Method", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Method = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RPCResult) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RPCResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RPCResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SendRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SendRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SendRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := protohelpers.Skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protohelpers.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} diff --git a/vendor/github.com/centrifugal/protocol/decode.go b/vendor/github.com/centrifugal/protocol/decode.go new file mode 100644 index 0000000000..199e03c9ae --- /dev/null +++ b/vendor/github.com/centrifugal/protocol/decode.go @@ -0,0 +1,217 @@ +package protocol + +import ( + "bytes" + "encoding/binary" + "io" + + "github.com/segmentio/encoding/json" +) + +// CommandDecoder ... +type CommandDecoder interface { + Reset([]byte) error + Decode() (*Command, error) +} + +// JSONCommandDecoder ... +type JSONCommandDecoder struct { + data []byte + messageCount int + prevNewLine int + numMessagesRead int +} + +// NewJSONCommandDecoder ... +func NewJSONCommandDecoder(data []byte) *JSONCommandDecoder { + // Protocol message must be separated by exactly one `\n`. + messageCount := bytes.Count(data, []byte("\n")) + 1 + if len(data) == 0 || data[len(data)-1] == '\n' { + // Protocol message must have zero or one `\n` at the end. + messageCount-- + } + return &JSONCommandDecoder{ + data: data, + messageCount: messageCount, + prevNewLine: 0, + numMessagesRead: 0, + } +} + +// Reset ... +func (d *JSONCommandDecoder) Reset(data []byte) error { + // We have a strict contract that protocol messages should be separated by at most one `\n`. + messageCount := bytes.Count(data, []byte("\n")) + 1 + if len(data) == 0 || data[len(data)-1] == '\n' { + // We have a strict contract that protocol message should use at most one `\n` at the end. + messageCount-- + } + d.data = data + d.messageCount = messageCount + d.prevNewLine = 0 + d.numMessagesRead = 0 + return nil +} + +// Decode ... +func (d *JSONCommandDecoder) Decode() (*Command, error) { + if d.messageCount == 0 { + return nil, io.ErrUnexpectedEOF + } + var c Command + if d.messageCount == 1 { + _, err := json.Parse(d.data, &c, json.ZeroCopy) + if err != nil { + return nil, err + } + return &c, io.EOF + } + var nextNewLine int + if d.numMessagesRead == d.messageCount-1 { + // Last message, no need to search for a new line. + nextNewLine = len(d.data[d.prevNewLine:]) + } else if len(d.data) > d.prevNewLine { + nextNewLine = bytes.Index(d.data[d.prevNewLine:], []byte("\n")) + if nextNewLine < 0 { + return nil, io.ErrShortBuffer + } + } else { + return nil, io.ErrShortBuffer + } + if len(d.data) >= d.prevNewLine+nextNewLine { + _, err := json.Parse(d.data[d.prevNewLine:d.prevNewLine+nextNewLine], &c, json.ZeroCopy) + if err != nil { + return nil, err + } + d.numMessagesRead++ + d.prevNewLine = d.prevNewLine + nextNewLine + 1 + if d.numMessagesRead == d.messageCount { + return &c, io.EOF + } + return &c, nil + } else { + return nil, io.ErrShortBuffer + } +} + +// ProtobufCommandDecoder ... +type ProtobufCommandDecoder struct { + data []byte + offset int +} + +// NewProtobufCommandDecoder ... +func NewProtobufCommandDecoder(data []byte) *ProtobufCommandDecoder { + return &ProtobufCommandDecoder{ + data: data, + } +} + +// Reset ... +func (d *ProtobufCommandDecoder) Reset(data []byte) error { + d.data = data + d.offset = 0 + return nil +} + +// Decode ... +func (d *ProtobufCommandDecoder) Decode() (*Command, error) { + if d.offset < len(d.data) { + var c Command + l, n := binary.Uvarint(d.data[d.offset:]) + if n <= 0 { + return nil, io.EOF + } + from := d.offset + n + to := d.offset + n + int(l) + if to > 0 && to <= len(d.data) { + cmdBytes := d.data[from:to] + err := c.UnmarshalVT(cmdBytes) // Check whether UnmarshalVTUnsafe here is OK. + if err != nil { + return nil, err + } + d.offset = to + if d.offset == len(d.data) { + err = io.EOF + } + return &c, err + } else { + return nil, io.ErrShortBuffer + } + } + return nil, io.EOF +} + +// ReplyDecoder ... +type ReplyDecoder interface { + Reset([]byte) error + Decode() (*Reply, error) +} + +var _ ReplyDecoder = NewJSONReplyDecoder(nil) + +// JSONReplyDecoder ... +type JSONReplyDecoder struct { + decoder *json.Decoder +} + +// NewJSONReplyDecoder ... +func NewJSONReplyDecoder(data []byte) *JSONReplyDecoder { + return &JSONReplyDecoder{ + decoder: json.NewDecoder(bytes.NewReader(data)), + } +} + +// Reset ... +func (d *JSONReplyDecoder) Reset(data []byte) error { + d.decoder = json.NewDecoder(bytes.NewReader(data)) + return nil +} + +// Decode ... +func (d *JSONReplyDecoder) Decode() (*Reply, error) { + var c Reply + err := d.decoder.Decode(&c) + if err != nil { + return nil, err + } + return &c, nil +} + +var _ ReplyDecoder = NewProtobufReplyDecoder(nil) + +// ProtobufReplyDecoder ... +type ProtobufReplyDecoder struct { + data []byte + offset int +} + +// NewProtobufReplyDecoder ... +func NewProtobufReplyDecoder(data []byte) *ProtobufReplyDecoder { + return &ProtobufReplyDecoder{ + data: data, + } +} + +// Reset ... +func (d *ProtobufReplyDecoder) Reset(data []byte) error { + d.data = data + d.offset = 0 + return nil +} + +// Decode ... +func (d *ProtobufReplyDecoder) Decode() (*Reply, error) { + if d.offset < len(d.data) { + var c Reply + l, n := binary.Uvarint(d.data[d.offset:]) + replyBytes := d.data[d.offset+n : d.offset+n+int(l)] + err := c.UnmarshalVT(replyBytes) + if err != nil { + return nil, err + } + d.offset = d.offset + n + int(l) + return &c, nil + } + return nil, io.EOF +} diff --git a/vendor/github.com/centrifugal/protocol/decode_stream.go b/vendor/github.com/centrifugal/protocol/decode_stream.go new file mode 100644 index 0000000000..99b5e6403e --- /dev/null +++ b/vendor/github.com/centrifugal/protocol/decode_stream.go @@ -0,0 +1,161 @@ +package protocol + +import ( + "bufio" + "encoding/binary" + "errors" + "io" + "sync" + + "github.com/segmentio/encoding/json" +) + +var ( + streamJsonCommandDecoderPool sync.Pool + streamProtobufCommandDecoderPool sync.Pool +) + +func GetStreamCommandDecoder(protoType Type, reader io.Reader) StreamCommandDecoder { + return GetStreamCommandDecoderLimited(protoType, reader, 0) +} + +func GetStreamCommandDecoderLimited(protoType Type, reader io.Reader, messageSizeLimit int64) StreamCommandDecoder { + if protoType == TypeJSON { + e := streamJsonCommandDecoderPool.Get() + if e == nil { + return NewJSONStreamCommandDecoder(reader, messageSizeLimit) + } + commandDecoder := e.(*JSONStreamCommandDecoder) + commandDecoder.Reset(reader, messageSizeLimit) + return commandDecoder + } + e := streamProtobufCommandDecoderPool.Get() + if e == nil { + return NewProtobufStreamCommandDecoder(reader, messageSizeLimit) + } + commandDecoder := e.(*ProtobufStreamCommandDecoder) + commandDecoder.Reset(reader, messageSizeLimit) + return commandDecoder +} + +func PutStreamCommandDecoder(protoType Type, e StreamCommandDecoder) { + e.Reset(nil, 0) + if protoType == TypeJSON { + streamJsonCommandDecoderPool.Put(e) + return + } + streamProtobufCommandDecoderPool.Put(e) +} + +type StreamCommandDecoder interface { + Decode() (*Command, int, error) + Reset(reader io.Reader, messageSizeLimit int64) +} + +// ErrMessageTooLarge for when the message exceeds the limit. +var ErrMessageTooLarge = errors.New("message size exceeds the limit") + +type JSONStreamCommandDecoder struct { + reader *bufio.Reader + limitedReader *io.LimitedReader + messageSizeLimit int64 +} + +func NewJSONStreamCommandDecoder(reader io.Reader, messageSizeLimit int64) *JSONStreamCommandDecoder { + var limitedReader *io.LimitedReader + var bufioReader *bufio.Reader + if messageSizeLimit > 0 { + limitedReader = &io.LimitedReader{R: reader, N: messageSizeLimit + 1} + bufioReader = bufio.NewReader(limitedReader) + } else { + bufioReader = bufio.NewReader(reader) + } + return &JSONStreamCommandDecoder{ + reader: bufioReader, + limitedReader: limitedReader, + messageSizeLimit: messageSizeLimit, + } +} + +func (d *JSONStreamCommandDecoder) Decode() (*Command, int, error) { + if d.messageSizeLimit > 0 { + d.limitedReader.N = int64(d.messageSizeLimit) + 1 + } + cmdBytes, err := d.reader.ReadBytes('\n') + if err != nil { + if d.messageSizeLimit > 0 && int64(len(cmdBytes)) > d.messageSizeLimit { + return nil, 0, ErrMessageTooLarge + } + if err == io.EOF && len(cmdBytes) > 0 { + var c Command + _, parseErr := json.Parse(cmdBytes, &c, 0) + if parseErr != nil { + return nil, 0, parseErr + } + return &c, len(cmdBytes), err + } + return nil, 0, err + } + + var c Command + _, err = json.Parse(cmdBytes, &c, 0) + if err != nil { + return nil, 0, err + } + return &c, len(cmdBytes), nil +} + +func (d *JSONStreamCommandDecoder) Reset(reader io.Reader, messageSizeLimit int64) { + d.messageSizeLimit = messageSizeLimit + if messageSizeLimit > 0 { + limitedReader := &io.LimitedReader{R: reader, N: messageSizeLimit + 1} + bufioReader := bufio.NewReader(limitedReader) + d.limitedReader = limitedReader + d.reader.Reset(bufioReader) + } else { + d.limitedReader = nil + d.reader.Reset(reader) + } +} + +type ProtobufStreamCommandDecoder struct { + reader *bufio.Reader + messageSizeLimit int64 +} + +func NewProtobufStreamCommandDecoder(reader io.Reader, messageSizeLimit int64) *ProtobufStreamCommandDecoder { + return &ProtobufStreamCommandDecoder{reader: bufio.NewReader(reader), messageSizeLimit: messageSizeLimit} +} + +func (d *ProtobufStreamCommandDecoder) Decode() (*Command, int, error) { + msgLength, err := binary.ReadUvarint(d.reader) + if err != nil { + return nil, 0, err + } + + if d.messageSizeLimit > 0 && msgLength > uint64(d.messageSizeLimit) { + return nil, 0, ErrMessageTooLarge + } + + bb := getByteBuffer(int(msgLength)) + defer putByteBuffer(bb) + + n, err := io.ReadFull(d.reader, bb.B[:int(msgLength)]) + if err != nil { + return nil, 0, err + } + if uint64(n) != msgLength { + return nil, 0, io.ErrShortBuffer + } + var c Command + err = c.UnmarshalVT(bb.B[:int(msgLength)]) // Note, UnmarshalVTUnsafe here will result into issues. + if err != nil { + return nil, 0, err + } + return &c, int(msgLength) + 8, nil +} + +func (d *ProtobufStreamCommandDecoder) Reset(reader io.Reader, messageSizeLimit int64) { + d.messageSizeLimit = messageSizeLimit + d.reader.Reset(reader) +} diff --git a/vendor/github.com/centrifugal/protocol/encode.go b/vendor/github.com/centrifugal/protocol/encode.go new file mode 100644 index 0000000000..021f4805f5 --- /dev/null +++ b/vendor/github.com/centrifugal/protocol/encode.go @@ -0,0 +1,609 @@ +package protocol + +import ( + "bytes" + "encoding/binary" + "errors" + fastJSON "github.com/segmentio/encoding/json" +) + +var errInvalidJSON = errors.New("invalid JSON data") + +// checks that JSON is valid. +func isValidJSON(b []byte) error { + if b == nil { + return nil + } + if !fastJSON.Valid(b) { + return errInvalidJSON + } + return nil +} + +// PushEncoder ... +type PushEncoder interface { + Encode(*Push) ([]byte, error) + EncodeMessage(*Message, ...[]byte) ([]byte, error) + EncodePublication(*Publication, ...[]byte) ([]byte, error) + EncodeJoin(*Join, ...[]byte) ([]byte, error) + EncodeLeave(*Leave, ...[]byte) ([]byte, error) + EncodeUnsubscribe(*Unsubscribe, ...[]byte) ([]byte, error) + EncodeSubscribe(*Subscribe, ...[]byte) ([]byte, error) + EncodeConnect(*Connect, ...[]byte) ([]byte, error) + EncodeDisconnect(*Disconnect, ...[]byte) ([]byte, error) + EncodeRefresh(*Refresh, ...[]byte) ([]byte, error) +} + +var _ PushEncoder = (*JSONPushEncoder)(nil) +var _ PushEncoder = (*ProtobufPushEncoder)(nil) + +// JSONPushEncoder ... +type JSONPushEncoder struct { +} + +// NewJSONPushEncoder ... +func NewJSONPushEncoder() *JSONPushEncoder { + return &JSONPushEncoder{} +} + +// Encode Push to bytes. +func (e *JSONPushEncoder) Encode(message *Push) ([]byte, error) { + jw := newWriter() + message.MarshalEasyJSON(jw) + res, err := jw.BuildBytes() + if err != nil { + return nil, err + } + if err := isValidJSON(res); err != nil { + return nil, err + } + return res, nil +} + +// EncodePublication to bytes. +func (e *JSONPushEncoder) EncodePublication(message *Publication, reuse ...[]byte) ([]byte, error) { + jw := newWriter() + message.MarshalEasyJSON(jw) + return jw.BuildBytes(reuse...) +} + +// EncodeMessage to bytes. +func (e *JSONPushEncoder) EncodeMessage(message *Message, reuse ...[]byte) ([]byte, error) { + jw := newWriter() + message.MarshalEasyJSON(jw) + return jw.BuildBytes(reuse...) +} + +// EncodeJoin to bytes. +func (e *JSONPushEncoder) EncodeJoin(message *Join, reuse ...[]byte) ([]byte, error) { + jw := newWriter() + message.MarshalEasyJSON(jw) + return jw.BuildBytes(reuse...) +} + +// EncodeLeave to bytes. +func (e *JSONPushEncoder) EncodeLeave(message *Leave, reuse ...[]byte) ([]byte, error) { + jw := newWriter() + message.MarshalEasyJSON(jw) + return jw.BuildBytes(reuse...) +} + +// EncodeUnsubscribe to bytes. +func (e *JSONPushEncoder) EncodeUnsubscribe(message *Unsubscribe, reuse ...[]byte) ([]byte, error) { + jw := newWriter() + message.MarshalEasyJSON(jw) + return jw.BuildBytes(reuse...) +} + +// EncodeSubscribe to bytes. +func (e *JSONPushEncoder) EncodeSubscribe(message *Subscribe, reuse ...[]byte) ([]byte, error) { + jw := newWriter() + message.MarshalEasyJSON(jw) + return jw.BuildBytes(reuse...) +} + +// EncodeConnect to bytes. +func (e *JSONPushEncoder) EncodeConnect(message *Connect, reuse ...[]byte) ([]byte, error) { + jw := newWriter() + message.MarshalEasyJSON(jw) + return jw.BuildBytes(reuse...) +} + +// EncodeDisconnect to bytes. +func (e *JSONPushEncoder) EncodeDisconnect(message *Disconnect, reuse ...[]byte) ([]byte, error) { + jw := newWriter() + message.MarshalEasyJSON(jw) + return jw.BuildBytes(reuse...) +} + +// EncodeRefresh to bytes. +func (e *JSONPushEncoder) EncodeRefresh(message *Refresh, reuse ...[]byte) ([]byte, error) { + jw := newWriter() + message.MarshalEasyJSON(jw) + return jw.BuildBytes(reuse...) +} + +// ProtobufPushEncoder ... +type ProtobufPushEncoder struct { +} + +// NewProtobufPushEncoder ... +func NewProtobufPushEncoder() *ProtobufPushEncoder { + return &ProtobufPushEncoder{} +} + +// Encode Push to bytes. +func (e *ProtobufPushEncoder) Encode(message *Push) ([]byte, error) { + return message.MarshalVT() +} + +// EncodePublication to bytes. +func (e *ProtobufPushEncoder) EncodePublication(message *Publication, reuse ...[]byte) ([]byte, error) { + if len(reuse) == 1 { + size := message.SizeVT() + if cap(reuse[0]) >= size { + n, err := message.MarshalToSizedBufferVT(reuse[0][:size]) + if err != nil { + return nil, err + } + return reuse[0][:n], nil + } + } + return message.MarshalVT() +} + +// EncodeMessage to bytes. +func (e *ProtobufPushEncoder) EncodeMessage(message *Message, reuse ...[]byte) ([]byte, error) { + if len(reuse) == 1 { + size := message.SizeVT() + if cap(reuse[0]) >= size { + n, err := message.MarshalToSizedBufferVT(reuse[0][:size]) + if err != nil { + return nil, err + } + return reuse[0][:n], nil + } + } + return message.MarshalVT() +} + +// EncodeJoin to bytes. +func (e *ProtobufPushEncoder) EncodeJoin(message *Join, reuse ...[]byte) ([]byte, error) { + if len(reuse) == 1 { + size := message.SizeVT() + if cap(reuse[0]) >= size { + n, err := message.MarshalToSizedBufferVT(reuse[0][:size]) + if err != nil { + return nil, err + } + return reuse[0][:n], nil + } + } + return message.MarshalVT() +} + +// EncodeLeave to bytes. +func (e *ProtobufPushEncoder) EncodeLeave(message *Leave, reuse ...[]byte) ([]byte, error) { + if len(reuse) == 1 { + size := message.SizeVT() + if cap(reuse[0]) >= size { + n, err := message.MarshalToSizedBufferVT(reuse[0][:size]) + if err != nil { + return nil, err + } + return reuse[0][:n], nil + } + } + return message.MarshalVT() +} + +// EncodeUnsubscribe to bytes. +func (e *ProtobufPushEncoder) EncodeUnsubscribe(message *Unsubscribe, reuse ...[]byte) ([]byte, error) { + if len(reuse) == 1 { + size := message.SizeVT() + if cap(reuse[0]) >= size { + n, err := message.MarshalToSizedBufferVT(reuse[0][:size]) + if err != nil { + return nil, err + } + return reuse[0][:n], nil + } + } + return message.MarshalVT() +} + +// EncodeSubscribe to bytes. +func (e *ProtobufPushEncoder) EncodeSubscribe(message *Subscribe, reuse ...[]byte) ([]byte, error) { + if len(reuse) == 1 { + size := message.SizeVT() + if cap(reuse[0]) >= size { + n, err := message.MarshalToSizedBufferVT(reuse[0][:size]) + if err != nil { + return nil, err + } + return reuse[0][:n], nil + } + } + return message.MarshalVT() +} + +// EncodeConnect to bytes. +func (e *ProtobufPushEncoder) EncodeConnect(message *Connect, reuse ...[]byte) ([]byte, error) { + if len(reuse) == 1 { + size := message.SizeVT() + if cap(reuse[0]) >= size { + n, err := message.MarshalToSizedBufferVT(reuse[0][:size]) + if err != nil { + return nil, err + } + return reuse[0][:n], nil + } + } + return message.MarshalVT() +} + +// EncodeDisconnect to bytes. +func (e *ProtobufPushEncoder) EncodeDisconnect(message *Disconnect, reuse ...[]byte) ([]byte, error) { + if len(reuse) == 1 { + size := message.SizeVT() + if cap(reuse[0]) >= size { + n, err := message.MarshalToSizedBufferVT(reuse[0][:size]) + if err != nil { + return nil, err + } + return reuse[0][:n], nil + } + } + return message.MarshalVT() +} + +// EncodeRefresh to bytes. +func (e *ProtobufPushEncoder) EncodeRefresh(message *Refresh, reuse ...[]byte) ([]byte, error) { + if len(reuse) == 1 { + size := message.SizeVT() + if cap(reuse[0]) >= size { + n, err := message.MarshalToSizedBufferVT(reuse[0][:size]) + if err != nil { + return nil, err + } + return reuse[0][:n], nil + } + } + return message.MarshalVT() +} + +// ReplyEncoder ... +type ReplyEncoder interface { + Encode(*Reply) ([]byte, error) +} + +// JSONReplyEncoder ... +type JSONReplyEncoder struct{} + +// NewJSONReplyEncoder ... +func NewJSONReplyEncoder() *JSONReplyEncoder { + return &JSONReplyEncoder{} +} + +// Encode Reply to bytes. +func (e *JSONReplyEncoder) Encode(r *Reply) ([]byte, error) { + jw := newWriter() + r.MarshalEasyJSON(jw) + result, err := jw.BuildBytes() + if err != nil { + return nil, err + } + if err := isValidJSON(result); err != nil { + return nil, err + } + return result, nil +} + +//func (e *JSONReplyEncoder) EncodeNoCopy(r *Reply, _ []byte) ([]byte, error) { +// // No copy is not supported for JSON encoding. Just use Encode method, ignore pre-allocated buffer. +// return e.Encode(r) +//} + +// ProtobufReplyEncoder ... +type ProtobufReplyEncoder struct{} + +// NewProtobufReplyEncoder ... +func NewProtobufReplyEncoder() *ProtobufReplyEncoder { + return &ProtobufReplyEncoder{} +} + +// Encode Reply to bytes. +func (e *ProtobufReplyEncoder) Encode(r *Reply) ([]byte, error) { + return r.MarshalVT() +} + +//// EncodeNoCopy Reply to bytes without making copy of buffer byte slice. +//func (e *ProtobufReplyEncoder) EncodeNoCopy(r *Reply, buf []byte) ([]byte, error) { +// size := r.SizeVT() +// n, err := r.MarshalToSizedBufferVT(buf[:size]) +// if err != nil { +// return nil, err +// } +// return buf[:n], nil +//} + +// DataEncoder ... +type DataEncoder interface { + Reset() + Encode([]byte) error + Finish() []byte +} + +// JSONDataEncoder ... +type JSONDataEncoder struct { + count int + buffer bytes.Buffer +} + +// NewJSONDataEncoder ... +func NewJSONDataEncoder() *JSONDataEncoder { + return &JSONDataEncoder{} +} + +// Reset ... +func (e *JSONDataEncoder) Reset() { + e.count = 0 + e.buffer.Reset() +} + +// Encode ... +func (e *JSONDataEncoder) Encode(data []byte) error { + if e.count > 0 { + e.buffer.WriteString("\n") + } + e.buffer.Write(data) + e.count++ + return nil +} + +// Finish ... +func (e *JSONDataEncoder) Finish() []byte { + data := e.buffer.Bytes() + dataCopy := make([]byte, len(data)) + copy(dataCopy, data) + return dataCopy +} + +// ProtobufDataEncoder ... +type ProtobufDataEncoder struct { + buffer bytes.Buffer +} + +// NewProtobufDataEncoder ... +func NewProtobufDataEncoder() *ProtobufDataEncoder { + return &ProtobufDataEncoder{} +} + +// Encode ... +func (e *ProtobufDataEncoder) Encode(data []byte) error { + bs := make([]byte, 8) + n := binary.PutUvarint(bs, uint64(len(data))) + e.buffer.Write(bs[:n]) + e.buffer.Write(data) + return nil +} + +// Reset ... +func (e *ProtobufDataEncoder) Reset() { + e.buffer.Reset() +} + +// Finish ... +func (e *ProtobufDataEncoder) Finish() []byte { + data := e.buffer.Bytes() + dataCopy := make([]byte, len(data)) + copy(dataCopy, data) + return dataCopy +} + +// ResultEncoder ... +type ResultEncoder interface { + EncodeConnectResult(*ConnectResult) ([]byte, error) + EncodeRefreshResult(*RefreshResult) ([]byte, error) + EncodeSubscribeResult(*SubscribeResult) ([]byte, error) + EncodeSubRefreshResult(*SubRefreshResult) ([]byte, error) + EncodeUnsubscribeResult(*UnsubscribeResult) ([]byte, error) + EncodePublishResult(*PublishResult) ([]byte, error) + EncodePresenceResult(*PresenceResult) ([]byte, error) + EncodePresenceStatsResult(*PresenceStatsResult) ([]byte, error) + EncodeHistoryResult(*HistoryResult) ([]byte, error) + EncodePingResult(*PingResult) ([]byte, error) + EncodeRPCResult(*RPCResult) ([]byte, error) +} + +// JSONResultEncoder ... +type JSONResultEncoder struct{} + +// NewJSONResultEncoder ... +func NewJSONResultEncoder() *JSONResultEncoder { + return &JSONResultEncoder{} +} + +// EncodeConnectResult ... +func (e *JSONResultEncoder) EncodeConnectResult(res *ConnectResult) ([]byte, error) { + jw := newWriter() + res.MarshalEasyJSON(jw) + return jw.BuildBytes() +} + +// EncodeRefreshResult ... +func (e *JSONResultEncoder) EncodeRefreshResult(res *RefreshResult) ([]byte, error) { + jw := newWriter() + res.MarshalEasyJSON(jw) + return jw.BuildBytes() +} + +// EncodeSubscribeResult ... +func (e *JSONResultEncoder) EncodeSubscribeResult(res *SubscribeResult) ([]byte, error) { + jw := newWriter() + res.MarshalEasyJSON(jw) + return jw.BuildBytes() +} + +// EncodeSubRefreshResult ... +func (e *JSONResultEncoder) EncodeSubRefreshResult(res *SubRefreshResult) ([]byte, error) { + jw := newWriter() + res.MarshalEasyJSON(jw) + return jw.BuildBytes() +} + +// EncodeUnsubscribeResult ... +func (e *JSONResultEncoder) EncodeUnsubscribeResult(res *UnsubscribeResult) ([]byte, error) { + jw := newWriter() + res.MarshalEasyJSON(jw) + return jw.BuildBytes() +} + +// EncodePublishResult ... +func (e *JSONResultEncoder) EncodePublishResult(res *PublishResult) ([]byte, error) { + jw := newWriter() + res.MarshalEasyJSON(jw) + return jw.BuildBytes() +} + +// EncodePresenceResult ... +func (e *JSONResultEncoder) EncodePresenceResult(res *PresenceResult) ([]byte, error) { + jw := newWriter() + res.MarshalEasyJSON(jw) + return jw.BuildBytes() +} + +// EncodePresenceStatsResult ... +func (e *JSONResultEncoder) EncodePresenceStatsResult(res *PresenceStatsResult) ([]byte, error) { + jw := newWriter() + res.MarshalEasyJSON(jw) + return jw.BuildBytes() +} + +// EncodeHistoryResult ... +func (e *JSONResultEncoder) EncodeHistoryResult(res *HistoryResult) ([]byte, error) { + jw := newWriter() + res.MarshalEasyJSON(jw) + return jw.BuildBytes() +} + +// EncodePingResult ... +func (e *JSONResultEncoder) EncodePingResult(res *PingResult) ([]byte, error) { + jw := newWriter() + res.MarshalEasyJSON(jw) + return jw.BuildBytes() +} + +// EncodeRPCResult ... +func (e *JSONResultEncoder) EncodeRPCResult(res *RPCResult) ([]byte, error) { + jw := newWriter() + res.MarshalEasyJSON(jw) + return jw.BuildBytes() +} + +// ProtobufResultEncoder ... +type ProtobufResultEncoder struct{} + +// NewProtobufResultEncoder ... +func NewProtobufResultEncoder() *ProtobufResultEncoder { + return &ProtobufResultEncoder{} +} + +// EncodeConnectResult ... +func (e *ProtobufResultEncoder) EncodeConnectResult(res *ConnectResult) ([]byte, error) { + return res.MarshalVT() +} + +// EncodeRefreshResult ... +func (e *ProtobufResultEncoder) EncodeRefreshResult(res *RefreshResult) ([]byte, error) { + return res.MarshalVT() +} + +// EncodeSubscribeResult ... +func (e *ProtobufResultEncoder) EncodeSubscribeResult(res *SubscribeResult) ([]byte, error) { + return res.MarshalVT() +} + +// EncodeSubRefreshResult ... +func (e *ProtobufResultEncoder) EncodeSubRefreshResult(res *SubRefreshResult) ([]byte, error) { + return res.MarshalVT() +} + +// EncodeUnsubscribeResult ... +func (e *ProtobufResultEncoder) EncodeUnsubscribeResult(res *UnsubscribeResult) ([]byte, error) { + return res.MarshalVT() +} + +// EncodePublishResult ... +func (e *ProtobufResultEncoder) EncodePublishResult(res *PublishResult) ([]byte, error) { + return res.MarshalVT() +} + +// EncodePresenceResult ... +func (e *ProtobufResultEncoder) EncodePresenceResult(res *PresenceResult) ([]byte, error) { + return res.MarshalVT() +} + +// EncodePresenceStatsResult ... +func (e *ProtobufResultEncoder) EncodePresenceStatsResult(res *PresenceStatsResult) ([]byte, error) { + return res.MarshalVT() +} + +// EncodeHistoryResult ... +func (e *ProtobufResultEncoder) EncodeHistoryResult(res *HistoryResult) ([]byte, error) { + return res.MarshalVT() +} + +// EncodePingResult ... +func (e *ProtobufResultEncoder) EncodePingResult(res *PingResult) ([]byte, error) { + return res.MarshalVT() +} + +// EncodeRPCResult ... +func (e *ProtobufResultEncoder) EncodeRPCResult(res *RPCResult) ([]byte, error) { + return res.MarshalVT() +} + +// CommandEncoder ... +type CommandEncoder interface { + Encode(cmd *Command) ([]byte, error) +} + +// JSONCommandEncoder ... +type JSONCommandEncoder struct { +} + +// NewJSONCommandEncoder ... +func NewJSONCommandEncoder() *JSONCommandEncoder { + return &JSONCommandEncoder{} +} + +// Encode ... +func (e *JSONCommandEncoder) Encode(cmd *Command) ([]byte, error) { + jw := newWriter() + cmd.MarshalEasyJSON(jw) + return jw.BuildBytes() +} + +// ProtobufCommandEncoder ... +type ProtobufCommandEncoder struct { +} + +// NewProtobufCommandEncoder ... +func NewProtobufCommandEncoder() *ProtobufCommandEncoder { + return &ProtobufCommandEncoder{} +} + +// Encode ... +func (e *ProtobufCommandEncoder) Encode(cmd *Command) ([]byte, error) { + commandBytes, err := cmd.MarshalVT() + if err != nil { + return nil, err + } + bs := make([]byte, 8) + n := binary.PutUvarint(bs, uint64(len(commandBytes))) + var buf bytes.Buffer + buf.Write(bs[:n]) + buf.Write(commandBytes) + return buf.Bytes(), nil +} diff --git a/vendor/github.com/centrifugal/protocol/encode_writer.go b/vendor/github.com/centrifugal/protocol/encode_writer.go new file mode 100644 index 0000000000..fb028e7923 --- /dev/null +++ b/vendor/github.com/centrifugal/protocol/encode_writer.go @@ -0,0 +1,396 @@ +package protocol + +import ( + "strconv" + "unicode/utf8" + + "github.com/valyala/bytebufferpool" +) + +// Flags describe various encoding options. The behavior may be actually implemented in the encoder, but +// Flags field in writer is used to set and pass them around. +type flags int + +const ( + nilMapAsEmpty flags = 1 << iota // Encode nil map as '{}' rather than 'null'. + nilSliceAsEmpty // Encode nil slice as '[]' rather than 'null'. +) + +// writer is a JSON writer. +type writer struct { + Buffer *bytebufferpool.ByteBuffer + Flags flags + Error error + NoEscapeHTML bool +} + +func newWriter() *writer { + return &writer{ + Buffer: bytebufferpool.Get(), + } +} + +// BuildBytes returns writer data as a single byte slice. +func (w *writer) BuildBytes(reuse ...[]byte) ([]byte, error) { + if w.Error != nil { + return nil, w.Error + } + var ret []byte + size := w.Buffer.Len() + // If we got a buffer as argument and it is big enough, reuse it. + if len(reuse) == 1 && cap(reuse[0]) >= size { + ret = reuse[0][:0] + } else { + ret = make([]byte, 0, size) + } + ret = append(ret, w.Buffer.Bytes()...) + bytebufferpool.Put(w.Buffer) + // Make writer non-usable after building bytes - writes will panic. + w.Buffer = nil + return ret, nil +} + +// BuildBytesNoCopy returns writer data as a single byte slice and returns function to call when data is no longer needed. +func (w *writer) BuildBytesNoCopy() ([]byte, error) { + if w.Error != nil { + return nil, w.Error + } + buffer := w.Buffer + // Make writer non-usable after building bytes - writes will panic. + w.Buffer = nil + return buffer.Bytes(), nil +} + +// RawByte appends raw binary data to the buffer. +func (w *writer) RawByte(c byte) { + _ = w.Buffer.WriteByte(c) +} + +// RawString appends string to the buffer. +func (w *writer) RawString(s string) { + _, _ = w.Buffer.WriteString(s) +} + +// Raw appends raw binary data to the buffer or sets the error if it is given. Useful for +// calling with results of MarshalJSON-like functions. +func (w *writer) Raw(src []byte, err error) { + switch { + case w.Error != nil: + return + case err != nil: + w.Error = err + case len(src) > 0: + _, _ = w.Buffer.Write(src) + default: + w.RawString("null") + } +} + +func (w *writer) Uint32(n uint32) { + _, _ = w.Buffer.WriteString(strconv.FormatUint(uint64(n), 10)) +} + +func (w *writer) Uint64(n uint64) { + _, _ = w.Buffer.WriteString(strconv.FormatUint(n, 10)) +} + +func (w *writer) Int32(n int32) { + _, _ = w.Buffer.WriteString(strconv.FormatInt(int64(n), 10)) +} + +func (w *writer) Int64(n int64) { + _, _ = w.Buffer.WriteString(strconv.FormatInt(n, 10)) +} + +func (w *writer) Bool(v bool) { + if v { + _, _ = w.Buffer.Write([]byte(`true`)) + } else { + _, _ = w.Buffer.Write([]byte(`false`)) + } +} + +var hex = "0123456789abcdef" + +func (w *writer) String(s string) { + escapeHTML := !w.NoEscapeHTML + _ = w.Buffer.WriteByte('"') + start := 0 + for i := 0; i < len(s); { + if b := s[i]; b < utf8.RuneSelf { + if htmlSafeSet[b] || (!escapeHTML && safeSet[b]) { + i++ + continue + } + if start < i { + _, _ = w.Buffer.WriteString(s[start:i]) + } + _ = w.Buffer.WriteByte('\\') + switch b { + case '\\', '"': + _ = w.Buffer.WriteByte(b) + case '\n': + _ = w.Buffer.WriteByte('n') + case '\r': + _ = w.Buffer.WriteByte('r') + case '\t': + _ = w.Buffer.WriteByte('t') + default: + // This encodes bytes < 0x20 except for \t, \n and \r. + // If escapeHTML is set, it also escapes <, >, and & + // because they can lead to security holes when + // user-controlled strings are rendered into JSON + // and served to some browsers. + _, _ = w.Buffer.WriteString(`u00`) + _ = w.Buffer.WriteByte(hex[b>>4]) + _ = w.Buffer.WriteByte(hex[b&0xF]) + } + i++ + start = i + continue + } + c, size := utf8.DecodeRuneInString(s[i:]) + if c == utf8.RuneError && size == 1 { + if start < i { + _, _ = w.Buffer.WriteString(s[start:i]) + } + _, _ = w.Buffer.WriteString(`\ufffd`) + i += size + start = i + continue + } + // U+2028 is LINE SEPARATOR. + // U+2029 is PARAGRAPH SEPARATOR. + // They are both technically valid characters in JSON strings, + // but don't work in JSONP, which has to be evaluated as JavaScript, + // and can lead to security holes there. It is valid JSON to + // escape them, so we do so unconditionally. + // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion. + if c == '\u2028' || c == '\u2029' { + if start < i { + _, _ = w.Buffer.WriteString(s[start:i]) + } + _, _ = w.Buffer.WriteString(`\u202`) + _ = w.Buffer.WriteByte(hex[c&0xF]) + i += size + start = i + continue + } + i += size + } + if start < len(s) { + _, _ = w.Buffer.WriteString(s[start:]) + } + _ = w.Buffer.WriteByte('"') +} + +// safeSet holds the value true if the ASCII character with the given array +// position can be represented inside a JSON string without any further +// escaping. +// +// All values are true except for the ASCII control characters (0-31), the +// double quote ("), and the backslash character ("\"). +var safeSet = [utf8.RuneSelf]bool{ + ' ': true, + '!': true, + '"': false, + '#': true, + '$': true, + '%': true, + '&': true, + '\'': true, + '(': true, + ')': true, + '*': true, + '+': true, + ',': true, + '-': true, + '.': true, + '/': true, + '0': true, + '1': true, + '2': true, + '3': true, + '4': true, + '5': true, + '6': true, + '7': true, + '8': true, + '9': true, + ':': true, + ';': true, + '<': true, + '=': true, + '>': true, + '?': true, + '@': true, + 'A': true, + 'B': true, + 'C': true, + 'D': true, + 'E': true, + 'F': true, + 'G': true, + 'H': true, + 'I': true, + 'J': true, + 'K': true, + 'L': true, + 'M': true, + 'N': true, + 'O': true, + 'P': true, + 'Q': true, + 'R': true, + 'S': true, + 'T': true, + 'U': true, + 'V': true, + 'W': true, + 'X': true, + 'Y': true, + 'Z': true, + '[': true, + '\\': false, + ']': true, + '^': true, + '_': true, + '`': true, + 'a': true, + 'b': true, + 'c': true, + 'd': true, + 'e': true, + 'f': true, + 'g': true, + 'h': true, + 'i': true, + 'j': true, + 'k': true, + 'l': true, + 'm': true, + 'n': true, + 'o': true, + 'p': true, + 'q': true, + 'r': true, + 's': true, + 't': true, + 'u': true, + 'v': true, + 'w': true, + 'x': true, + 'y': true, + 'z': true, + '{': true, + '|': true, + '}': true, + '~': true, + '\u007f': true, +} + +// htmlSafeSet holds the value true if the ASCII character with the given +// array position can be safely represented inside a JSON string, embedded +// inside of HTML