diff --git a/syncapi/routing/messages.go b/syncapi/routing/messages.go index e55c661d..24745cd5 100644 --- a/syncapi/routing/messages.go +++ b/syncapi/routing/messages.go @@ -50,7 +50,7 @@ type messagesReq struct { type messagesResp struct { Start string `json:"start"` StartStream string `json:"start_stream,omitempty"` // NOTSPEC: used by Cerulean, so clients can hit /messages then immediately /sync with a latest sync token - End string `json:"end"` + End string `json:"end,omitempty"` Chunk []gomatrixserverlib.ClientEvent `json:"chunk"` State []gomatrixserverlib.ClientEvent `json:"state"` } @@ -200,30 +200,6 @@ func OnIncomingMessagesRequest( return jsonerror.InternalServerError() } - // at least fetch the membership events for the users returned in chunk if LazyLoadMembers is set - state := []gomatrixserverlib.ClientEvent{} - if filter.LazyLoadMembers { - membershipToUser := make(map[string]*gomatrixserverlib.HeaderedEvent) - for _, evt := range clientEvents { - // Don't add membership events the client should already know about - if _, cached := lazyLoadCache.IsLazyLoadedUserCached(device, roomID, evt.Sender); cached { - continue - } - membership, err := db.GetStateEvent(req.Context(), roomID, gomatrixserverlib.MRoomMember, evt.Sender) - if err != nil { - util.GetLogger(req.Context()).WithError(err).Error("failed to get membership event for user") - continue - } - if membership != nil { - membershipToUser[evt.Sender] = membership - lazyLoadCache.StoreLazyLoadedUser(device, roomID, evt.Sender, membership.EventID()) - } - } - for _, evt := range membershipToUser { - state = append(state, gomatrixserverlib.HeaderedToClientEvent(evt, gomatrixserverlib.FormatSync)) - } - } - util.GetLogger(req.Context()).WithFields(logrus.Fields{ "from": from.String(), "to": to.String(), @@ -237,7 +213,13 @@ func OnIncomingMessagesRequest( Chunk: clientEvents, Start: start.String(), End: end.String(), - State: state, + } + res.applyLazyLoadMembers(req.Context(), db, roomID, device, filter.LazyLoadMembers, lazyLoadCache) + + // If we didn't return any events, set the end to an empty string, so it will be omitted + // in the response JSON. + if len(res.Chunk) == 0 { + res.End = "" } if fromStream != nil { res.StartStream = fromStream.String() @@ -250,6 +232,40 @@ func OnIncomingMessagesRequest( } } +// applyLazyLoadMembers loads membership events for users returned in Chunk, if the filter has +// LazyLoadMembers enabled. +func (m *messagesResp) applyLazyLoadMembers( + ctx context.Context, + db storage.Database, + roomID string, + device *userapi.Device, + lazyLoad bool, + lazyLoadCache caching.LazyLoadCache, +) { + if !lazyLoad { + return + } + membershipToUser := make(map[string]*gomatrixserverlib.HeaderedEvent) + for _, evt := range m.Chunk { + // Don't add membership events the client should already know about + if _, cached := lazyLoadCache.IsLazyLoadedUserCached(device, roomID, evt.Sender); cached { + continue + } + membership, err := db.GetStateEvent(ctx, roomID, gomatrixserverlib.MRoomMember, evt.Sender) + if err != nil { + util.GetLogger(ctx).WithError(err).Error("failed to get membership event for user") + continue + } + if membership != nil { + membershipToUser[evt.Sender] = membership + lazyLoadCache.StoreLazyLoadedUser(device, roomID, evt.Sender, membership.EventID()) + } + } + for _, evt := range membershipToUser { + m.State = append(m.State, gomatrixserverlib.HeaderedToClientEvent(evt, gomatrixserverlib.FormatSync)) + } +} + func checkIsRoomForgotten(ctx context.Context, roomID, userID string, rsAPI api.SyncRoomserverAPI) (forgotten bool, exists bool, err error) { req := api.QueryMembershipForUserRequest{ RoomID: roomID,