Change API and rename to ExtraPublicRoomsProvider

This commit is contained in:
Kegan Dougal 2020-07-02 19:31:12 +01:00
parent 38caf8e5b7
commit cd3da772de
7 changed files with 91 additions and 31 deletions

View file

@ -14,9 +14,10 @@
package api package api
type ExternalPublicRoomsProvider interface { import "github.com/matrix-org/gomatrixserverlib"
// 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 // ExtraPublicRoomsProvider provides a way to inject extra published rooms into /publicRooms requests.
// This will be called -on demand- by clients, so cache appropriately! type ExtraPublicRoomsProvider interface {
Homeservers() []string // Rooms returns the extra rooms. This is called on-demand by clients, so cache appropriately.
Rooms() []gomatrixserverlib.PublicRoom
} }

View file

@ -48,7 +48,7 @@ func AddPublicRoutes(
transactionsCache *transactions.Cache, transactionsCache *transactions.Cache,
fsAPI federationSenderAPI.FederationSenderInternalAPI, fsAPI federationSenderAPI.FederationSenderInternalAPI,
userAPI userapi.UserInternalAPI, userAPI userapi.UserInternalAPI,
extRoomsProvider api.ExternalPublicRoomsProvider, extRoomsProvider api.ExtraPublicRoomsProvider,
) { ) {
syncProducer := &producers.SyncAPIProducer{ syncProducer := &producers.SyncAPIProducer{
Producer: producer, Producer: producer,

View file

@ -22,7 +22,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"time"
"github.com/matrix-org/dendrite/clientapi/api" "github.com/matrix-org/dendrite/clientapi/api"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
@ -33,6 +32,11 @@ import (
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
var (
cacheMu sync.Mutex
publicRoomsCache []gomatrixserverlib.PublicRoom
)
type PublicRoomReq struct { type PublicRoomReq struct {
Since string `json:"since,omitempty"` Since string `json:"since,omitempty"`
Limit int16 `json:"limit,omitempty"` Limit int16 `json:"limit,omitempty"`
@ -46,13 +50,15 @@ type filter struct {
// GetPostPublicRooms implements GET and POST /publicRooms // GetPostPublicRooms implements GET and POST /publicRooms
func GetPostPublicRooms( func GetPostPublicRooms(
req *http.Request, rsAPI roomserverAPI.RoomserverInternalAPI, stateAPI currentstateAPI.CurrentStateInternalAPI, req *http.Request, rsAPI roomserverAPI.RoomserverInternalAPI, stateAPI currentstateAPI.CurrentStateInternalAPI,
extRoomsProvider api.ExtraPublicRoomsProvider,
) util.JSONResponse { ) util.JSONResponse {
var request PublicRoomReq var request PublicRoomReq
if fillErr := fillPublicRoomsReq(req, &request); fillErr != nil { if fillErr := fillPublicRoomsReq(req, &request); fillErr != nil {
return *fillErr return *fillErr
} }
response, err := publicRooms(req.Context(), request, rsAPI, stateAPI) response, err := publicRooms(req.Context(), request, rsAPI, stateAPI, extRoomsProvider)
if err != nil { if err != nil {
util.GetLogger(req.Context()).WithError(err).Errorf("failed to work out public rooms")
return jsonerror.InternalServerError() return jsonerror.InternalServerError()
} }
return util.JSONResponse{ return util.JSONResponse{
@ -64,13 +70,13 @@ func GetPostPublicRooms(
// GetPostPublicRoomsWithExternal is the same as GetPostPublicRooms but also mixes in public rooms from the provider supplied. // GetPostPublicRoomsWithExternal is the same as GetPostPublicRooms but also mixes in public rooms from the provider supplied.
func GetPostPublicRoomsWithExternal( func GetPostPublicRoomsWithExternal(
req *http.Request, rsAPI roomserverAPI.RoomserverInternalAPI, stateAPI currentstateAPI.CurrentStateInternalAPI, req *http.Request, rsAPI roomserverAPI.RoomserverInternalAPI, stateAPI currentstateAPI.CurrentStateInternalAPI,
fedClient *gomatrixserverlib.FederationClient, extRoomsProvider api.ExternalPublicRoomsProvider, fedClient *gomatrixserverlib.FederationClient, extRoomsProvider api.ExtraPublicRoomsProvider,
) util.JSONResponse { ) util.JSONResponse {
var request PublicRoomReq var request PublicRoomReq
if fillErr := fillPublicRoomsReq(req, &request); fillErr != nil { if fillErr := fillPublicRoomsReq(req, &request); fillErr != nil {
return *fillErr return *fillErr
} }
response, err := publicRooms(req.Context(), request, rsAPI, stateAPI) response, err := publicRooms(req.Context(), request, rsAPI, stateAPI, extRoomsProvider)
if err != nil { if err != nil {
return jsonerror.InternalServerError() return jsonerror.InternalServerError()
} }
@ -98,8 +104,8 @@ func GetPostPublicRoomsWithExternal(
} }
// downcasting `limit` is safe as we know it isn't bigger than request.Limit which is int16 // 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)) //fedRooms := bulkFetchPublicRoomsFromServers(req.Context(), fedClient, extRoomsProvider.Homeservers(), int16(limit))
response.Chunk = append(response.Chunk, fedRooms...) //response.Chunk = append(response.Chunk, fedRooms...)
// de-duplicate rooms with the same room ID. We can join the room via any of these aliases as we know these servers // de-duplicate rooms with the same room ID. We can join the room via any of these aliases as we know these servers
// are alive and well, so we arbitrarily pick one (purposefully shuffling them to spread the load a bit) // are alive and well, so we arbitrarily pick one (purposefully shuffling them to spread the load a bit)
@ -128,6 +134,7 @@ func GetPostPublicRoomsWithExternal(
} }
} }
/*
// bulkFetchPublicRoomsFromServers fetches public rooms from the list of homeservers. // bulkFetchPublicRoomsFromServers fetches public rooms from the list of homeservers.
// Returns a list of public rooms up to the limit specified. // Returns a list of public rooms up to the limit specified.
func bulkFetchPublicRoomsFromServers( func bulkFetchPublicRoomsFromServers(
@ -198,9 +205,11 @@ FanIn:
return publicRooms return publicRooms
} }
*/
func publicRooms(ctx context.Context, request PublicRoomReq, rsAPI roomserverAPI.RoomserverInternalAPI, func publicRooms(ctx context.Context, request PublicRoomReq, rsAPI roomserverAPI.RoomserverInternalAPI,
stateAPI currentstateAPI.CurrentStateInternalAPI) (*gomatrixserverlib.RespPublicRooms, error) { stateAPI currentstateAPI.CurrentStateInternalAPI, extRoomsProvider api.ExtraPublicRoomsProvider,
) (*gomatrixserverlib.RespPublicRooms, error) {
var response gomatrixserverlib.RespPublicRooms var response gomatrixserverlib.RespPublicRooms
var limit int16 var limit int16
@ -216,23 +225,25 @@ func publicRooms(ctx context.Context, request PublicRoomReq, rsAPI roomserverAPI
util.GetLogger(ctx).WithError(err).Error("strconv.ParseInt failed") util.GetLogger(ctx).WithError(err).Error("strconv.ParseInt failed")
return nil, err return nil, err
} }
err = nil
var queryRes roomserverAPI.QueryPublishedRoomsResponse var rooms []gomatrixserverlib.PublicRoom
err = rsAPI.QueryPublishedRooms(ctx, &roomserverAPI.QueryPublishedRoomsRequest{}, &queryRes) if request.Since == "" {
if err != nil { rooms = refreshPublicRoomCache(ctx, rsAPI, extRoomsProvider, stateAPI)
util.GetLogger(ctx).WithError(err).Error("QueryPublishedRooms failed") } else {
return nil, err rooms = getPublicRoomsFromCache()
} }
response.TotalRoomCountEstimate = len(queryRes.RoomIDs)
roomIDs, prev, next := sliceInto(queryRes.RoomIDs, offset, limit) response.TotalRoomCountEstimate = len(rooms)
chunk, prev, next := sliceInto(rooms, offset, limit)
if prev >= 0 { if prev >= 0 {
response.PrevBatch = "T" + strconv.Itoa(prev) response.PrevBatch = "T" + strconv.Itoa(prev)
} }
if next >= 0 { if next >= 0 {
response.NextBatch = "T" + strconv.Itoa(next) response.NextBatch = "T" + strconv.Itoa(next)
} }
response.Chunk, err = fillInRooms(ctx, roomIDs, stateAPI) response.Chunk = chunk
return &response, err return &response, err
} }
@ -348,7 +359,7 @@ func fillInRooms(ctx context.Context, roomIDs []string, stateAPI currentstateAPI
// limit=3&since=6 => G (prev='3', next='') // limit=3&since=6 => G (prev='3', next='')
// //
// A value of '-1' for prev/next indicates no position. // A value of '-1' for prev/next indicates no position.
func sliceInto(slice []string, since int64, limit int16) (subset []string, prev, next int) { func sliceInto(slice []gomatrixserverlib.PublicRoom, since int64, limit int16) (subset []gomatrixserverlib.PublicRoom, prev, next int) {
prev = -1 prev = -1
next = -1 next = -1
@ -371,3 +382,41 @@ func sliceInto(slice []string, since int64, limit int16) (subset []string, prev,
subset = slice[since:nextIndex] subset = slice[since:nextIndex]
return return
} }
func refreshPublicRoomCache(
ctx context.Context, rsAPI roomserverAPI.RoomserverInternalAPI, extRoomsProvider api.ExtraPublicRoomsProvider,
stateAPI currentstateAPI.CurrentStateInternalAPI,
) []gomatrixserverlib.PublicRoom {
cacheMu.Lock()
defer cacheMu.Unlock()
var extraRooms []gomatrixserverlib.PublicRoom
if extRoomsProvider != nil {
extraRooms = extRoomsProvider.Rooms()
}
var queryRes roomserverAPI.QueryPublishedRoomsResponse
err := rsAPI.QueryPublishedRooms(ctx, &roomserverAPI.QueryPublishedRoomsRequest{}, &queryRes)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("QueryPublishedRooms failed")
return publicRoomsCache
}
pubRooms, err := fillInRooms(ctx, queryRes.RoomIDs, stateAPI)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("fillInRooms failed")
return publicRoomsCache
}
publicRoomsCache = []gomatrixserverlib.PublicRoom{}
publicRoomsCache = append(publicRoomsCache, pubRooms...)
publicRoomsCache = append(publicRoomsCache, extraRooms...)
// sort by total joined member count (big to small)
sort.SliceStable(publicRoomsCache, func(i, j int) bool {
return publicRoomsCache[i].JoinedMembersCount > publicRoomsCache[j].JoinedMembersCount
})
return publicRoomsCache
}
func getPublicRoomsFromCache() []gomatrixserverlib.PublicRoom {
cacheMu.Lock()
defer cacheMu.Unlock()
return publicRoomsCache
}

View file

@ -3,16 +3,26 @@ package routing
import ( import (
"reflect" "reflect"
"testing" "testing"
"github.com/matrix-org/gomatrixserverlib"
) )
func pubRoom(name string) gomatrixserverlib.PublicRoom {
return gomatrixserverlib.PublicRoom{
Name: name,
}
}
func TestSliceInto(t *testing.T) { func TestSliceInto(t *testing.T) {
slice := []string{"a", "b", "c", "d", "e", "f", "g"} slice := []gomatrixserverlib.PublicRoom{
pubRoom("a"), pubRoom("b"), pubRoom("c"), pubRoom("d"), pubRoom("e"), pubRoom("f"), pubRoom("g"),
}
limit := int16(3) limit := int16(3)
testCases := []struct { testCases := []struct {
since int64 since int64
wantPrev int wantPrev int
wantNext int wantNext int
wantSubset []string wantSubset []gomatrixserverlib.PublicRoom
}{ }{
{ {
since: 0, since: 0,

View file

@ -61,7 +61,7 @@ func Setup(
transactionsCache *transactions.Cache, transactionsCache *transactions.Cache,
federationSender federationSenderAPI.FederationSenderInternalAPI, federationSender federationSenderAPI.FederationSenderInternalAPI,
stateAPI currentstateAPI.CurrentStateInternalAPI, stateAPI currentstateAPI.CurrentStateInternalAPI,
extRoomsProvider api.ExternalPublicRoomsProvider, extRoomsProvider api.ExtraPublicRoomsProvider,
) { ) {
publicAPIMux.Handle("/client/versions", publicAPIMux.Handle("/client/versions",
@ -313,11 +313,7 @@ func Setup(
).Methods(http.MethodPut, http.MethodOptions) ).Methods(http.MethodPut, http.MethodOptions)
r0mux.Handle("/publicRooms", r0mux.Handle("/publicRooms",
httputil.MakeExternalAPI("public_rooms", func(req *http.Request) util.JSONResponse { httputil.MakeExternalAPI("public_rooms", func(req *http.Request) util.JSONResponse {
/* TODO: return GetPostPublicRooms(req, rsAPI, stateAPI, extRoomsProvider)
if extRoomsProvider != nil {
return GetPostPublicRoomsWithExternal(req, stateAPI, fedClient, extRoomsProvider)
} */
return GetPostPublicRooms(req, rsAPI, stateAPI)
}), }),
).Methods(http.MethodGet, http.MethodPost, http.MethodOptions) ).Methods(http.MethodGet, http.MethodPost, http.MethodOptions)

View file

@ -62,6 +62,10 @@ func (p *libp2pPublicRoomsProvider) foundProviders(peerInfos []go_http_js_libp2p
p.providers = peerInfos p.providers = peerInfos
} }
func (p *libp2pPublicRoomsProvider) Rooms() []gomatrixserverlib.PublicRoom {
return nil
}
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 {

View file

@ -58,7 +58,7 @@ type Monolith struct {
StateAPI currentstateAPI.CurrentStateInternalAPI StateAPI currentstateAPI.CurrentStateInternalAPI
// Optional // Optional
ExtPublicRoomsProvider api.ExternalPublicRoomsProvider ExtPublicRoomsProvider api.ExtraPublicRoomsProvider
} }
// AddAllPublicRoutes attaches all public paths to the given router // AddAllPublicRoutes attaches all public paths to the given router