diff --git a/federationsender/api/query.go b/federationsender/api/query.go new file mode 100644 index 00000000..ebc6e833 --- /dev/null +++ b/federationsender/api/query.go @@ -0,0 +1,98 @@ +package api + +import ( + "context" + "net/http" + + commonHTTP "github.com/matrix-org/dendrite/common/http" + "github.com/matrix-org/gomatrixserverlib" + + "github.com/matrix-org/dendrite/federationsender/types" + "github.com/opentracing/opentracing-go" +) + +// QueryJoinedHostsInRoomRequest is a request to QueryJoinedHostsInRoom +type QueryJoinedHostsInRoomRequest struct { + RoomID string `json:"room_id"` +} + +// QueryJoinedHostsInRoomResponse is a response to QueryJoinedHostsInRoom +type QueryJoinedHostsInRoomResponse struct { + JoinedHosts []types.JoinedHost `json:"joined_hosts"` +} + +// QueryJoinedHostServerNamesRequest is a request to QueryJoinedHostServerNames +type QueryJoinedHostServerNamesInRoomRequest struct { + RoomID string `json:"room_id"` +} + +// QueryJoinedHostServerNamesResponse is a response to QueryJoinedHostServerNames +type QueryJoinedHostServerNamesInRoomResponse struct { + ServerNames []gomatrixserverlib.ServerName `json:"server_names"` +} + +// FederationSenderQueryAPI is used to query information from the federation sender. +type FederationSenderQueryAPI interface { + // Query the joined hosts and the membership events accounting for their participation in a room. + // Note that if a server has multiple users in the room, it will have multiple entries in the returned slice. + // See `QueryJoinedHostServerNamesInRoom` for a de-duplicated version. + QueryJoinedHostsInRoom( + ctx context.Context, + request *QueryJoinedHostsInRoomRequest, + response *QueryJoinedHostsInRoomResponse, + ) error + // Query the server names of the joined hosts in a room. + // Unlike QueryJoinedHostsInRoom, this function returns a de-duplicated slice + // containing only the server names (without information for membership events). + QueryJoinedHostServerNamesInRoom( + ctx context.Context, + request *QueryJoinedHostServerNamesInRoomRequest, + response *QueryJoinedHostServerNamesInRoomResponse, + ) error +} + +// FederationSenderQueryJoinedHostsInRoomPath is the HTTP path for the QueryJoinedHostsInRoom API. +const FederationSenderQueryJoinedHostsInRoomPath = "/api/federationsender/queryJoinedHostsInRoom" + +// FederationSenderQueryJoinedHostServerNamesInRoomPath is the HTTP path for the QueryJoinedHostServerNamesInRoom API. +const FederationSenderQueryJoinedHostServerNamesInRoomPath = "/api/federationsender/queryJoinedHostServerNamesInRoom" + +// NewFederationSenderQueryAPIHTTP creates a FederationSenderQueryAPI implemented by talking to a HTTP POST API. +// If httpClient is nil then it uses the http.DefaultClient +func NewFederationSenderQueryAPIHTTP(federationSenderURL string, httpClient *http.Client) FederationSenderQueryAPI { + if httpClient == nil { + httpClient = http.DefaultClient + } + return &httpFederationSenderQueryAPI{federationSenderURL, httpClient} +} + +type httpFederationSenderQueryAPI struct { + federationSenderURL string + httpClient *http.Client +} + +// QueryJoinedHostsInRoom implements FederationSenderQueryAPI +func (h *httpFederationSenderQueryAPI) QueryJoinedHostsInRoom( + ctx context.Context, + request *QueryJoinedHostsInRoomRequest, + response *QueryJoinedHostsInRoomResponse, +) error { + span, ctx := opentracing.StartSpanFromContext(ctx, "QueryJoinedHostsInRoom") + defer span.Finish() + + apiURL := h.federationSenderURL + FederationSenderQueryJoinedHostsInRoomPath + return commonHTTP.PostJSON(ctx, span, h.httpClient, apiURL, request, response) +} + +// QueryJoinedHostServerNamesInRoom implements FederationSenderQueryAPI +func (h *httpFederationSenderQueryAPI) QueryJoinedHostServerNamesInRoom( + ctx context.Context, + request *QueryJoinedHostServerNamesInRoomRequest, + response *QueryJoinedHostServerNamesInRoomResponse, +) error { + span, ctx := opentracing.StartSpanFromContext(ctx, "QueryJoinedHostServerNamesInRoom") + defer span.Finish() + + apiURL := h.federationSenderURL + FederationSenderQueryJoinedHostServerNamesInRoomPath + return commonHTTP.PostJSON(ctx, span, h.httpClient, apiURL, request, response) +} diff --git a/federationsender/query/query.go b/federationsender/query/query.go new file mode 100644 index 00000000..ec924286 --- /dev/null +++ b/federationsender/query/query.go @@ -0,0 +1,55 @@ +package query + +import ( + "context" + + "github.com/matrix-org/dendrite/federationsender/api" + "github.com/matrix-org/dendrite/federationsender/types" + "github.com/matrix-org/gomatrixserverlib" +) + +// FederationSenderQueryDatabase has the APIs needed to implement the query API. +type FederationSenderQueryDatabase interface { + GetJoinedHosts( + ctx context.Context, roomID string, + ) ([]types.JoinedHost, error) +} + +// FederationSenderQueryAPI is an implementation of api.FederationSenderQueryAPI +type FederationSenderQueryAPI struct { + DB FederationSenderQueryDatabase +} + +// QueryJoinedHostsInRoom implements api.FederationSenderQueryAPI +func (f *FederationSenderQueryAPI) QueryJoinedHostsInRoom( + ctx context.Context, + request *api.QueryJoinedHostsInRoomRequest, + response *api.QueryJoinedHostsInRoomResponse, +) (err error) { + response.JoinedHosts, err = f.DB.GetJoinedHosts(ctx, request.RoomID) + return +} + +// QueryJoinedHostServerNamesInRoom implements api.FederationSenderQueryAPI +func (f *FederationSenderQueryAPI) QueryJoinedHostServerNamesInRoom( + ctx context.Context, + request *api.QueryJoinedHostServerNamesInRoomRequest, + response *api.QueryJoinedHostServerNamesInRoomResponse, +) (err error) { + joinedHosts, err := f.DB.GetJoinedHosts(ctx, request.RoomID) + if err != nil { + return + } + + serverNamesSet := make(map[gomatrixserverlib.ServerName]bool, len(joinedHosts)) + for _, host := range joinedHosts { + serverNamesSet[host.ServerName] = true + } + + response.ServerNames = make([]gomatrixserverlib.ServerName, 0, len(serverNamesSet)) + for name := range serverNamesSet { + response.ServerNames = append(response.ServerNames, name) + } + + return +}