From e239fb10f36662d38ab9d1cad5a0bc65518c1704 Mon Sep 17 00:00:00 2001 From: Alex Chen Date: Wed, 2 Oct 2019 00:09:47 +0800 Subject: [PATCH] Add missing servers field in /directory/room/:alias response (#732) --- clientapi/clientapi.go | 4 +- clientapi/routing/directory.go | 80 ++++++++++++++++---------- clientapi/routing/routing.go | 4 +- cmd/dendrite-client-api-server/main.go | 3 +- cmd/dendrite-monolith-server/main.go | 4 +- common/basecomponent/base.go | 7 +++ common/config/config.go | 9 +++ testfile | 10 ++++ 8 files changed, 85 insertions(+), 36 deletions(-) diff --git a/clientapi/clientapi.go b/clientapi/clientapi.go index 5b6e21c8..f3f3e08c 100644 --- a/clientapi/clientapi.go +++ b/clientapi/clientapi.go @@ -23,6 +23,7 @@ import ( "github.com/matrix-org/dendrite/clientapi/routing" "github.com/matrix-org/dendrite/common/basecomponent" "github.com/matrix-org/dendrite/common/transactions" + federationSenderAPI "github.com/matrix-org/dendrite/federationsender/api" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" typingServerAPI "github.com/matrix-org/dendrite/typingserver/api" "github.com/matrix-org/gomatrixserverlib" @@ -43,6 +44,7 @@ func SetupClientAPIComponent( typingInputAPI typingServerAPI.TypingServerInputAPI, asAPI appserviceAPI.AppServiceQueryAPI, transactionsCache *transactions.Cache, + fedSenderAPI federationSenderAPI.FederationSenderQueryAPI, ) { roomserverProducer := producers.NewRoomserverProducer(inputAPI) typingProducer := producers.NewTypingServerProducer(typingInputAPI) @@ -67,6 +69,6 @@ func SetupClientAPIComponent( routing.Setup( base.APIMux, *base.Cfg, roomserverProducer, queryAPI, aliasAPI, asAPI, accountsDB, deviceDB, federation, *keyRing, userUpdateProducer, - syncProducer, typingProducer, transactionsCache, + syncProducer, typingProducer, transactionsCache, fedSenderAPI, ) } diff --git a/clientapi/routing/directory.go b/clientapi/routing/directory.go index 0d91d042..574b275d 100644 --- a/clientapi/routing/directory.go +++ b/clientapi/routing/directory.go @@ -22,12 +22,24 @@ import ( "github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/common/config" + federationSenderAPI "github.com/matrix-org/dendrite/federationsender/api" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" - "github.com/matrix-org/gomatrix" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" ) +type roomDirectoryResponse struct { + RoomID string `json:"room_id"` + Servers []string `json:"servers"` +} + +func (r *roomDirectoryResponse) fillServers(servers []gomatrixserverlib.ServerName) { + r.Servers = make([]string, len(servers)) + for i, s := range servers { + r.Servers[i] = string(s) + } +} + // DirectoryRoom looks up a room alias func DirectoryRoom( req *http.Request, @@ -35,6 +47,7 @@ func DirectoryRoom( federation *gomatrixserverlib.FederationClient, cfg *config.Dendrite, rsAPI roomserverAPI.RoomserverAliasAPI, + fedSenderAPI federationSenderAPI.FederationSenderQueryAPI, ) util.JSONResponse { _, domain, err := gomatrixserverlib.SplitID('#', roomAlias) if err != nil { @@ -44,46 +57,51 @@ func DirectoryRoom( } } - if domain == cfg.Matrix.ServerName { - // Query the roomserver API to check if the alias exists locally - queryReq := roomserverAPI.GetRoomIDForAliasRequest{Alias: roomAlias} - var queryRes roomserverAPI.GetRoomIDForAliasResponse - if err = rsAPI.GetRoomIDForAlias(req.Context(), &queryReq, &queryRes); err != nil { - return httputil.LogThenError(req, err) + var res roomDirectoryResponse + + // Query the roomserver API to check if the alias exists locally. + queryReq := roomserverAPI.GetRoomIDForAliasRequest{Alias: roomAlias} + var queryRes roomserverAPI.GetRoomIDForAliasResponse + if err = rsAPI.GetRoomIDForAlias(req.Context(), &queryReq, &queryRes); err != nil { + return httputil.LogThenError(req, err) + } + + res.RoomID = queryRes.RoomID + + if res.RoomID == "" { + // If we don't know it locally, do a federation query. + // But don't send the query to ourselves. + if domain != cfg.Matrix.ServerName { + fedRes, fedErr := federation.LookupRoomAlias(req.Context(), domain, roomAlias) + if fedErr != nil { + // TODO: Return 502 if the remote server errored. + // TODO: Return 504 if the remote server timed out. + return httputil.LogThenError(req, fedErr) + } + res.RoomID = fedRes.RoomID + res.fillServers(fedRes.Servers) } - // List any roomIDs found associated with this alias - if len(queryRes.RoomID) > 0 { + if res.RoomID == "" { return util.JSONResponse{ - Code: http.StatusOK, - JSON: queryRes, + Code: http.StatusNotFound, + JSON: jsonerror.NotFound( + fmt.Sprintf("Room alias %s not found", roomAlias), + ), } } } else { - // Query the federation for this room alias - resp, err := federation.LookupRoomAlias(req.Context(), domain, roomAlias) - if err != nil { - switch err.(type) { - case gomatrix.HTTPError: - default: - // TODO: Return 502 if the remote server errored. - // TODO: Return 504 if the remote server timed out. - return httputil.LogThenError(req, err) - } - } - if len(resp.RoomID) > 0 { - return util.JSONResponse{ - Code: http.StatusOK, - JSON: resp, - } + joinedHostsReq := federationSenderAPI.QueryJoinedHostServerNamesInRoomRequest{RoomID: res.RoomID} + var joinedHostsRes federationSenderAPI.QueryJoinedHostServerNamesInRoomResponse + if err = fedSenderAPI.QueryJoinedHostServerNamesInRoom(req.Context(), &joinedHostsReq, &joinedHostsRes); err != nil { + return httputil.LogThenError(req, err) } + res.fillServers(joinedHostsRes.ServerNames) } return util.JSONResponse{ - Code: http.StatusNotFound, - JSON: jsonerror.NotFound( - fmt.Sprintf("Room alias %s not found", roomAlias), - ), + Code: http.StatusOK, + JSON: res, } } diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go index d4b323a2..4a36661d 100644 --- a/clientapi/routing/routing.go +++ b/clientapi/routing/routing.go @@ -30,6 +30,7 @@ import ( "github.com/matrix-org/dendrite/common" "github.com/matrix-org/dendrite/common/config" "github.com/matrix-org/dendrite/common/transactions" + federationSenderAPI "github.com/matrix-org/dendrite/federationsender/api" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" @@ -59,6 +60,7 @@ func Setup( syncProducer *producers.SyncAPIProducer, typingProducer *producers.TypingServerProducer, transactionsCache *transactions.Cache, + federationSender federationSenderAPI.FederationSenderQueryAPI, ) { apiMux.Handle("/_matrix/client/versions", @@ -185,7 +187,7 @@ func Setup( if err != nil { return util.ErrorResponse(err) } - return DirectoryRoom(req, vars["roomAlias"], federation, &cfg, aliasAPI) + return DirectoryRoom(req, vars["roomAlias"], federation, &cfg, aliasAPI, federationSender) }), ).Methods(http.MethodGet, http.MethodOptions) diff --git a/cmd/dendrite-client-api-server/main.go b/cmd/dendrite-client-api-server/main.go index dd065644..e273ecad 100644 --- a/cmd/dendrite-client-api-server/main.go +++ b/cmd/dendrite-client-api-server/main.go @@ -37,11 +37,12 @@ func main() { asQuery := base.CreateHTTPAppServiceAPIs() alias, input, query := base.CreateHTTPRoomserverAPIs() + fedSenderAPI := base.CreateHTTPFederationSenderAPIs() typingInputAPI := typingserver.SetupTypingServerComponent(base, cache.NewTypingCache()) clientapi.SetupClientAPIComponent( base, deviceDB, accountDB, federation, &keyRing, - alias, input, query, typingInputAPI, asQuery, transactions.New(), + alias, input, query, typingInputAPI, asQuery, transactions.New(), fedSenderAPI, ) base.SetupAndServeHTTP(string(base.Cfg.Listen.ClientAPI)) diff --git a/cmd/dendrite-monolith-server/main.go b/cmd/dendrite-monolith-server/main.go index 87a625b8..0a320616 100644 --- a/cmd/dendrite-monolith-server/main.go +++ b/cmd/dendrite-monolith-server/main.go @@ -60,14 +60,14 @@ func main() { asQuery := appservice.SetupAppServiceAPIComponent( base, accountDB, deviceDB, federation, alias, query, transactions.New(), ) + fedSenderAPI := federationsender.SetupFederationSenderComponent(base, federation, query) clientapi.SetupClientAPIComponent( base, deviceDB, accountDB, federation, &keyRing, alias, input, query, - typingInputAPI, asQuery, transactions.New(), + typingInputAPI, asQuery, transactions.New(), fedSenderAPI, ) federationapi.SetupFederationAPIComponent(base, accountDB, deviceDB, federation, &keyRing, alias, input, query, asQuery) - federationsender.SetupFederationSenderComponent(base, federation, query) mediaapi.SetupMediaAPIComponent(base, deviceDB) publicroomsapi.SetupPublicRoomsAPIComponent(base, deviceDB) syncapi.SetupSyncAPIComponent(base, deviceDB, accountDB, query) diff --git a/common/basecomponent/base.go b/common/basecomponent/base.go index 6a20aca3..503134b2 100644 --- a/common/basecomponent/base.go +++ b/common/basecomponent/base.go @@ -32,6 +32,7 @@ import ( appserviceAPI "github.com/matrix-org/dendrite/appservice/api" "github.com/matrix-org/dendrite/common/config" + federationSenderAPI "github.com/matrix-org/dendrite/federationsender/api" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" typingServerAPI "github.com/matrix-org/dendrite/typingserver/api" "github.com/sirupsen/logrus" @@ -107,6 +108,12 @@ func (b *BaseDendrite) CreateHTTPTypingServerAPIs() typingServerAPI.TypingServer return typingServerAPI.NewTypingServerInputAPIHTTP(b.Cfg.TypingServerURL(), nil) } +// CreateHTTPFederationSenderAPIs returns FederationSenderQueryAPI for hitting +// the federation sender over HTTP +func (b *BaseDendrite) CreateHTTPFederationSenderAPIs() federationSenderAPI.FederationSenderQueryAPI { + return federationSenderAPI.NewFederationSenderQueryAPIHTTP(b.Cfg.FederationSenderURL(), nil) +} + // CreateDeviceDB creates a new instance of the device database. Should only be // called once per component. func (b *BaseDendrite) CreateDeviceDB() *devices.Database { diff --git a/common/config/config.go b/common/config/config.go index 40232fb0..16cf2640 100644 --- a/common/config/config.go +++ b/common/config/config.go @@ -678,6 +678,15 @@ func (config *Dendrite) TypingServerURL() string { return "http://" + string(config.Listen.TypingServer) } +// FederationSenderURL returns an HTTP URL for where the federation sender is listening. +func (config *Dendrite) FederationSenderURL() string { + // Hard code the typing server to talk HTTP for now. + // If we support HTTPS we need to think of a practical way to do certificate validation. + // People setting up servers shouldn't need to get a certificate valid for the public + // internet for an internal API. + return "http://" + string(config.Listen.FederationSender) +} + // SetupTracing configures the opentracing using the supplied configuration. func (config *Dendrite) SetupTracing(serviceName string) (closer io.Closer, err error) { return config.Tracing.Jaeger.InitGlobalTracer( diff --git a/testfile b/testfile index 38506d71..5ad3426e 100644 --- a/testfile +++ b/testfile @@ -172,3 +172,13 @@ Outbound federation can query profile data /event/ does not allow access to events before the user joined Federation key API allows unsigned requests for keys Can paginate public room list +GET /directory/room/:room_alias yields room ID +PUT /directory/room/:room_alias creates alias +Room aliases can contain Unicode +Creators can delete alias +Alias creators can delete alias with no ops +Alias creators can delete canonical alias with no ops +Regular users cannot create room aliases within the AS namespace +Deleting a non-existent alias should return a 404 +Users can't delete other's aliases +Outbound federation can query room alias directory