Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support multiple urls per backend #770

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions api_signaling.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,11 +340,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
Expand Down Expand Up @@ -411,11 +417,17 @@ func (m *HelloClientMessage) CheckValid() error {
case HelloClientTypeClient:
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
Expand Down
4 changes: 2 additions & 2 deletions backend_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,13 @@ func (b *BackendClient) PerformJSONRequest(ctx context.Context, u *url.URL, requ

ct := resp.Header.Get("Content-Type")
if !strings.HasPrefix(ct, "application/json") {
log.Printf("Received unsupported content-type from %s: %s (%s)", req.URL, ct, resp.Status)
log.Printf("Received unsupported content-type from %s for %s: %s (%s)", req.URL, string(data), ct, resp.Status)
return ErrUnsupportedContentType
}

body, err := io.ReadAll(resp.Body)
if err != nil {
log.Printf("Could not read response body from %s: %s", req.URL, err)
log.Printf("Could not read response body from %s for %s: %s", req.URL, string(data), err)
return err
}

Expand Down
34 changes: 20 additions & 14 deletions backend_configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
}
Expand Down
41 changes: 21 additions & 20 deletions backend_configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"context"
"net/url"
"reflect"
"slices"
"sort"
"testing"

Expand Down Expand Up @@ -530,8 +531,8 @@ func TestBackendConfiguration_Etcd(t *testing.T) {

if backends := sortBackends(cfg.GetBackends()); len(backends) != 1 {
t.Errorf("Expected one backend, got %+v", backends)
} else if backends[0].url != url1 {
t.Errorf("Expected backend url %s, got %s", url1, backends[0].url)
} else if !slices.Equal(backends[0].urls, []string{url1}) {
t.Errorf("Expected backend url %s, got %v", url1, backends[0].urls)
} else if string(backends[0].secret) != initialSecret1 {
t.Errorf("Expected backend secret %s, got %s", initialSecret1, string(backends[0].secret))
} else if backend := cfg.GetBackend(mustParse(url1)); backend != backends[0] {
Expand All @@ -543,8 +544,8 @@ func TestBackendConfiguration_Etcd(t *testing.T) {
<-ch
if backends := sortBackends(cfg.GetBackends()); len(backends) != 1 {
t.Errorf("Expected one backend, got %+v", backends)
} else if backends[0].url != url1 {
t.Errorf("Expected backend url %s, got %s", url1, backends[0].url)
} else if !slices.Equal(backends[0].urls, []string{url1}) {
t.Errorf("Expected backend url %s, got %v", url1, backends[0].urls)
} else if string(backends[0].secret) != secret1 {
t.Errorf("Expected backend secret %s, got %s", secret1, string(backends[0].secret))
} else if backend := cfg.GetBackend(mustParse(url1)); backend != backends[0] {
Expand All @@ -559,12 +560,12 @@ func TestBackendConfiguration_Etcd(t *testing.T) {
<-ch
if backends := sortBackends(cfg.GetBackends()); len(backends) != 2 {
t.Errorf("Expected two backends, got %+v", backends)
} else if backends[0].url != url1 {
t.Errorf("Expected backend url %s, got %s", url1, backends[0].url)
} else if !slices.Equal(backends[0].urls, []string{url1}) {
t.Errorf("Expected backend url %s, got %v", url1, backends[0].urls)
} else if string(backends[0].secret) != secret1 {
t.Errorf("Expected backend secret %s, got %s", secret1, string(backends[0].secret))
} else if backends[1].url != url2 {
t.Errorf("Expected backend url %s, got %s", url2, backends[1].url)
} else if !slices.Equal(backends[1].urls, []string{url2}) {
t.Errorf("Expected backend url %s, got %v", url2, backends[1].urls)
} else if string(backends[1].secret) != secret2 {
t.Errorf("Expected backend secret %s, got %s", secret2, string(backends[1].secret))
} else if backend := cfg.GetBackend(mustParse(url1)); backend != backends[0] {
Expand All @@ -581,16 +582,16 @@ func TestBackendConfiguration_Etcd(t *testing.T) {
<-ch
if backends := sortBackends(cfg.GetBackends()); len(backends) != 3 {
t.Errorf("Expected three backends, got %+v", backends)
} else if backends[0].url != url1 {
t.Errorf("Expected backend url %s, got %s", url1, backends[0].url)
} else if !slices.Equal(backends[0].urls, []string{url1}) {
t.Errorf("Expected backend url %s, got %v", url1, backends[0].urls)
} else if string(backends[0].secret) != secret1 {
t.Errorf("Expected backend secret %s, got %s", secret1, string(backends[0].secret))
} else if backends[1].url != url2 {
t.Errorf("Expected backend url %s, got %s", url2, backends[1].url)
} else if !slices.Equal(backends[1].urls, []string{url2}) {
t.Errorf("Expected backend url %s, got %v", url2, backends[1].urls)
} else if string(backends[1].secret) != secret2 {
t.Errorf("Expected backend secret %s, got %s", secret2, string(backends[1].secret))
} else if backends[2].url != url3 {
t.Errorf("Expected backend url %s, got %s", url3, backends[2].url)
} else if !slices.Equal(backends[2].urls, []string{url3}) {
t.Errorf("Expected backend url %s, got %v", url3, backends[2].urls)
} else if string(backends[2].secret) != secret3 {
t.Errorf("Expected backend secret %s, got %s", secret3, string(backends[2].secret))
} else if backend := cfg.GetBackend(mustParse(url1)); backend != backends[0] {
Expand All @@ -606,12 +607,12 @@ func TestBackendConfiguration_Etcd(t *testing.T) {
<-ch
if backends := sortBackends(cfg.GetBackends()); len(backends) != 2 {
t.Errorf("Expected two backends, got %+v", backends)
} else if backends[0].url != url2 {
t.Errorf("Expected backend url %s, got %s", url2, backends[0].url)
} else if !slices.Equal(backends[0].urls, []string{url2}) {
t.Errorf("Expected backend url %s, got %v", url2, backends[0].urls)
} else if string(backends[0].secret) != secret2 {
t.Errorf("Expected backend secret %s, got %s", secret2, string(backends[0].secret))
} else if backends[1].url != url3 {
t.Errorf("Expected backend url %s, got %s", url3, backends[1].url)
} else if !slices.Equal(backends[1].urls, []string{url3}) {
t.Errorf("Expected backend url %s, got %v", url3, backends[1].urls)
} else if string(backends[1].secret) != secret3 {
t.Errorf("Expected backend secret %s, got %s", secret3, string(backends[1].secret))
}
Expand All @@ -621,8 +622,8 @@ func TestBackendConfiguration_Etcd(t *testing.T) {
<-ch
if backends := sortBackends(cfg.GetBackends()); len(backends) != 1 {
t.Errorf("Expected one backend, got %+v", backends)
} else if backends[0].url != url3 {
t.Errorf("Expected backend url %s, got %s", url3, backends[0].url)
} else if !slices.Equal(backends[0].urls, []string{url3}) {
t.Errorf("Expected backend url %s, got %v", url3, backends[0].urls)
} else if string(backends[0].secret) != secret3 {
t.Errorf("Expected backend secret %s, got %s", secret3, string(backends[0].secret))
}
Expand Down
22 changes: 16 additions & 6 deletions backend_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 3 additions & 4 deletions backend_storage_etcd.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",

Expand Down
19 changes: 8 additions & 11 deletions backend_storage_static.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ func NewBackendStorageStatic(config *goconf.ConfigFile) (BackendStorage, error)
compatBackend = &Backend{
id: "compat",
secret: []byte(commonSecret),
compat: true,

allowHttp: allowHttp,

Expand All @@ -69,7 +68,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, ", "))
}
numBackends += len(configuredBackends)
}
Expand All @@ -93,7 +92,6 @@ func NewBackendStorageStatic(config *goconf.ConfigFile) (BackendStorage, error)
compatBackend = &Backend{
id: "compat",
secret: []byte(commonSecret),
compat: true,

allowHttp: allowHttp,

Expand Down Expand Up @@ -137,7 +135,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, ", "))
}
statsBackendsCurrent.Sub(float64(len(oldBackends)))
}
Expand All @@ -157,22 +155,22 @@ 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, ", "))
break
}
index++
}
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:]...)
statsBackendsCurrent.Dec()
}
}

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, ", "))
}
statsBackendsCurrent.Add(float64(len(backends)))
}
Expand Down Expand Up @@ -247,10 +245,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",

Expand Down
27 changes: 7 additions & 20 deletions clientsession.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import (
"fmt"
"log"
"net/url"
"strings"
"sync"
"sync/atomic"
"time"
Expand All @@ -39,6 +38,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"
)

Expand Down Expand Up @@ -122,24 +123,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
Expand Down Expand Up @@ -299,6 +282,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
}
Expand Down Expand Up @@ -505,7 +492,7 @@ func (s *ClientSession) doUnsubscribeRoomEvents(notify bool) {
request := NewBackendClientRoomRequest(room.Id(), s.userId, sid)
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)
Expand Down
Loading
Loading