test__base.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. # Copyright 2022 The Matrix.org Foundation C.I.C.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from http import HTTPStatus
  15. from typing import Dict, List, Tuple
  16. from twisted.web.resource import Resource
  17. from synapse.api.errors import Codes
  18. from synapse.federation.transport.server import BaseFederationServlet
  19. from synapse.federation.transport.server._base import Authenticator, _parse_auth_header
  20. from synapse.http.server import JsonResource
  21. from synapse.server import HomeServer
  22. from synapse.types import JsonDict
  23. from synapse.util.cancellation import cancellable
  24. from synapse.util.ratelimitutils import FederationRateLimiter
  25. from tests import unittest
  26. from tests.http.server._base import test_disconnect
  27. class CancellableFederationServlet(BaseFederationServlet):
  28. PATH = "/sleep"
  29. def __init__(
  30. self,
  31. hs: HomeServer,
  32. authenticator: Authenticator,
  33. ratelimiter: FederationRateLimiter,
  34. server_name: str,
  35. ):
  36. super().__init__(hs, authenticator, ratelimiter, server_name)
  37. self.clock = hs.get_clock()
  38. @cancellable
  39. async def on_GET(
  40. self, origin: str, content: None, query: Dict[bytes, List[bytes]]
  41. ) -> Tuple[int, JsonDict]:
  42. await self.clock.sleep(1.0)
  43. return HTTPStatus.OK, {"result": True}
  44. async def on_POST(
  45. self, origin: str, content: JsonDict, query: Dict[bytes, List[bytes]]
  46. ) -> Tuple[int, JsonDict]:
  47. await self.clock.sleep(1.0)
  48. return HTTPStatus.OK, {"result": True}
  49. class BaseFederationServletCancellationTests(unittest.FederatingHomeserverTestCase):
  50. """Tests for `BaseFederationServlet` cancellation."""
  51. skip = "`BaseFederationServlet` does not support cancellation yet."
  52. path = f"{CancellableFederationServlet.PREFIX}{CancellableFederationServlet.PATH}"
  53. def create_test_resource(self) -> Resource:
  54. """Overrides `HomeserverTestCase.create_test_resource`."""
  55. resource = JsonResource(self.hs)
  56. CancellableFederationServlet(
  57. hs=self.hs,
  58. authenticator=Authenticator(self.hs),
  59. ratelimiter=self.hs.get_federation_ratelimiter(),
  60. server_name=self.hs.hostname,
  61. ).register(resource)
  62. return resource
  63. def test_cancellable_disconnect(self) -> None:
  64. """Test that handlers with the `@cancellable` flag can be cancelled."""
  65. channel = self.make_signed_federation_request(
  66. "GET", self.path, await_result=False
  67. )
  68. # Advance past all the rate limiting logic. If we disconnect too early, the
  69. # request won't be processed.
  70. self.pump()
  71. test_disconnect(
  72. self.reactor,
  73. channel,
  74. expect_cancellation=True,
  75. expected_body={"error": "Request cancelled", "errcode": Codes.UNKNOWN},
  76. )
  77. def test_uncancellable_disconnect(self) -> None:
  78. """Test that handlers without the `@cancellable` flag cannot be cancelled."""
  79. channel = self.make_signed_federation_request(
  80. "POST",
  81. self.path,
  82. content={},
  83. await_result=False,
  84. )
  85. # Advance past all the rate limiting logic. If we disconnect too early, the
  86. # request won't be processed.
  87. self.pump()
  88. test_disconnect(
  89. self.reactor,
  90. channel,
  91. expect_cancellation=False,
  92. expected_body={"result": True},
  93. )
  94. class BaseFederationAuthorizationTests(unittest.TestCase):
  95. def test_authorization_header(self) -> None:
  96. """Tests that the Authorization header is parsed correctly."""
  97. # test a "normal" Authorization header
  98. self.assertEqual(
  99. _parse_auth_header(
  100. b'X-Matrix origin=foo,key="ed25519:1",sig="sig",destination="bar"'
  101. ),
  102. ("foo", "ed25519:1", "sig", "bar"),
  103. )
  104. # test an Authorization with extra spaces, upper-case names, and escaped
  105. # characters
  106. self.assertEqual(
  107. _parse_auth_header(
  108. b'X-Matrix ORIGIN=foo,KEY="ed25\\519:1",SIG="sig",destination="bar"'
  109. ),
  110. ("foo", "ed25519:1", "sig", "bar"),
  111. )
  112. self.assertEqual(
  113. _parse_auth_header(
  114. b'X-Matrix origin=foo,key="ed25519:1",sig="sig",destination="bar",extra_field=ignored'
  115. ),
  116. ("foo", "ed25519:1", "sig", "bar"),
  117. )