mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2025-06-09 00:22:39 -04:00
Pass the Requester down to the HttpTransactionCache. (#15200)
This commit is contained in:
parent
820f02b70b
commit
47bc84dd53
6 changed files with 217 additions and 131 deletions
|
@ -15,16 +15,16 @@
|
|||
"""This module contains logic for storing HTTP PUT transactions. This is used
|
||||
to ensure idempotency when performing PUTs using the REST API."""
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Awaitable, Callable, Dict, Tuple
|
||||
from typing import TYPE_CHECKING, Awaitable, Callable, Dict, Hashable, Tuple
|
||||
|
||||
from typing_extensions import ParamSpec
|
||||
|
||||
from twisted.internet.defer import Deferred
|
||||
from twisted.python.failure import Failure
|
||||
from twisted.web.server import Request
|
||||
from twisted.web.iweb import IRequest
|
||||
|
||||
from synapse.logging.context import make_deferred_yieldable, run_in_background
|
||||
from synapse.types import JsonDict
|
||||
from synapse.types import JsonDict, Requester
|
||||
from synapse.util.async_helpers import ObservableDeferred
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -41,53 +41,47 @@ P = ParamSpec("P")
|
|||
class HttpTransactionCache:
|
||||
def __init__(self, hs: "HomeServer"):
|
||||
self.hs = hs
|
||||
self.auth = self.hs.get_auth()
|
||||
self.clock = self.hs.get_clock()
|
||||
# $txn_key: (ObservableDeferred<(res_code, res_json_body)>, timestamp)
|
||||
self.transactions: Dict[
|
||||
str, Tuple[ObservableDeferred[Tuple[int, JsonDict]], int]
|
||||
Hashable, Tuple[ObservableDeferred[Tuple[int, JsonDict]], int]
|
||||
] = {}
|
||||
# Try to clean entries every 30 mins. This means entries will exist
|
||||
# for at *LEAST* 30 mins, and at *MOST* 60 mins.
|
||||
self.cleaner = self.clock.looping_call(self._cleanup, CLEANUP_PERIOD_MS)
|
||||
|
||||
def _get_transaction_key(self, request: Request) -> str:
|
||||
def _get_transaction_key(self, request: IRequest, requester: Requester) -> Hashable:
|
||||
"""A helper function which returns a transaction key that can be used
|
||||
with TransactionCache for idempotent requests.
|
||||
|
||||
Idempotency is based on the returned key being the same for separate
|
||||
requests to the same endpoint. The key is formed from the HTTP request
|
||||
path and the access_token for the requesting user.
|
||||
path and attributes from the requester: the access_token_id for regular users,
|
||||
the user ID for guest users, and the appservice ID for appservice users.
|
||||
|
||||
Args:
|
||||
request: The incoming request. Must contain an access_token.
|
||||
request: The incoming request.
|
||||
requester: The requester doing the request.
|
||||
Returns:
|
||||
A transaction key
|
||||
"""
|
||||
assert request.path is not None
|
||||
token = self.auth.get_access_token_from_request(request)
|
||||
return request.path.decode("utf8") + "/" + token
|
||||
path: str = request.path.decode("utf8")
|
||||
if requester.is_guest:
|
||||
assert requester.user is not None, "Guest requester must have a user ID set"
|
||||
return (path, "guest", requester.user)
|
||||
elif requester.app_service is not None:
|
||||
return (path, "appservice", requester.app_service.id)
|
||||
else:
|
||||
assert (
|
||||
requester.access_token_id is not None
|
||||
), "Requester must have an access_token_id"
|
||||
return (path, "user", requester.access_token_id)
|
||||
|
||||
def fetch_or_execute_request(
|
||||
self,
|
||||
request: Request,
|
||||
fn: Callable[P, Awaitable[Tuple[int, JsonDict]]],
|
||||
*args: P.args,
|
||||
**kwargs: P.kwargs,
|
||||
) -> Awaitable[Tuple[int, JsonDict]]:
|
||||
"""A helper function for fetch_or_execute which extracts
|
||||
a transaction key from the given request.
|
||||
|
||||
See:
|
||||
fetch_or_execute
|
||||
"""
|
||||
return self.fetch_or_execute(
|
||||
self._get_transaction_key(request), fn, *args, **kwargs
|
||||
)
|
||||
|
||||
def fetch_or_execute(
|
||||
self,
|
||||
txn_key: str,
|
||||
request: IRequest,
|
||||
requester: Requester,
|
||||
fn: Callable[P, Awaitable[Tuple[int, JsonDict]]],
|
||||
*args: P.args,
|
||||
**kwargs: P.kwargs,
|
||||
|
@ -96,14 +90,15 @@ class HttpTransactionCache:
|
|||
to produce a response for this transaction.
|
||||
|
||||
Args:
|
||||
txn_key: A key to ensure idempotency should fetch_or_execute be
|
||||
called again at a later point in time.
|
||||
request:
|
||||
requester:
|
||||
fn: A function which returns a tuple of (response_code, response_dict).
|
||||
*args: Arguments to pass to fn.
|
||||
**kwargs: Keyword arguments to pass to fn.
|
||||
Returns:
|
||||
Deferred which resolves to a tuple of (response_code, response_dict).
|
||||
"""
|
||||
txn_key = self._get_transaction_key(request, requester)
|
||||
if txn_key in self.transactions:
|
||||
observable = self.transactions[txn_key][0]
|
||||
else:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue