diff --git a/api_signaling.go b/api_signaling.go index 6ff48e89..7242da36 100644 --- a/api_signaling.go +++ b/api_signaling.go @@ -370,11 +370,17 @@ type ClientTypeInternalAuthParams struct { func (p *ClientTypeInternalAuthParams) CheckValid() error { if p.Backend == "" { return fmt.Errorf("backend missing") - } else if u, err := url.Parse(p.Backend); err != nil { + } + + if p.Backend[len(p.Backend)-1] != '/' { + p.Backend += "/" + } + if u, err := url.Parse(p.Backend); err != nil { return err } else { if strings.Contains(u.Host, ":") && hasStandardPort(u) { u.Host = u.Hostname() + p.Backend = u.String() } p.parsedBackend = u @@ -475,11 +481,17 @@ func (m *HelloClientMessage) CheckValid() error { case HelloClientTypeFederation: if m.Auth.Url == "" { return fmt.Errorf("url missing") - } else if u, err := url.ParseRequestURI(m.Auth.Url); err != nil { + } + + if m.Auth.Url[len(m.Auth.Url)-1] != '/' { + m.Auth.Url += "/" + } + if u, err := url.ParseRequestURI(m.Auth.Url); err != nil { return err } else { if strings.Contains(u.Host, ":") && hasStandardPort(u) { u.Host = u.Hostname() + m.Auth.Url = u.String() } m.Auth.parsedUrl = u diff --git a/backend_configuration.go b/backend_configuration.go index fa6b45b8..33d5eb8b 100644 --- a/backend_configuration.go +++ b/backend_configuration.go @@ -42,11 +42,9 @@ var ( ) type Backend struct { - id string - url string - parsedUrl *url.URL - secret []byte - compat bool + id string + urls []string + secret []byte allowHttp bool @@ -67,7 +65,7 @@ func (b *Backend) Secret() []byte { } func (b *Backend) IsCompat() bool { - return b.compat + return len(b.urls) == 0 } func (b *Backend) IsUrlAllowed(u *url.URL) bool { @@ -81,12 +79,23 @@ func (b *Backend) IsUrlAllowed(u *url.URL) bool { } } -func (b *Backend) Url() string { - return b.url +func (b *Backend) HasUrl(url string) bool { + if b.IsCompat() { + // Old-style configuration, only hosts are configured. + return true + } + + for _, u := range b.urls { + if strings.HasPrefix(url, u) { + return true + } + } + + return false } -func (b *Backend) ParsedUrl() *url.URL { - return b.parsedUrl +func (b *Backend) Urls() []string { + return b.urls } func (b *Backend) Limit() int { @@ -173,10 +182,7 @@ func (s *backendStorageCommon) getBackendLocked(u *url.URL) *Backend { continue } - if entry.url == "" { - // Old-style configuration, only hosts are configured. - return entry - } else if strings.HasPrefix(url, entry.url) { + if entry.HasUrl(url) { return entry } } diff --git a/backend_configuration_test.go b/backend_configuration_test.go index e467cbd8..0e612a20 100644 --- a/backend_configuration_test.go +++ b/backend_configuration_test.go @@ -491,7 +491,7 @@ func TestBackendConfiguration_Etcd(t *testing.T) { require.NoError(storage.WaitForInitialized(ctx)) if backends := sortBackends(cfg.GetBackends()); assert.Len(backends, 1) && - assert.Equal(url1, backends[0].url) && + assert.Equal([]string{url1}, backends[0].urls) && assert.Equal(initialSecret1, string(backends[0].secret)) { if backend := cfg.GetBackend(mustParse(url1)); backend != backends[0] { assert.Fail("Expected backend %+v, got %+v", backends[0], backend) @@ -502,7 +502,7 @@ func TestBackendConfiguration_Etcd(t *testing.T) { SetEtcdValue(etcd, "/backends/1_one", []byte("{\"url\":\""+url1+"\",\"secret\":\""+secret1+"\"}")) <-ch if backends := sortBackends(cfg.GetBackends()); assert.Len(backends, 1) && - assert.Equal(url1, backends[0].url) && + assert.Equal([]string{url1}, backends[0].urls) && assert.Equal(secret1, string(backends[0].secret)) { if backend := cfg.GetBackend(mustParse(url1)); backend != backends[0] { assert.Fail("Expected backend %+v, got %+v", backends[0], backend) @@ -516,9 +516,9 @@ func TestBackendConfiguration_Etcd(t *testing.T) { SetEtcdValue(etcd, "/backends/2_two", []byte("{\"url\":\""+url2+"\",\"secret\":\""+secret2+"\"}")) <-ch if backends := sortBackends(cfg.GetBackends()); assert.Len(backends, 2) && - assert.Equal(url1, backends[0].url) && + assert.Equal([]string{url1}, backends[0].urls) && assert.Equal(secret1, string(backends[0].secret)) && - assert.Equal(url2, backends[1].url) && + assert.Equal([]string{url2}, backends[1].urls) && assert.Equal(secret2, string(backends[1].secret)) { if backend := cfg.GetBackend(mustParse(url1)); backend != backends[0] { assert.Fail("Expected backend %+v, got %+v", backends[0], backend) @@ -534,11 +534,11 @@ func TestBackendConfiguration_Etcd(t *testing.T) { SetEtcdValue(etcd, "/backends/3_three", []byte("{\"url\":\""+url3+"\",\"secret\":\""+secret3+"\"}")) <-ch if backends := sortBackends(cfg.GetBackends()); assert.Len(backends, 3) && - assert.Equal(url1, backends[0].url) && + assert.Equal([]string{url1}, backends[0].urls) && assert.Equal(secret1, string(backends[0].secret)) && - assert.Equal(url2, backends[1].url) && + assert.Equal([]string{url2}, backends[1].urls) && assert.Equal(secret2, string(backends[1].secret)) && - assert.Equal(url3, backends[2].url) && + assert.Equal([]string{url3}, backends[2].urls) && assert.Equal(secret3, string(backends[2].secret)) { if backend := cfg.GetBackend(mustParse(url1)); backend != backends[0] { assert.Fail("Expected backend %+v, got %+v", backends[0], backend) @@ -553,9 +553,9 @@ func TestBackendConfiguration_Etcd(t *testing.T) { DeleteEtcdValue(etcd, "/backends/1_one") <-ch if backends := sortBackends(cfg.GetBackends()); assert.Len(backends, 2) { - assert.Equal(url2, backends[0].url) + assert.Equal([]string{url2}, backends[0].urls) assert.Equal(secret2, string(backends[0].secret)) - assert.Equal(url3, backends[1].url) + assert.Equal([]string{url3}, backends[1].urls) assert.Equal(secret3, string(backends[1].secret)) } @@ -563,7 +563,7 @@ func TestBackendConfiguration_Etcd(t *testing.T) { DeleteEtcdValue(etcd, "/backends/2_two") <-ch if backends := sortBackends(cfg.GetBackends()); assert.Len(backends, 1) { - assert.Equal(url3, backends[0].url) + assert.Equal([]string{url3}, backends[0].urls) assert.Equal(secret3, string(backends[0].secret)) } diff --git a/backend_server.go b/backend_server.go index 016f0eb1..2cb00b9a 100644 --- a/backend_server.go +++ b/backend_server.go @@ -708,12 +708,22 @@ func (b *BackendServer) startDialout(roomid string, backend *Backend, backendUrl return returnDialoutError(http.StatusNotFound, NewError("no_client_available", "No available client found to trigger dialout.")) } - url := backend.Url() - if url == "" { - // Old-style compat backend, use client-provided URL. - url = backendUrl - if url != "" && url[len(url)-1] != '/' { - url += "/" + url := backendUrl + if url != "" && url[len(url)-1] != '/' { + url += "/" + } + if urls := backend.Urls(); len(urls) > 0 { + // Check if client-provided URL is registered for backend and use that. + found := false + for _, u := range urls { + if strings.HasPrefix(url, u) { + found = true + break + } + } + + if !found { + url = urls[0] } } id := newRandomString(32) diff --git a/backend_storage_etcd.go b/backend_storage_etcd.go index ce82beff..64a35718 100644 --- a/backend_storage_etcd.go +++ b/backend_storage_etcd.go @@ -179,10 +179,9 @@ func (s *backendStorageEtcd) EtcdKeyUpdated(client *EtcdClient, key string, data } backend := &Backend{ - id: key, - url: info.Url, - parsedUrl: info.parsedUrl, - secret: []byte(info.Secret), + id: key, + urls: []string{info.Url}, + secret: []byte(info.Secret), allowHttp: info.parsedUrl.Scheme == "http", diff --git a/backend_storage_static.go b/backend_storage_static.go index 4a60c3f3..bc85524d 100644 --- a/backend_storage_static.go +++ b/backend_storage_static.go @@ -55,7 +55,6 @@ func NewBackendStorageStatic(config *goconf.ConfigFile) (BackendStorage, error) compatBackend = &Backend{ id: "compat", secret: []byte(commonSecret), - compat: true, allowHttp: allowHttp, @@ -70,7 +69,7 @@ func NewBackendStorageStatic(config *goconf.ConfigFile) (BackendStorage, error) for host, configuredBackends := range getConfiguredHosts(backendIds, config, commonSecret) { backends[host] = append(backends[host], configuredBackends...) for _, be := range configuredBackends { - log.Printf("Backend %s added for %s", be.id, be.url) + log.Printf("Backend %s added for %s", be.id, strings.Join(be.urls, ", ")) updateBackendStats(be) } numBackends += len(configuredBackends) @@ -95,7 +94,6 @@ func NewBackendStorageStatic(config *goconf.ConfigFile) (BackendStorage, error) compatBackend = &Backend{ id: "compat", secret: []byte(commonSecret), - compat: true, allowHttp: allowHttp, @@ -140,7 +138,7 @@ func (s *backendStorageStatic) Close() { func (s *backendStorageStatic) RemoveBackendsForHost(host string) { if oldBackends := s.backends[host]; len(oldBackends) > 0 { for _, backend := range oldBackends { - log.Printf("Backend %s removed for %s", backend.id, backend.url) + log.Printf("Backend %s removed for %s", backend.id, strings.Join(backend.urls, ", ")) deleteBackendStats(backend) } statsBackendsCurrent.Sub(float64(len(oldBackends))) @@ -161,7 +159,7 @@ func (s *backendStorageStatic) UpsertHost(host string, backends []*Backend) { found = true s.backends[host][existingIndex] = newBackend backends = append(backends[:index], backends[index+1:]...) - log.Printf("Backend %s updated for %s", newBackend.id, newBackend.url) + log.Printf("Backend %s updated for %s", newBackend.id, strings.Join(newBackend.urls, ", ")) updateBackendStats(newBackend) break } @@ -169,7 +167,7 @@ func (s *backendStorageStatic) UpsertHost(host string, backends []*Backend) { } if !found { removed := s.backends[host][existingIndex] - log.Printf("Backend %s removed for %s", removed.id, removed.url) + log.Printf("Backend %s removed for %s", removed.id, strings.Join(removed.urls, ", ")) s.backends[host] = append(s.backends[host][:existingIndex], s.backends[host][existingIndex+1:]...) deleteBackendStats(removed) statsBackendsCurrent.Dec() @@ -178,7 +176,7 @@ func (s *backendStorageStatic) UpsertHost(host string, backends []*Backend) { s.backends[host] = append(s.backends[host], backends...) for _, added := range backends { - log.Printf("Backend %s added for %s", added.id, added.url) + log.Printf("Backend %s added for %s", added.id, strings.Join(added.urls, ", ")) updateBackendStats(added) } statsBackendsCurrent.Add(float64(len(backends))) @@ -254,10 +252,9 @@ func getConfiguredHosts(backendIds string, config *goconf.ConfigFile, commonSecr } hosts[parsed.Host] = append(hosts[parsed.Host], &Backend{ - id: id, - url: u, - parsedUrl: parsed, - secret: []byte(secret), + id: id, + urls: []string{u}, + secret: []byte(secret), allowHttp: parsed.Scheme == "http", diff --git a/clientsession.go b/clientsession.go index c59920a4..0e75d2d1 100644 --- a/clientsession.go +++ b/clientsession.go @@ -39,6 +39,8 @@ var ( // Warn if a session has 32 or more pending messages. warnPendingMessagesCount = 32 + // The "/api/v1/signaling/" URL will be changed to use "v3" as the "signaling-v3" + // feature is returned by the capabilities endpoint. PathToOcsSignalingBackend = "ocs/v2.php/apps/spreed/api/v1/signaling/backend" ) @@ -130,24 +132,6 @@ func NewClientSession(hub *Hub, privateId string, publicId string, data *Session s.backendUrl = hello.Auth.Url s.parsedBackendUrl = hello.Auth.parsedUrl } - if !strings.Contains(s.backendUrl, "/ocs/v2.php/") { - backendUrl := s.backendUrl - if !strings.HasSuffix(backendUrl, "/") { - backendUrl += "/" - } - backendUrl += PathToOcsSignalingBackend - u, err := url.Parse(backendUrl) - if err != nil { - return nil, err - } - - if strings.Contains(u.Host, ":") && hasStandardPort(u) { - u.Host = u.Hostname() - } - - s.backendUrl = backendUrl - s.parsedBackendUrl = u - } if err := s.SubscribeEvents(); err != nil { return nil, err @@ -307,6 +291,10 @@ func (s *ClientSession) ParsedBackendUrl() *url.URL { return s.parsedBackendUrl } +func (s *ClientSession) ParsedBackendOcsUrl() *url.URL { + return s.parsedBackendUrl.JoinPath(PathToOcsSignalingBackend) +} + func (s *ClientSession) AuthUserId() string { return s.userId } @@ -563,7 +551,7 @@ func (s *ClientSession) doUnsubscribeRoomEvents(notify bool) { request.Room.UpdateFromSession(s) request.Room.Action = "leave" var response map[string]interface{} - if err := s.hub.backend.PerformJSONRequest(ctx, s.ParsedBackendUrl(), request, &response); err != nil { + if err := s.hub.backend.PerformJSONRequest(ctx, s.ParsedBackendOcsUrl(), request, &response); err != nil { log.Printf("Could not notify about room session %s left room %s: %s", sid, room.Id(), err) } else { log.Printf("Removed room session %s: %+v", sid, response) diff --git a/federation.go b/federation.go index 9b609c89..3163cfdf 100644 --- a/federation.go +++ b/federation.go @@ -63,6 +63,8 @@ func getCloudUrl(s string) string { } if pos := strings.Index(s, "/ocs/v"); pos != -1 { s = s[:pos] + } else { + s = strings.TrimSuffix(s, "/") } return s } @@ -602,6 +604,7 @@ func (c *FederationClient) updateEventUsers(users []map[string]interface{}, loca localCloudUrlLen := len(localCloudUrl) remoteCloudUrl := "@" + getCloudUrl(c.federation.Load().NextcloudUrl) checkSessionId := true + log.Printf("XXX local=%s remote=%s", localCloudUrl, remoteCloudUrl) for _, u := range users { if actorType, found := getStringMapEntry[string](u, "actorType"); found { if actorId, found := getStringMapEntry[string](u, "actorId"); found { diff --git a/grpc_client.go b/grpc_client.go index 24d2b0cd..8e43da71 100644 --- a/grpc_client.go +++ b/grpc_client.go @@ -29,7 +29,6 @@ import ( "io" "log" "net" - "net/url" "strings" "sync" "sync/atomic" @@ -231,14 +230,14 @@ func (c *GrpcClient) LookupSessionId(ctx context.Context, roomSessionId string, return sessionId, nil } -func (c *GrpcClient) IsSessionInCall(ctx context.Context, sessionId string, room *Room) (bool, error) { +func (c *GrpcClient) IsSessionInCall(ctx context.Context, sessionId string, room *Room, backendUrl string) (bool, error) { statsGrpcClientCalls.WithLabelValues("IsSessionInCall").Inc() // TODO: Remove debug logging log.Printf("Check if session %s is in call %s on %s", sessionId, room.Id(), c.Target()) response, err := c.impl.IsSessionInCall(ctx, &IsSessionInCallRequest{ SessionId: sessionId, RoomId: room.Id(), - BackendUrl: room.Backend().url, + BackendUrl: backendUrl, }, grpc.WaitForReady(true)) if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound { return false, nil @@ -249,13 +248,18 @@ func (c *GrpcClient) IsSessionInCall(ctx context.Context, sessionId string, room return response.GetInCall(), nil } -func (c *GrpcClient) GetInternalSessions(ctx context.Context, roomId string, backend *Backend) (internal map[string]*InternalSessionData, virtual map[string]*VirtualSessionData, err error) { +func (c *GrpcClient) GetInternalSessions(ctx context.Context, roomId string, backendUrls []string) (internal map[string]*InternalSessionData, virtual map[string]*VirtualSessionData, err error) { statsGrpcClientCalls.WithLabelValues("GetInternalSessions").Inc() // TODO: Remove debug logging - log.Printf("Get internal sessions for %s@%s on %s", roomId, backend.Id(), c.Target()) + log.Printf("Get internal sessions for %s on %s", roomId, c.Target()) + var backendUrl string + if len(backendUrls) > 0 { + backendUrl = backendUrls[0] + } response, err := c.impl.GetInternalSessions(ctx, &GetInternalSessionsRequest{ - RoomId: roomId, - BackendUrl: backend.Url(), + RoomId: roomId, + BackendUrl: backendUrl, + BackendUrls: backendUrls, }, grpc.WaitForReady(true)) if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound { return nil, nil, nil @@ -296,12 +300,12 @@ func (c *GrpcClient) GetPublisherId(ctx context.Context, sessionId string, strea return response.GetPublisherId(), response.GetProxyUrl(), net.ParseIP(response.GetIp()), nil } -func (c *GrpcClient) GetSessionCount(ctx context.Context, u *url.URL) (uint32, error) { +func (c *GrpcClient) GetSessionCount(ctx context.Context, url string) (uint32, error) { statsGrpcClientCalls.WithLabelValues("GetSessionCount").Inc() // TODO: Remove debug logging - log.Printf("Get session count for %s on %s", u, c.Target()) + log.Printf("Get session count for %s on %s", url, c.Target()) response, err := c.impl.GetSessionCount(ctx, &GetSessionCountRequest{ - Url: u.String(), + Url: url, }, grpc.WaitForReady(true)) if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound { return 0, nil diff --git a/grpc_server.go b/grpc_server.go index 44674954..172f6a35 100644 --- a/grpc_server.go +++ b/grpc_server.go @@ -176,7 +176,7 @@ func (s *GrpcServer) IsSessionInCall(ctx context.Context, request *IsSessionInCa result := &IsSessionInCallReply{} room := session.GetRoom() - if room == nil || room.Id() != request.GetRoomId() || room.Backend().url != request.GetBackendUrl() || + if room == nil || room.Id() != request.GetRoomId() || !room.Backend().HasUrl(request.GetBackendUrl()) || (session.ClientType() != HelloClientTypeInternal && !room.IsSessionInCall(session)) { // Recipient is not in a room, a different room or not in the call. result.InCall = false @@ -189,44 +189,63 @@ func (s *GrpcServer) IsSessionInCall(ctx context.Context, request *IsSessionInCa func (s *GrpcServer) GetInternalSessions(ctx context.Context, request *GetInternalSessionsRequest) (*GetInternalSessionsReply, error) { statsGrpcServerCalls.WithLabelValues("GetInternalSessions").Inc() // TODO: Remove debug logging - log.Printf("Get internal sessions from %s on %s", request.RoomId, request.BackendUrl) + log.Printf("Get internal sessions from %s on %v (fallback %s)", request.RoomId, request.BackendUrls, request.BackendUrl) - var u *url.URL - if request.BackendUrl != "" { - var err error - u, err = url.Parse(request.BackendUrl) - if err != nil { - return nil, status.Error(codes.InvalidArgument, "invalid url") - } + var backendUrls []string + if len(request.BackendUrls) > 0 { + backendUrls = request.BackendUrls + } else if request.BackendUrl != "" { + backendUrls = append(backendUrls, request.BackendUrl) + } else { + // Only compat backend. + backendUrls = []string{""} } - backend := s.hub.GetBackend(u) - if backend == nil { - return nil, status.Error(codes.NotFound, "no such backend") - } + result := &GetInternalSessionsReply{} + processed := make(map[string]bool) + for _, bu := range backendUrls { + var parsed *url.URL + if bu != "" { + var err error + parsed, err = url.Parse(bu) + if err != nil { + return nil, status.Error(codes.InvalidArgument, "invalid url") + } + } - room := s.hub.GetRoomForBackend(request.RoomId, backend) - if room == nil { - return nil, status.Error(codes.NotFound, "no such room") - } + backend := s.hub.GetBackend(parsed) + if backend == nil { + return nil, status.Error(codes.NotFound, "no such backend") + } - result := &GetInternalSessionsReply{} - room.mu.RLock() - defer room.mu.RUnlock() - - for session := range room.internalSessions { - result.InternalSessions = append(result.InternalSessions, &InternalSessionData{ - SessionId: session.PublicId(), - InCall: uint32(session.GetInCall()), - Features: session.GetFeatures(), - }) - } + // Only process each backend once. + if processed[backend.Id()] { + continue + } + processed[backend.Id()] = true - for session := range room.virtualSessions { - result.VirtualSessions = append(result.VirtualSessions, &VirtualSessionData{ - SessionId: session.PublicId(), - InCall: uint32(session.GetInCall()), - }) + room := s.hub.GetRoomForBackend(request.RoomId, backend) + if room == nil { + return nil, status.Error(codes.NotFound, "no such room") + } + + room.mu.RLock() + defer room.mu.RUnlock() + + for session := range room.internalSessions { + result.InternalSessions = append(result.InternalSessions, &InternalSessionData{ + SessionId: session.PublicId(), + InCall: uint32(session.GetInCall()), + Features: session.GetFeatures(), + }) + } + + for session := range room.virtualSessions { + result.VirtualSessions = append(result.VirtualSessions, &VirtualSessionData{ + SessionId: session.PublicId(), + InCall: uint32(session.GetInCall()), + }) + } } return result, nil diff --git a/grpc_sessions.pb.go b/grpc_sessions.pb.go index f47e8fb0..6fb2c303 100644 --- a/grpc_sessions.pb.go +++ b/grpc_sessions.pb.go @@ -328,9 +328,11 @@ func (x *IsSessionInCallReply) GetInCall() bool { } type GetInternalSessionsRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - RoomId string `protobuf:"bytes,1,opt,name=roomId,proto3" json:"roomId,omitempty"` - BackendUrl string `protobuf:"bytes,2,opt,name=backendUrl,proto3" json:"backendUrl,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + RoomId string `protobuf:"bytes,1,opt,name=roomId,proto3" json:"roomId,omitempty"` + // Deprecated: Marked as deprecated in grpc_sessions.proto. + BackendUrl string `protobuf:"bytes,2,opt,name=backendUrl,proto3" json:"backendUrl,omitempty"` + BackendUrls []string `protobuf:"bytes,3,rep,name=backendUrls,proto3" json:"backendUrls,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -372,6 +374,7 @@ func (x *GetInternalSessionsRequest) GetRoomId() string { return "" } +// Deprecated: Marked as deprecated in grpc_sessions.proto. func (x *GetInternalSessionsRequest) GetBackendUrl() string { if x != nil { return x.BackendUrl @@ -379,6 +382,13 @@ func (x *GetInternalSessionsRequest) GetBackendUrl() string { return "" } +func (x *GetInternalSessionsRequest) GetBackendUrls() []string { + if x != nil { + return x.BackendUrls + } + return nil +} + type InternalSessionData struct { state protoimpl.MessageState `protogen:"open.v1"` SessionId string `protobuf:"bytes,1,opt,name=sessionId,proto3" json:"sessionId,omitempty"` @@ -662,75 +672,77 @@ var file_grpc_sessions_proto_rawDesc = []byte{ 0x52, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x55, 0x72, 0x6c, 0x22, 0x2e, 0x0a, 0x14, 0x49, 0x73, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x43, 0x61, 0x6c, 0x6c, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x6e, 0x43, 0x61, 0x6c, 0x6c, 0x22, 0x54, 0x0a, 0x1a, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x6e, 0x43, 0x61, 0x6c, 0x6c, 0x22, 0x7a, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x6f, 0x6d, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6f, 0x6d, - 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x55, 0x72, 0x6c, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x55, - 0x72, 0x6c, 0x22, 0x67, 0x0a, 0x13, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x43, 0x61, 0x6c, - 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x69, 0x6e, 0x43, 0x61, 0x6c, 0x6c, 0x12, - 0x1a, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x4a, 0x0a, 0x12, 0x56, - 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, - 0x61, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, - 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x43, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x06, 0x69, 0x6e, 0x43, 0x61, 0x6c, 0x6c, 0x22, 0xaf, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x65, 0x70, 0x6c, 0x79, 0x12, 0x4a, 0x0a, 0x10, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, - 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x10, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x47, 0x0a, 0x0f, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0f, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, - 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x30, 0x0a, 0x14, 0x43, 0x6c, 0x69, + 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x55, 0x72, 0x6c, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0a, 0x62, 0x61, 0x63, 0x6b, + 0x65, 0x6e, 0x64, 0x55, 0x72, 0x6c, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, + 0x64, 0x55, 0x72, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x61, 0x63, + 0x6b, 0x65, 0x6e, 0x64, 0x55, 0x72, 0x6c, 0x73, 0x22, 0x67, 0x0a, 0x13, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, + 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, + 0x06, 0x69, 0x6e, 0x43, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x69, + 0x6e, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x22, 0x4a, 0x0a, 0x12, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x43, 0x61, 0x6c, 0x6c, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x69, 0x6e, 0x43, 0x61, 0x6c, 0x6c, 0x22, 0xaf, 0x01, + 0x0a, 0x18, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x4a, 0x0a, 0x10, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, + 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x44, 0x61, 0x74, 0x61, 0x52, 0x10, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x47, 0x0a, 0x0f, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, + 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1d, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x56, 0x69, 0x72, 0x74, + 0x75, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0f, + 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, + 0x30, 0x0a, 0x14, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x22, 0x30, 0x0a, 0x14, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x32, 0xd2, 0x03, 0x0a, 0x0b, 0x52, 0x70, 0x63, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x54, 0x0a, 0x0e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, + 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x20, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, + 0x67, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x49, 0x64, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, + 0x69, 0x6e, 0x67, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, + 0x49, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0f, 0x4c, 0x6f, 0x6f, + 0x6b, 0x75, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x21, 0x2e, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, + 0x75, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0f, 0x49, 0x73, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, + 0x6e, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x21, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, + 0x67, 0x2e, 0x49, 0x73, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x43, 0x61, 0x6c, + 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x49, 0x73, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, + 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x63, 0x0a, 0x13, 0x47, + 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x25, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x47, + 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, + 0x12, 0x56, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x1f, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x30, 0x0a, 0x14, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0xd2, 0x03, - 0x0a, 0x0b, 0x52, 0x70, 0x63, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x54, 0x0a, - 0x0e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x12, - 0x20, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, - 0x75, 0x70, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1e, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x4c, 0x6f, - 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x49, 0x64, 0x52, 0x65, 0x70, 0x6c, - 0x79, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0f, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x21, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, - 0x6e, 0x67, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0f, - 0x49, 0x73, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x43, 0x61, 0x6c, 0x6c, 0x12, - 0x21, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x49, 0x73, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x49, - 0x73, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, - 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x63, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x2e, 0x73, - 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, - 0x47, 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x56, 0x0a, 0x0c, 0x50, 0x72, - 0x6f, 0x78, 0x79, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1f, 0x2e, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x28, 0x01, - 0x30, 0x01, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x73, 0x74, 0x72, 0x75, 0x6b, 0x74, 0x75, 0x72, 0x61, 0x67, 0x2f, 0x6e, 0x65, 0x78, 0x74, - 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2d, 0x73, 0x70, 0x72, 0x65, 0x65, 0x64, 0x2d, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x3b, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x1a, 0x1f, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x6b, 0x74, 0x75, 0x72, 0x61, + 0x67, 0x2f, 0x6e, 0x65, 0x78, 0x74, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2d, 0x73, 0x70, 0x72, 0x65, + 0x65, 0x64, 0x2d, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x3b, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/grpc_sessions.proto b/grpc_sessions.proto index 3d29dfef..0503553d 100644 --- a/grpc_sessions.proto +++ b/grpc_sessions.proto @@ -63,7 +63,8 @@ message IsSessionInCallReply { message GetInternalSessionsRequest { string roomId = 1; - string backendUrl = 2; + string backendUrl = 2 [deprecated = true]; + repeated string backendUrls = 3; } message InternalSessionData { diff --git a/hub.go b/hub.go index 747b435c..d71738ca 100644 --- a/hub.go +++ b/hub.go @@ -633,12 +633,10 @@ func (h *Hub) GetSessionIdByRoomSessionId(roomSessionId string) (string, error) } func (h *Hub) GetDialoutSession(roomId string, backend *Backend) *ClientSession { - url := backend.Url() - h.mu.RLock() defer h.mu.RUnlock() for session := range h.dialoutSessions { - if session.backend.Url() != url { + if !backend.HasUrl(session.BackendUrl()) { continue } @@ -890,14 +888,14 @@ func (h *Hub) processRegister(c HandlerClient, message *ClientMessage, backend * go func(c *GrpcClient) { defer wg.Done() - count, err := c.GetSessionCount(ctx, backend.ParsedUrl()) + count, err := c.GetSessionCount(ctx, session.BackendUrl()) if err != nil { - log.Printf("Received error while getting session count for %s from %s: %s", backend.Url(), c.Target(), err) + log.Printf("Received error while getting session count for %s from %s: %s", session.BackendUrl(), c.Target(), err) return } if count > 0 { - log.Printf("%d sessions connected for %s on %s", count, backend.Url(), c.Target()) + log.Printf("%d sessions connected for %s on %s", count, session.BackendUrl(), c.Target()) totalCount.Add(count) } }(client) @@ -1241,6 +1239,8 @@ func (h *Hub) processHelloV1(ctx context.Context, client HandlerClient, message return nil, nil, InvalidBackendUrl } + url = url.JoinPath(PathToOcsSignalingBackend) + // Run in timeout context to prevent blocking too long. ctx, cancel := context.WithTimeout(ctx, h.backendTimeout) defer cancel() @@ -1700,7 +1700,7 @@ func (h *Hub) processRoom(sess Session, message *ClientMessage) { } request := NewBackendClientRoomRequest(roomId, session.UserId(), sessionId) request.Room.UpdateFromSession(session) - if err := h.backend.PerformJSONRequest(ctx, session.ParsedBackendUrl(), request, &room); err != nil { + if err := h.backend.PerformJSONRequest(ctx, session.ParsedBackendOcsUrl(), request, &room); err != nil { session.SendMessage(message.NewWrappedErrorServerMessage(err)) return } @@ -2328,7 +2328,7 @@ func (h *Hub) processInternalMsg(sess Session, message *ClientMessage) { request.Room.InCall = sess.GetInCall() var response BackendClientResponse - if err := h.backend.PerformJSONRequest(ctx, session.ParsedBackendUrl(), request, &response); err != nil { + if err := h.backend.PerformJSONRequest(ctx, session.ParsedBackendOcsUrl(), request, &response); err != nil { sess.Close() log.Printf("Could not join virtual session %s at backend %s: %s", virtualSessionId, session.BackendUrl(), err) reply := message.NewErrorServerMessage(NewError("add_failed", "Could not join virtual session.")) @@ -2346,7 +2346,7 @@ func (h *Hub) processInternalMsg(sess Session, message *ClientMessage) { } else { request := NewBackendClientSessionRequest(room.Id(), "add", publicSessionId, msg) var response BackendClientSessionResponse - if err := h.backend.PerformJSONRequest(ctx, session.ParsedBackendUrl(), request, &response); err != nil { + if err := h.backend.PerformJSONRequest(ctx, session.ParsedBackendOcsUrl(), request, &response); err != nil { sess.Close() log.Printf("Could not add virtual session %s at backend %s: %s", virtualSessionId, session.BackendUrl(), err) reply := message.NewErrorServerMessage(NewError("add_failed", "Could not add virtual session.")) @@ -2553,7 +2553,7 @@ func (h *Hub) isInSameCallRemote(ctx context.Context, senderSession *ClientSessi go func(client *GrpcClient) { defer wg.Done() - inCall, err := client.IsSessionInCall(rpcCtx, recipientSessionId, senderRoom) + inCall, err := client.IsSessionInCall(rpcCtx, recipientSessionId, senderRoom, senderSession.BackendUrl()) if errors.Is(err, context.Canceled) { return } else if err != nil { diff --git a/hub_test.go b/hub_test.go index 3af0cdef..c1d0777a 100644 --- a/hub_test.go +++ b/hub_test.go @@ -1982,7 +1982,7 @@ func TestClientHelloClient_V3Api(t *testing.T) { } // The "/api/v1/signaling/" URL will be changed to use "v3" as the "signaling-v3" // feature is returned by the capabilities endpoint. - require.NoError(client.SendHelloParams(server.URL+"/ocs/v2.php/apps/spreed/api/v1/signaling/backend", HelloVersionV1, "client", nil, params)) + require.NoError(client.SendHelloParams(server.URL, HelloVersionV1, "client", nil, params)) ctx, cancel := context.WithTimeout(context.Background(), testTimeout) defer cancel() diff --git a/room.go b/room.go index e6e04fbe..e6b90f8c 100644 --- a/room.go +++ b/room.go @@ -29,6 +29,7 @@ import ( "log" "net/url" "strconv" + "strings" "sync" "time" @@ -603,7 +604,7 @@ func (r *Room) getClusteredInternalSessionsRLocked() (internal map[string]*Inter go func(c *GrpcClient) { defer wg.Done() - clientInternal, clientVirtual, err := c.GetInternalSessions(ctx, r.Id(), r.Backend()) + clientInternal, clientVirtual, err := c.GetInternalSessions(ctx, r.Id(), r.Backend().Urls()) if err != nil { log.Printf("Received error while getting internal sessions for %s@%s from %s: %s", r.Id(), r.Backend().Id(), c.Target(), err) return @@ -1042,6 +1043,20 @@ func (r *Room) publishActiveSessions() (int, *sync.WaitGroup) { continue } + u += PathToOcsSignalingBackend + parsed, err := url.Parse(u) + if err != nil { + log.Printf("Could not parse backend url %s: %s", u, err) + continue + } + + if strings.Contains(parsed.Host, ":") && hasStandardPort(parsed) { + parsed.Host = parsed.Hostname() + } + + u = parsed.String() + parsedBackendUrl := parsed + var sid string var uid string switch sess := session.(type) { @@ -1061,12 +1076,11 @@ func (r *Room) publishActiveSessions() (int, *sync.WaitGroup) { } e, found := entries[u] if !found { - p := session.ParsedBackendUrl() - if p == nil { + if parsedBackendUrl == nil { // Should not happen, invalid URLs should get rejected earlier. continue } - urls[u] = p + urls[u] = parsedBackendUrl } entries[u] = append(e, BackendPingEntry{ diff --git a/virtualsession.go b/virtualsession.go index cfbd4356..8dff0840 100644 --- a/virtualsession.go +++ b/virtualsession.go @@ -132,6 +132,10 @@ func (s *VirtualSession) ParsedBackendUrl() *url.URL { return s.session.ParsedBackendUrl() } +func (s *VirtualSession) ParsedBackendOcsUrl() *url.URL { + return s.session.ParsedBackendOcsUrl() +} + func (s *VirtualSession) UserId() string { return s.userId } @@ -197,7 +201,7 @@ func (s *VirtualSession) notifyBackendRemoved(room *Room, session Session, messa } var response BackendClientResponse - if err := s.hub.backend.PerformJSONRequest(ctx, s.ParsedBackendUrl(), request, &response); err != nil { + if err := s.hub.backend.PerformJSONRequest(ctx, s.ParsedBackendOcsUrl(), request, &response); err != nil { virtualSessionId := GetVirtualSessionId(s.session, s.PublicId()) log.Printf("Could not leave virtual session %s at backend %s: %s", virtualSessionId, s.BackendUrl(), err) if session != nil && message != nil { @@ -222,7 +226,7 @@ func (s *VirtualSession) notifyBackendRemoved(room *Room, session Session, messa User: s.userData, }) var response BackendClientSessionResponse - err := s.hub.backend.PerformJSONRequest(ctx, s.ParsedBackendUrl(), request, &response) + err := s.hub.backend.PerformJSONRequest(ctx, s.ParsedBackendOcsUrl(), request, &response) if err != nil { log.Printf("Could not remove virtual session %s from backend %s: %s", s.PublicId(), s.BackendUrl(), err) if session != nil && message != nil { diff --git a/virtualsession_test.go b/virtualsession_test.go index bc9ca103..ad844159 100644 --- a/virtualsession_test.go +++ b/virtualsession_test.go @@ -42,8 +42,7 @@ func TestVirtualSession(t *testing.T) { roomId := "the-room-id" emptyProperties := json.RawMessage("{}") backend := &Backend{ - id: "compat", - compat: true, + id: "compat", } room, err := hub.createRoom(roomId, emptyProperties, backend) require.NoError(err) @@ -270,8 +269,7 @@ func TestVirtualSessionCustomInCall(t *testing.T) { roomId := "the-room-id" emptyProperties := json.RawMessage("{}") backend := &Backend{ - id: "compat", - compat: true, + id: "compat", } room, err := hub.createRoom(roomId, emptyProperties, backend) require.NoError(err) @@ -425,8 +423,7 @@ func TestVirtualSessionCleanup(t *testing.T) { roomId := "the-room-id" emptyProperties := json.RawMessage("{}") backend := &Backend{ - id: "compat", - compat: true, + id: "compat", } room, err := hub.createRoom(roomId, emptyProperties, backend) require.NoError(err)