mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-17 03:43:11 -06:00
Implement federated public rooms
- Make thirdparty endpoint r0 so riot-web loads the public room list
This commit is contained in:
parent
31bc5ce45d
commit
0c51a17acd
|
|
@ -390,7 +390,7 @@ func Setup(
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodGet, http.MethodOptions)
|
).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
|
||||||
unstableMux.Handle("/thirdparty/protocols",
|
r0mux.Handle("/thirdparty/protocols",
|
||||||
common.MakeExternalAPI("thirdparty_protocols", func(req *http.Request) util.JSONResponse {
|
common.MakeExternalAPI("thirdparty_protocols", func(req *http.Request) util.JSONResponse {
|
||||||
// TODO: Return the third party protcols
|
// TODO: Return the third party protcols
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ func main() {
|
||||||
)
|
)
|
||||||
federationapi.SetupFederationAPIComponent(base, accountDB, deviceDB, federation, &keyRing, alias, input, query, asQuery, fedSenderAPI)
|
federationapi.SetupFederationAPIComponent(base, accountDB, deviceDB, federation, &keyRing, alias, input, query, asQuery, fedSenderAPI)
|
||||||
mediaapi.SetupMediaAPIComponent(base, deviceDB)
|
mediaapi.SetupMediaAPIComponent(base, deviceDB)
|
||||||
publicroomsapi.SetupPublicRoomsAPIComponent(base, deviceDB, query, p2pPublicRoomProvider)
|
publicroomsapi.SetupPublicRoomsAPIComponent(base, deviceDB, query, federation, p2pPublicRoomProvider)
|
||||||
syncapi.SetupSyncAPIComponent(base, deviceDB, accountDB, query, federation, cfg)
|
syncapi.SetupSyncAPIComponent(base, deviceDB, accountDB, query, federation, cfg)
|
||||||
|
|
||||||
httpHandler := common.WrapHandlerInCORS(base.APIMux)
|
httpHandler := common.WrapHandlerInCORS(base.APIMux)
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/matrix-org/go-http-js-libp2p/go_http_js_libp2p"
|
"github.com/matrix-org/go-http-js-libp2p/go_http_js_libp2p"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -39,10 +37,6 @@ func (p *libp2pPublicRoomsProvider) foundProviders(peerInfos []go_http_js_libp2p
|
||||||
p.providers = peerInfos
|
p.providers = peerInfos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *libp2pPublicRoomsProvider) PollInterval() time.Duration {
|
|
||||||
return 10 * time.Second
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *libp2pPublicRoomsProvider) Homeservers() []string {
|
func (p *libp2pPublicRoomsProvider) Homeservers() []string {
|
||||||
result := make([]string, len(p.providers))
|
result := make([]string, len(p.providers))
|
||||||
for i := range p.providers {
|
for i := range p.providers {
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,13 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/httputil"
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
"github.com/matrix-org/dendrite/publicroomsapi/storage"
|
"github.com/matrix-org/dendrite/publicroomsapi/storage"
|
||||||
|
"github.com/matrix-org/dendrite/publicroomsapi/types"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
)
|
)
|
||||||
|
|
@ -44,7 +47,7 @@ func GetPostPublicRooms(
|
||||||
if fillErr := fillPublicRoomsReq(req, &request); fillErr != nil {
|
if fillErr := fillPublicRoomsReq(req, &request); fillErr != nil {
|
||||||
return *fillErr
|
return *fillErr
|
||||||
}
|
}
|
||||||
response, err := PublicRooms(req.Context(), request, publicRoomDatabase)
|
response, err := publicRooms(req.Context(), request, publicRoomDatabase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
}
|
}
|
||||||
|
|
@ -54,7 +57,123 @@ func GetPostPublicRooms(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func PublicRooms(ctx context.Context, request PublicRoomReq, publicRoomDatabase storage.Database) (*gomatrixserverlib.RespPublicRooms, error) {
|
// GetPostPublicRoomsWithExternal is the same as GetPostPublicRooms but also mixes in public rooms from the provider supplied.
|
||||||
|
func GetPostPublicRoomsWithExternal(
|
||||||
|
req *http.Request, publicRoomDatabase storage.Database, fedClient *gomatrixserverlib.FederationClient,
|
||||||
|
extRoomsProvider types.ExternalPublicRoomsProvider,
|
||||||
|
) util.JSONResponse {
|
||||||
|
var request PublicRoomReq
|
||||||
|
if fillErr := fillPublicRoomsReq(req, &request); fillErr != nil {
|
||||||
|
return *fillErr
|
||||||
|
}
|
||||||
|
response, err := publicRooms(req.Context(), request, publicRoomDatabase)
|
||||||
|
if err != nil {
|
||||||
|
return jsonerror.InternalServerError()
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.Since != "" {
|
||||||
|
// TODO: handle pagination tokens sensibly rather than ignoring them.
|
||||||
|
// ignore paginated requests since we don't handle them yet over federation.
|
||||||
|
// Only the initial request will contain federated rooms.
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: response,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have already hit the limit on the number of rooms, bail.
|
||||||
|
var limit int
|
||||||
|
if request.Limit > 0 {
|
||||||
|
limit = int(request.Limit) - len(response.Chunk)
|
||||||
|
if limit <= 0 {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: response,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// downcasting `limit` is safe as we know it isn't bigger than request.Limit which is int16
|
||||||
|
fedRooms := bulkFetchPublicRoomsFromServers(req.Context(), fedClient, extRoomsProvider.Homeservers(), int16(limit))
|
||||||
|
response.Chunk = append(response.Chunk, fedRooms...)
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: response,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bulkFetchPublicRoomsFromServers fetches public rooms from the list of homeservers.
|
||||||
|
// Returns a list of public rooms up to the limit specified.
|
||||||
|
func bulkFetchPublicRoomsFromServers(
|
||||||
|
ctx context.Context, fedClient *gomatrixserverlib.FederationClient, homeservers []string, limit int16,
|
||||||
|
) (publicRooms []gomatrixserverlib.PublicRoom) {
|
||||||
|
// follow pipeline semantics, see https://blog.golang.org/pipelines for more info.
|
||||||
|
// goroutines send rooms to this channel
|
||||||
|
roomCh := make(chan gomatrixserverlib.PublicRoom, int(limit))
|
||||||
|
// signalling channel to tell goroutines to stop sending rooms and quit
|
||||||
|
done := make(chan bool)
|
||||||
|
// signalling to say when we can close the room channel
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(len(homeservers))
|
||||||
|
// concurrently query for public rooms
|
||||||
|
for _, hs := range homeservers {
|
||||||
|
go func(homeserverDomain string) {
|
||||||
|
defer wg.Done()
|
||||||
|
util.GetLogger(ctx).WithField("hs", homeserverDomain).Info("Querying HS for public rooms")
|
||||||
|
fres, err := fedClient.GetPublicRooms(ctx, gomatrixserverlib.ServerName(homeserverDomain), int(limit), "", false, "")
|
||||||
|
if err != nil {
|
||||||
|
util.GetLogger(ctx).WithError(err).WithField("hs", homeserverDomain).Warn(
|
||||||
|
"bulkFetchPublicRoomsFromServers: failed to query hs",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, room := range fres.Chunk {
|
||||||
|
// atomically send a room or stop
|
||||||
|
select {
|
||||||
|
case roomCh <- room:
|
||||||
|
case <-done:
|
||||||
|
util.GetLogger(ctx).WithError(err).WithField("hs", homeserverDomain).Info("Interruped whilst sending rooms")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(hs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the room channel when the goroutines have quit so we don't leak, but don't let it stop the in-flight request.
|
||||||
|
// This also allows the request to fail fast if all HSes experience errors as it will cause the room channel to be
|
||||||
|
// closed.
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
util.GetLogger(ctx).Info("Cleaning up resources")
|
||||||
|
close(roomCh)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// fan-in results with timeout. We stop when we reach the limit.
|
||||||
|
FanIn:
|
||||||
|
for len(publicRooms) < int(limit) || limit == 0 {
|
||||||
|
// add a room or timeout
|
||||||
|
select {
|
||||||
|
case room, ok := <-roomCh:
|
||||||
|
if !ok {
|
||||||
|
util.GetLogger(ctx).Info("All homeservers have been queried, returning results.")
|
||||||
|
break FanIn
|
||||||
|
}
|
||||||
|
publicRooms = append(publicRooms, room)
|
||||||
|
case <-time.After(15 * time.Second): // we've waited long enough, let's tell the client what we got.
|
||||||
|
util.GetLogger(ctx).Info("Waited 15s for federated public rooms, returning early")
|
||||||
|
break FanIn
|
||||||
|
case <-ctx.Done(): // the client hung up on us, let's stop.
|
||||||
|
util.GetLogger(ctx).Info("Client hung up, returning early")
|
||||||
|
break FanIn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// tell goroutines to stop
|
||||||
|
close(done)
|
||||||
|
|
||||||
|
return publicRooms
|
||||||
|
}
|
||||||
|
|
||||||
|
func publicRooms(ctx context.Context, request PublicRoomReq, publicRoomDatabase storage.Database) (*gomatrixserverlib.RespPublicRooms, error) {
|
||||||
var response gomatrixserverlib.RespPublicRooms
|
var response gomatrixserverlib.RespPublicRooms
|
||||||
var limit int16
|
var limit int16
|
||||||
var offset int64
|
var offset int64
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/matrix-org/dendrite/publicroomsapi/storage"
|
"github.com/matrix-org/dendrite/publicroomsapi/storage"
|
||||||
"github.com/matrix-org/dendrite/publicroomsapi/types"
|
"github.com/matrix-org/dendrite/publicroomsapi/types"
|
||||||
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -31,6 +32,7 @@ func SetupPublicRoomsAPIComponent(
|
||||||
base *basecomponent.BaseDendrite,
|
base *basecomponent.BaseDendrite,
|
||||||
deviceDB devices.Database,
|
deviceDB devices.Database,
|
||||||
rsQueryAPI roomserverAPI.RoomserverQueryAPI,
|
rsQueryAPI roomserverAPI.RoomserverQueryAPI,
|
||||||
|
fedClient *gomatrixserverlib.FederationClient,
|
||||||
extRoomsProvider types.ExternalPublicRoomsProvider,
|
extRoomsProvider types.ExternalPublicRoomsProvider,
|
||||||
) {
|
) {
|
||||||
publicRoomsDB, err := storage.NewPublicRoomsServerDatabase(string(base.Cfg.Database.PublicRoomsAPI))
|
publicRoomsDB, err := storage.NewPublicRoomsServerDatabase(string(base.Cfg.Database.PublicRoomsAPI))
|
||||||
|
|
@ -45,5 +47,5 @@ func SetupPublicRoomsAPIComponent(
|
||||||
logrus.WithError(err).Panic("failed to start public rooms server consumer")
|
logrus.WithError(err).Panic("failed to start public rooms server consumer")
|
||||||
}
|
}
|
||||||
|
|
||||||
routing.Setup(base.APIMux, deviceDB, publicRoomsDB, extRoomsProvider)
|
routing.Setup(base.APIMux, deviceDB, publicRoomsDB, fedClient, extRoomsProvider)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,7 @@
|
||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth"
|
"github.com/matrix-org/dendrite/clientapi/auth"
|
||||||
|
|
@ -27,6 +25,7 @@ import (
|
||||||
"github.com/matrix-org/dendrite/publicroomsapi/directory"
|
"github.com/matrix-org/dendrite/publicroomsapi/directory"
|
||||||
"github.com/matrix-org/dendrite/publicroomsapi/storage"
|
"github.com/matrix-org/dendrite/publicroomsapi/storage"
|
||||||
"github.com/matrix-org/dendrite/publicroomsapi/types"
|
"github.com/matrix-org/dendrite/publicroomsapi/types"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -37,7 +36,10 @@ const pathPrefixR0 = "/_matrix/client/r0"
|
||||||
// Due to Setup being used to call many other functions, a gocyclo nolint is
|
// Due to Setup being used to call many other functions, a gocyclo nolint is
|
||||||
// applied:
|
// applied:
|
||||||
// nolint: gocyclo
|
// nolint: gocyclo
|
||||||
func Setup(apiMux *mux.Router, deviceDB devices.Database, publicRoomsDB storage.Database, extRoomsProvider types.ExternalPublicRoomsProvider) {
|
func Setup(
|
||||||
|
apiMux *mux.Router, deviceDB devices.Database, publicRoomsDB storage.Database,
|
||||||
|
fedClient *gomatrixserverlib.FederationClient, extRoomsProvider types.ExternalPublicRoomsProvider,
|
||||||
|
) {
|
||||||
r0mux := apiMux.PathPrefix(pathPrefixR0).Subrouter()
|
r0mux := apiMux.PathPrefix(pathPrefixR0).Subrouter()
|
||||||
|
|
||||||
authData := auth.Data{
|
authData := auth.Data{
|
||||||
|
|
@ -68,6 +70,7 @@ func Setup(apiMux *mux.Router, deviceDB devices.Database, publicRoomsDB storage.
|
||||||
r0mux.Handle("/publicRooms",
|
r0mux.Handle("/publicRooms",
|
||||||
common.MakeExternalAPI("public_rooms", func(req *http.Request) util.JSONResponse {
|
common.MakeExternalAPI("public_rooms", func(req *http.Request) util.JSONResponse {
|
||||||
if extRoomsProvider != nil {
|
if extRoomsProvider != nil {
|
||||||
|
return directory.GetPostPublicRoomsWithExternal(req, publicRoomsDB, fedClient, extRoomsProvider)
|
||||||
}
|
}
|
||||||
return directory.GetPostPublicRooms(req, publicRoomsDB)
|
return directory.GetPostPublicRooms(req, publicRoomsDB)
|
||||||
}),
|
}),
|
||||||
|
|
@ -79,18 +82,4 @@ func Setup(apiMux *mux.Router, deviceDB devices.Database, publicRoomsDB storage.
|
||||||
return directory.GetPostPublicRooms(req, publicRoomsDB)
|
return directory.GetPostPublicRooms(req, publicRoomsDB)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodGet)
|
).Methods(http.MethodGet)
|
||||||
|
|
||||||
if extRoomsProvider != nil {
|
|
||||||
pollExternalPublicRooms(extRoomsProvider)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func pollExternalPublicRooms(extRoomsProvider types.ExternalPublicRoomsProvider) {
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
hses := extRoomsProvider.Homeservers()
|
|
||||||
fmt.Println(hses)
|
|
||||||
time.Sleep(extRoomsProvider.PollInterval())
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/common"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -66,7 +67,7 @@ const selectPublicRoomsWithLimitSQL = "" +
|
||||||
"SELECT room_id, joined_members, aliases, canonical_alias, name, topic, world_readable, guest_can_join, avatar_url" +
|
"SELECT room_id, joined_members, aliases, canonical_alias, name, topic, world_readable, guest_can_join, avatar_url" +
|
||||||
" FROM publicroomsapi_public_rooms WHERE visibility = true" +
|
" FROM publicroomsapi_public_rooms WHERE visibility = true" +
|
||||||
" ORDER BY joined_members DESC" +
|
" ORDER BY joined_members DESC" +
|
||||||
" LIMIT $2 OFFSET $1"
|
" LIMIT $1 OFFSET $2"
|
||||||
|
|
||||||
const selectPublicRoomsWithFilterSQL = "" +
|
const selectPublicRoomsWithFilterSQL = "" +
|
||||||
"SELECT room_id, joined_members, aliases, canonical_alias, name, topic, world_readable, guest_can_join, avatar_url" +
|
"SELECT room_id, joined_members, aliases, canonical_alias, name, topic, world_readable, guest_can_join, avatar_url" +
|
||||||
|
|
@ -192,6 +193,7 @@ func (s *publicRoomsStatements) selectPublicRooms(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []gomatrixserverlib.PublicRoom{}, nil
|
return []gomatrixserverlib.PublicRoom{}, nil
|
||||||
}
|
}
|
||||||
|
defer common.CloseAndLogIfError(ctx, rows, "selectPublicRooms failed to close rows")
|
||||||
|
|
||||||
rooms := []gomatrixserverlib.PublicRoom{}
|
rooms := []gomatrixserverlib.PublicRoom{}
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,11 @@
|
||||||
|
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// ExternalPublicRoomsProvider provides a list of homeservers who should be queried
|
// ExternalPublicRoomsProvider provides a list of homeservers who should be queried
|
||||||
// periodically for a list of public rooms on their server.
|
// periodically for a list of public rooms on their server.
|
||||||
type ExternalPublicRoomsProvider interface {
|
type ExternalPublicRoomsProvider interface {
|
||||||
// The interval at which to check servers
|
|
||||||
PollInterval() time.Duration
|
|
||||||
// The list of homeserver domains to query. These servers will receive a request
|
// The list of homeserver domains to query. These servers will receive a request
|
||||||
// via this API: https://matrix.org/docs/spec/server_server/latest#public-room-directory
|
// via this API: https://matrix.org/docs/spec/server_server/latest#public-room-directory
|
||||||
|
// This will be called -on demand- by clients, so cache appropriately!
|
||||||
Homeservers() []string
|
Homeservers() []string
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue