1818# [This file includes modifications made by New Vector Limited]
1919#
2020#
21+ from enum import Enum
2122import itertools
2223import logging
2324from typing import (
112113SyncRequestKey = Tuple [Any , ...]
113114
114115
116+ class SyncType (Enum ):
117+ """Enum for specifying the type of sync request."""
118+
119+ # These string values are semantically significant and are used in the the metrics
120+ INITIAL_SYNC = "initial_sync"
121+ FULL_STATE_SYNC = "full_state_sync"
122+ INCREMENTAL_SYNC = "incremental_sync"
123+ E2EE_SYNC = "e2ee_sync"
124+
125+
115126@attr .s (slots = True , frozen = True , auto_attribs = True )
116127class SyncConfig :
117128 user : UserID
118129 filter_collection : FilterCollection
119130 is_guest : bool
120- request_key : SyncRequestKey
121131 device_id : Optional [str ]
122132
123133
@@ -263,6 +273,15 @@ def __bool__(self) -> bool:
263273 )
264274
265275
276+ @attr .s (slots = True , frozen = True , auto_attribs = True )
277+ class E2eeSyncResult :
278+ next_batch : StreamToken
279+ to_device : List [JsonDict ]
280+ # device_lists: DeviceListUpdates
281+ # device_one_time_keys_count: JsonMapping
282+ # device_unused_fallback_key_types: List[str]
283+
284+
266285class SyncHandler :
267286 def __init__ (self , hs : "HomeServer" ):
268287 self .hs_config = hs .config
@@ -309,13 +328,18 @@ async def wait_for_sync_for_user(
309328 self ,
310329 requester : Requester ,
311330 sync_config : SyncConfig ,
331+ sync_type : SyncType ,
332+ request_key : SyncRequestKey ,
312333 since_token : Optional [StreamToken ] = None ,
313334 timeout : int = 0 ,
314335 full_state : bool = False ,
315336 ) -> SyncResult :
316337 """Get the sync for a client if we have new data for it now. Otherwise
317338 wait for new data to arrive on the server. If the timeout expires, then
318339 return an empty sync result.
340+
341+ Args:
342+ request_key: The key to use for caching the response.
319343 """
320344 # If the user is not part of the mau group, then check that limits have
321345 # not been exceeded (if not part of the group by this point, almost certain
@@ -324,9 +348,10 @@ async def wait_for_sync_for_user(
324348 await self .auth_blocking .check_auth_blocking (requester = requester )
325349
326350 res = await self .response_cache .wrap (
327- sync_config . request_key ,
351+ request_key ,
328352 self ._wait_for_sync_for_user ,
329353 sync_config ,
354+ sync_type ,
330355 since_token ,
331356 timeout ,
332357 full_state ,
@@ -338,6 +363,7 @@ async def wait_for_sync_for_user(
338363 async def _wait_for_sync_for_user (
339364 self ,
340365 sync_config : SyncConfig ,
366+ sync_type : SyncType ,
341367 since_token : Optional [StreamToken ],
342368 timeout : int ,
343369 full_state : bool ,
@@ -356,13 +382,6 @@ async def _wait_for_sync_for_user(
356382 Computing the body of the response begins in the next method,
357383 `current_sync_for_user`.
358384 """
359- if since_token is None :
360- sync_type = "initial_sync"
361- elif full_state :
362- sync_type = "full_state_sync"
363- else :
364- sync_type = "incremental_sync"
365-
366385 context = current_context ()
367386 if context :
368387 context .tag = sync_type
@@ -384,14 +403,16 @@ async def _wait_for_sync_for_user(
384403 # we are going to return immediately, so don't bother calling
385404 # notifier.wait_for_events.
386405 result : SyncResult = await self .current_sync_for_user (
387- sync_config , since_token , full_state = full_state
406+ sync_config , sync_type , since_token , full_state = full_state
388407 )
389408 else :
390409 # Otherwise, we wait for something to happen and report it to the user.
391410 async def current_sync_callback (
392411 before_token : StreamToken , after_token : StreamToken
393412 ) -> SyncResult :
394- return await self .current_sync_for_user (sync_config , since_token )
413+ return await self .current_sync_for_user (
414+ sync_config , sync_type , since_token
415+ )
395416
396417 result = await self .notifier .wait_for_events (
397418 sync_config .user .to_string (),
@@ -423,6 +444,7 @@ async def current_sync_callback(
423444 async def current_sync_for_user (
424445 self ,
425446 sync_config : SyncConfig ,
447+ sync_type : SyncType ,
426448 since_token : Optional [StreamToken ] = None ,
427449 full_state : bool = False ,
428450 ) -> SyncResult :
@@ -434,9 +456,25 @@ async def current_sync_for_user(
434456 """
435457 with start_active_span ("sync.current_sync_for_user" ):
436458 log_kv ({"since_token" : since_token })
437- sync_result = await self .generate_sync_result (
438- sync_config , since_token , full_state
439- )
459+
460+ # Go through the `/sync` v2 path
461+ if sync_type in {
462+ SyncType .INITIAL_SYNC ,
463+ SyncType .FULL_STATE_SYNC ,
464+ SyncType .INCREMENTAL_SYNC ,
465+ }:
466+ sync_result = await self .generate_sync_result (
467+ sync_config , since_token , full_state
468+ )
469+ # Go through the MSC3575 Sliding Sync `/sync/e2ee` path
470+ elif sync_type == SyncType .E2EE_SYNC :
471+ sync_result = await self .generate_e2ee_sync_result (
472+ sync_config , since_token
473+ )
474+ else :
475+ raise Exception (
476+ f"Unknown sync_type (this is a Synapse problem): { sync_type } "
477+ )
440478
441479 set_tag (SynapseTags .SYNC_RESULT , bool (sync_result ))
442480 return sync_result
@@ -1751,6 +1789,50 @@ async def generate_sync_result(
17511789 next_batch = sync_result_builder .now_token ,
17521790 )
17531791
1792+ async def generate_e2ee_sync_result (
1793+ self ,
1794+ sync_config : SyncConfig ,
1795+ since_token : Optional [StreamToken ] = None ,
1796+ ) -> SyncResult :
1797+ """Generates the response body of a MSC3575 Sliding Sync `/sync/e2ee` result."""
1798+
1799+ user_id = sync_config .user .to_string ()
1800+ # TODO: Should we exclude app services here? There could be an argument to allow
1801+ # them since the appservice doesn't have to make a massive initial sync.
1802+ # (related to https://github.com/matrix-org/matrix-doc/issues/1144)
1803+
1804+ # NB: The now_token gets changed by some of the generate_sync_* methods,
1805+ # this is due to some of the underlying streams not supporting the ability
1806+ # to query up to a given point.
1807+ # Always use the `now_token` in `SyncResultBuilder`
1808+ now_token = self .event_sources .get_current_token ()
1809+ log_kv ({"now_token" : now_token })
1810+
1811+ joined_room_ids = await self .store .get_rooms_for_user (user_id )
1812+
1813+ sync_result_builder = SyncResultBuilder (
1814+ sync_config ,
1815+ full_state = False ,
1816+ since_token = since_token ,
1817+ now_token = now_token ,
1818+ joined_room_ids = joined_room_ids ,
1819+ # Dummy values to fill out `SyncResultBuilder`
1820+ excluded_room_ids = frozenset ({}),
1821+ forced_newly_joined_room_ids = frozenset ({}),
1822+ membership_change_events = frozenset ({}),
1823+ )
1824+
1825+ await self ._generate_sync_entry_for_to_device (sync_result_builder )
1826+
1827+ return E2eeSyncResult (
1828+ to_device = sync_result_builder .to_device ,
1829+ # to_device: List[JsonDict]
1830+ # device_lists: DeviceListUpdates
1831+ # device_one_time_keys_count: JsonMapping
1832+ # device_unused_fallback_key_types: List[str]
1833+ next_batch = sync_result_builder .now_token ,
1834+ )
1835+
17541836 @measure_func ("_generate_sync_entry_for_device_list" )
17551837 async def _generate_sync_entry_for_device_list (
17561838 self ,
0 commit comments