test__base.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  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 synapse.api.errors import Codes
  17. from synapse.federation.transport.server import BaseFederationServlet
  18. from synapse.federation.transport.server._base import Authenticator, _parse_auth_header
  19. from synapse.http.server import JsonResource
  20. from synapse.server import HomeServer
  21. from synapse.types import JsonDict
  22. from synapse.util.cancellation import cancellable
  23. from synapse.util.ratelimitutils import FederationRateLimiter
  24. from tests import unittest
  25. from tests.http.server._base import test_disconnect
  26. class CancellableFederationServlet(BaseFederationServlet):
  27. PATH = "/sleep"
  28. def __init__(
  29. self,
  30. hs: HomeServer,
  31. authenticator: Authenticator,
  32. ratelimiter: FederationRateLimiter,
  33. server_name: str,
  34. ):
  35. super().__init__(hs, authenticator, ratelimiter, server_name)
  36. self.clock = hs.get_clock()
  37. @cancellable
  38. async def on_GET(
  39. self, origin: str, content: None, query: Dict[bytes, List[bytes]]
  40. ) -> Tuple[int, JsonDict]:
  41. await self.clock.sleep(1.0)
  42. return HTTPStatus.OK, {"result": True}
  43. async def on_POST(
  44. self, origin: str, content: JsonDict, query: Dict[bytes, List[bytes]]
  45. ) -> Tuple[int, JsonDict]:
  46. await self.clock.sleep(1.0)
  47. return HTTPStatus.OK, {"result": True}
  48. class BaseFederationServletCancellationTests(unittest.FederatingHomeserverTestCase):
  49. """Tests for `BaseFederationServlet` cancellation."""
  50. skip = "`BaseFederationServlet` does not support cancellation yet."
  51. path = f"{CancellableFederationServlet.PREFIX}{CancellableFederationServlet.PATH}"
  52. def create_test_resource(self):
  53. """Overrides `HomeserverTestCase.create_test_resource`."""
  54. resource = JsonResource(self.hs)
  55. CancellableFederationServlet(
  56. hs=self.hs,
  57. authenticator=Authenticator(self.hs),
  58. ratelimiter=self.hs.get_federation_ratelimiter(),
  59. server_name=self.hs.hostname,
  60. ).register(resource)
  61. return resource
  62. def test_cancellable_disconnect(self) -> None:
  63. """Test that handlers with the `@cancellable` flag can be cancelled."""
  64. channel = self.make_signed_federation_request(
  65. "GET", self.path, await_result=False
  66. )
  67. # Advance past all the rate limiting logic. If we disconnect too early, the
  68. # request won't be processed.
  69. self.pump()
  70. test_disconnect(
  71. self.reactor,
  72. channel,
  73. expect_cancellation=True,
  74. expected_body={"error": "Request cancelled", "errcode": Codes.UNKNOWN},
  75. )
  76. def test_uncancellable_disconnect(self) -> None:
  77. """Test that handlers without the `@cancellable` flag cannot be cancelled."""
  78. channel = self.make_signed_federation_request(
  79. "POST",
  80. self.path,
  81. content={},
  82. await_result=False,
  83. )
  84. # Advance past all the rate limiting logic. If we disconnect too early, the
  85. # request won't be processed.
  86. self.pump()
  87. test_disconnect(
  88. self.reactor,
  89. channel,
  90. expect_cancellation=False,
  91. expected_body={"result": True},
  92. )
  93. class BaseFederationAuthorizationTests(unittest.TestCase):
  94. def test_authorization_header(self) -> None:
  95. """Tests that the Authorization header is parsed correctly."""
  96. # test a "normal" Authorization header
  97. self.assertEqual(
  98. _parse_auth_header(
  99. b'X-Matrix origin=foo,key="ed25519:1",sig="sig",destination="bar"'
  100. ),
  101. ("foo", "ed25519:1", "sig", "bar"),
  102. )
  103. # test an Authorization with extra spaces, upper-case names, and escaped
  104. # characters
  105. self.assertEqual(
  106. _parse_auth_header(
  107. b'X-Matrix ORIGIN=foo,KEY="ed25\\519:1",SIG="sig",destination="bar"'
  108. ),
  109. ("foo", "ed25519:1", "sig", "bar"),
  110. )
  111. self.assertEqual(
  112. _parse_auth_header(
  113. b'X-Matrix origin=foo,key="ed25519:1",sig="sig",destination="bar",extra_field=ignored'
  114. ),
  115. ("foo", "ed25519:1", "sig", "bar"),
  116. )