Skip to content

Commit

Permalink
Merge pull request #288 from strukturag/initial-welcome
Browse files Browse the repository at this point in the history
Send initial "welcome" message when clients connect.
  • Loading branch information
fancycode authored Jul 7, 2022
2 parents 32a2f82 + 2434116 commit 042d447
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 60 deletions.
4 changes: 2 additions & 2 deletions api_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ func (m *HelloProxyClientMessage) CheckValid() error {
type HelloProxyServerMessage struct {
Version string `json:"version"`

SessionId string `json:"sessionid"`
Server *HelloServerMessageServer `json:"server,omitempty"`
SessionId string `json:"sessionid"`
Server *WelcomeServerMessage `json:"server,omitempty"`
}

// Type "bye"
Expand Down
77 changes: 67 additions & 10 deletions api_signaling.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"encoding/json"
"fmt"
"net/url"
"sort"
"strings"
)

Expand Down Expand Up @@ -141,6 +142,8 @@ type ServerMessage struct {

Error *Error `json:"error,omitempty"`

Welcome *WelcomeServerMessage `json:"welcome,omitempty"`

Hello *HelloServerMessage `json:"hello,omitempty"`

Bye *ByeServerMessage `json:"bye,omitempty"`
Expand Down Expand Up @@ -233,6 +236,54 @@ func (e *Error) Error() string {
return e.Message
}

type WelcomeServerMessage struct {
Version string `json:"version"`
Features []string `json:"features,omitempty"`
Country string `json:"country,omitempty"`
}

func NewWelcomeServerMessage(version string, feature ...string) *WelcomeServerMessage {
message := &WelcomeServerMessage{
Version: version,
Features: feature,
}
if len(feature) > 0 {
sort.Strings(message.Features)
}
return message
}

func (m *WelcomeServerMessage) AddFeature(feature ...string) {
newFeatures := make([]string, len(m.Features))
copy(newFeatures, m.Features)
for _, feat := range feature {
found := false
for _, f := range newFeatures {
if f == feat {
found = true
break
}
}
if !found {
newFeatures = append(newFeatures, feat)
}
}
sort.Strings(newFeatures)
m.Features = newFeatures
}

func (m *WelcomeServerMessage) RemoveFeature(feature ...string) {
newFeatures := make([]string, len(m.Features))
copy(newFeatures, m.Features)
for _, feat := range feature {
idx := sort.SearchStrings(newFeatures, feat)
if idx < len(newFeatures) && newFeatures[idx] == feat {
newFeatures = append(newFeatures[:idx], newFeatures[idx+1:]...)
}
}
m.Features = newFeatures
}

const (
HelloClientTypeClient = "client"
HelloClientTypeInternal = "internal"
Expand Down Expand Up @@ -345,6 +396,7 @@ const (
ServerFeatureAudioVideoPermissions = "audio-video-permissions"
ServerFeatureTransientData = "transient-data"
ServerFeatureInCallAll = "incall-all"
ServerFeatureWelcome = "welcome"

// Features for internal clients only.
ServerFeatureInternalVirtualSessions = "virtual-sessions"
Expand All @@ -355,27 +407,32 @@ var (
ServerFeatureAudioVideoPermissions,
ServerFeatureTransientData,
ServerFeatureInCallAll,
ServerFeatureWelcome,
}
DefaultFeaturesInternal = []string{
ServerFeatureInternalVirtualSessions,
ServerFeatureTransientData,
ServerFeatureInCallAll,
ServerFeatureWelcome,
}
DefaultWelcomeFeatures = []string{
ServerFeatureAudioVideoPermissions,
ServerFeatureInternalVirtualSessions,
ServerFeatureTransientData,
ServerFeatureInCallAll,
ServerFeatureWelcome,
}
)

type HelloServerMessageServer struct {
Version string `json:"version"`
Features []string `json:"features,omitempty"`
Country string `json:"country,omitempty"`
}

type HelloServerMessage struct {
Version string `json:"version"`

SessionId string `json:"sessionid"`
ResumeId string `json:"resumeid"`
UserId string `json:"userid"`
Server *HelloServerMessageServer `json:"server,omitempty"`
SessionId string `json:"sessionid"`
ResumeId string `json:"resumeid"`
UserId string `json:"userid"`

// TODO: Remove once all clients have switched to the "welcome" message.
Server *WelcomeServerMessage `json:"server,omitempty"`
}

// Type "bye"
Expand Down
41 changes: 41 additions & 0 deletions api_signaling_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ package signaling
import (
"encoding/json"
"fmt"
"reflect"
"sort"
"testing"
)

Expand Down Expand Up @@ -346,3 +348,42 @@ func TestIsChatRefresh(t *testing.T) {
t.Error("message should not be detected as chat refresh")
}
}

func assertEqualStrings(t *testing.T, expected, result []string) {
t.Helper()

if expected == nil {
expected = make([]string, 0)
} else {
sort.Strings(expected)
}
if result == nil {
result = make([]string, 0)
} else {
sort.Strings(result)
}

if !reflect.DeepEqual(expected, result) {
t.Errorf("Expected %+v, got %+v", expected, result)
}
}

func Test_Welcome_AddRemoveFeature(t *testing.T) {
var msg WelcomeServerMessage
assertEqualStrings(t, []string{}, msg.Features)

msg.AddFeature("one", "two", "one")
assertEqualStrings(t, []string{"one", "two"}, msg.Features)
if !sort.StringsAreSorted(msg.Features) {
t.Errorf("features should be sorted, got %+v", msg.Features)
}

msg.AddFeature("three")
assertEqualStrings(t, []string{"one", "two", "three"}, msg.Features)
if !sort.StringsAreSorted(msg.Features) {
t.Errorf("features should be sorted, got %+v", msg.Features)
}

msg.RemoveFeature("three", "one")
assertEqualStrings(t, []string{"two"}, msg.Features)
}
21 changes: 21 additions & 0 deletions docs/standalone-signaling-api-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,23 @@ must contain two additional HTTP headers:
- Calculated checksum: `3c4a69ff328299803ac2879614b707c807b4758cf19450755c60656cac46e3bc`


## Welcome message

When a client connects, the server will immediately send a `welcome` message to
notify the client about supported features. This is available if the server
supports the `welcome` feature id.

Message format (Server -> Client):

{
"type": "welcome",
"welcome": {
"features": ["optional", "list, "of", "feature", "ids"],
...additional information about the server...
}
}


## Establish connection

This must be the first request by a newly connected client and is used to
Expand Down Expand Up @@ -150,6 +167,10 @@ Message format (Server -> Client):
}
}

Please note that the `server` entry is deprecated and will be removed in a
future version. Clients should use the data from the
[`welcome` message](#welcome-message) instead.


### Backend validation

Expand Down
76 changes: 31 additions & 45 deletions hub.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,9 @@ type Hub struct {
nats NatsClient
upgrader websocket.Upgrader
cookie *securecookie.SecureCookie
info *HelloServerMessageServer
infoInternal *HelloServerMessageServer
info *WelcomeServerMessage
infoInternal *WelcomeServerMessage
welcome atomic.Value // *ServerMessage

stopped int32
stopChan chan bool
Expand Down Expand Up @@ -297,15 +298,9 @@ func NewHub(config *goconf.ConfigFile, nats NatsClient, r *mux.Router, version s
ReadBufferSize: websocketReadBufferSize,
WriteBufferSize: websocketWriteBufferSize,
},
cookie: securecookie.New([]byte(hashKey), blockBytes).MaxAge(0),
info: &HelloServerMessageServer{
Version: version,
Features: DefaultFeatures,
},
infoInternal: &HelloServerMessageServer{
Version: version,
Features: DefaultFeaturesInternal,
},
cookie: securecookie.New([]byte(hashKey), blockBytes).MaxAge(0),
info: NewWelcomeServerMessage(version, DefaultFeatures...),
infoInternal: NewWelcomeServerMessage(version, DefaultFeaturesInternal...),

stopChan: make(chan bool),

Expand Down Expand Up @@ -339,6 +334,10 @@ func NewHub(config *goconf.ConfigFile, nats NatsClient, r *mux.Router, version s
geoip: geoip,
geoipOverrides: geoipOverrides,
}
hub.setWelcomeMessage(&ServerMessage{
Type: "welcome",
Welcome: NewWelcomeServerMessage(version, DefaultWelcomeFeatures...),
})
backend.hub = hub
hub.upgrader.CheckOrigin = hub.checkOrigin
r.HandleFunc("/spreed", func(w http.ResponseWriter, r *http.Request) {
Expand All @@ -348,57 +347,39 @@ func NewHub(config *goconf.ConfigFile, nats NatsClient, r *mux.Router, version s
return hub, nil
}

func addFeature(msg *HelloServerMessageServer, feature string) {
var newFeatures []string
added := false
for _, f := range msg.Features {
newFeatures = append(newFeatures, f)
if f == feature {
added = true
}
}
if !added {
newFeatures = append(newFeatures, feature)
}
msg.Features = newFeatures
func (h *Hub) setWelcomeMessage(msg *ServerMessage) {
h.welcome.Store(msg)
}

func removeFeature(msg *HelloServerMessageServer, feature string) {
var newFeatures []string
for _, f := range msg.Features {
if f != feature {
newFeatures = append(newFeatures, f)
}
}
msg.Features = newFeatures
func (h *Hub) getWelcomeMessage() *ServerMessage {
return h.welcome.Load().(*ServerMessage)
}

func (h *Hub) SetMcu(mcu Mcu) {
h.mcu = mcu
// Create copy of message so it can be updated concurrently.
welcome := *h.getWelcomeMessage()
if mcu == nil {
removeFeature(h.info, ServerFeatureMcu)
removeFeature(h.info, ServerFeatureSimulcast)
removeFeature(h.info, ServerFeatureUpdateSdp)
removeFeature(h.infoInternal, ServerFeatureMcu)
removeFeature(h.infoInternal, ServerFeatureSimulcast)
removeFeature(h.infoInternal, ServerFeatureUpdateSdp)
h.info.RemoveFeature(ServerFeatureMcu, ServerFeatureSimulcast, ServerFeatureUpdateSdp)
h.infoInternal.RemoveFeature(ServerFeatureMcu, ServerFeatureSimulcast, ServerFeatureUpdateSdp)

welcome.Welcome.RemoveFeature(ServerFeatureMcu, ServerFeatureSimulcast, ServerFeatureUpdateSdp)
} else {
log.Printf("Using a timeout of %s for MCU requests", h.mcuTimeout)
addFeature(h.info, ServerFeatureMcu)
addFeature(h.info, ServerFeatureSimulcast)
addFeature(h.info, ServerFeatureUpdateSdp)
addFeature(h.infoInternal, ServerFeatureMcu)
addFeature(h.infoInternal, ServerFeatureSimulcast)
addFeature(h.infoInternal, ServerFeatureUpdateSdp)
h.info.AddFeature(ServerFeatureMcu, ServerFeatureSimulcast, ServerFeatureUpdateSdp)
h.infoInternal.AddFeature(ServerFeatureMcu, ServerFeatureSimulcast, ServerFeatureUpdateSdp)

welcome.Welcome.AddFeature(ServerFeatureMcu, ServerFeatureSimulcast, ServerFeatureUpdateSdp)
}
h.setWelcomeMessage(&welcome)
}

func (h *Hub) checkOrigin(r *http.Request) bool {
// We allow any Origin to connect to the service.
return true
}

func (h *Hub) GetServerInfo(session Session) *HelloServerMessageServer {
func (h *Hub) GetServerInfo(session Session) *WelcomeServerMessage {
if session.ClientType() == HelloClientTypeInternal {
return h.infoInternal
}
Expand Down Expand Up @@ -685,6 +666,11 @@ func (h *Hub) startExpectHello(client *Client) {

func (h *Hub) processNewClient(client *Client) {
h.startExpectHello(client)
h.sendWelcome(client)
}

func (h *Hub) sendWelcome(client *Client) {
client.SendMessage(h.getWelcomeMessage())
}

func (h *Hub) newSessionIdData(backend *Backend) *SessionIdData {
Expand Down
23 changes: 23 additions & 0 deletions hub_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,29 @@ func performHousekeeping(hub *Hub, now time.Time) *sync.WaitGroup {
return &wg
}

func TestInitialWelcome(t *testing.T) {
hub, _, _, server := CreateHubForTest(t)

ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()

client := NewTestClientContext(ctx, t, server, hub)
defer client.CloseWithBye()

msg, err := client.RunUntilMessage(ctx)
if err != nil {
t.Fatal(err)
}

if msg.Type != "welcome" {
t.Errorf("Expected \"welcome\" message, got %+v", msg)
} else if msg.Welcome.Version == "" {
t.Errorf("Expected welcome version, got %+v", msg)
} else if len(msg.Welcome.Features) == 0 {
t.Errorf("Expected welcome features, got %+v", msg)
}
}

func TestExpectClientHello(t *testing.T) {
hub, _, _, server := CreateHubForTest(t)

Expand Down
2 changes: 1 addition & 1 deletion proxy/proxy_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,7 @@ func (s *ProxyServer) processMessage(client *ProxyClient, data []byte) {
Hello: &signaling.HelloProxyServerMessage{
Version: signaling.HelloVersion,
SessionId: session.PublicId(),
Server: &signaling.HelloServerMessageServer{
Server: &signaling.WelcomeServerMessage{
Version: s.version,
Country: s.country,
},
Expand Down
Loading

0 comments on commit 042d447

Please sign in to comment.