test_fedclient.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2018 New Vector Ltd
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. from mock import Mock
  16. from twisted.internet.defer import TimeoutError
  17. from twisted.internet.error import ConnectingCancelledError, DNSLookupError
  18. from twisted.web.client import ResponseNeverReceived
  19. from twisted.web.http import HTTPChannel
  20. from synapse.api.errors import RequestSendFailed
  21. from synapse.http.matrixfederationclient import (
  22. MatrixFederationHttpClient,
  23. MatrixFederationRequest,
  24. )
  25. from tests.server import FakeTransport
  26. from tests.unittest import HomeserverTestCase
  27. class FederationClientTests(HomeserverTestCase):
  28. def make_homeserver(self, reactor, clock):
  29. hs = self.setup_test_homeserver(reactor=reactor, clock=clock)
  30. hs.tls_client_options_factory = None
  31. return hs
  32. def prepare(self, reactor, clock, homeserver):
  33. self.cl = MatrixFederationHttpClient(self.hs)
  34. self.reactor.lookups["testserv"] = "1.2.3.4"
  35. def test_dns_error(self):
  36. """
  37. If the DNS raising returns an error, it will bubble up.
  38. """
  39. d = self.cl.get_json("testserv2:8008", "foo/bar", timeout=10000)
  40. self.pump()
  41. f = self.failureResultOf(d)
  42. self.assertIsInstance(f.value, RequestSendFailed)
  43. self.assertIsInstance(f.value.inner_exception, DNSLookupError)
  44. def test_client_never_connect(self):
  45. """
  46. If the HTTP request is not connected and is timed out, it'll give a
  47. ConnectingCancelledError or TimeoutError.
  48. """
  49. d = self.cl.get_json("testserv:8008", "foo/bar", timeout=10000)
  50. self.pump()
  51. # Nothing happened yet
  52. self.assertFalse(d.called)
  53. # Make sure treq is trying to connect
  54. clients = self.reactor.tcpClients
  55. self.assertEqual(len(clients), 1)
  56. self.assertEqual(clients[0][0], '1.2.3.4')
  57. self.assertEqual(clients[0][1], 8008)
  58. # Deferred is still without a result
  59. self.assertFalse(d.called)
  60. # Push by enough to time it out
  61. self.reactor.advance(10.5)
  62. f = self.failureResultOf(d)
  63. self.assertIsInstance(f.value, RequestSendFailed)
  64. self.assertIsInstance(
  65. f.value.inner_exception,
  66. (ConnectingCancelledError, TimeoutError),
  67. )
  68. def test_client_connect_no_response(self):
  69. """
  70. If the HTTP request is connected, but gets no response before being
  71. timed out, it'll give a ResponseNeverReceived.
  72. """
  73. d = self.cl.get_json("testserv:8008", "foo/bar", timeout=10000)
  74. self.pump()
  75. # Nothing happened yet
  76. self.assertFalse(d.called)
  77. # Make sure treq is trying to connect
  78. clients = self.reactor.tcpClients
  79. self.assertEqual(len(clients), 1)
  80. self.assertEqual(clients[0][0], '1.2.3.4')
  81. self.assertEqual(clients[0][1], 8008)
  82. conn = Mock()
  83. client = clients[0][2].buildProtocol(None)
  84. client.makeConnection(conn)
  85. # Deferred is still without a result
  86. self.assertFalse(d.called)
  87. # Push by enough to time it out
  88. self.reactor.advance(10.5)
  89. f = self.failureResultOf(d)
  90. self.assertIsInstance(f.value, RequestSendFailed)
  91. self.assertIsInstance(f.value.inner_exception, ResponseNeverReceived)
  92. def test_client_gets_headers(self):
  93. """
  94. Once the client gets the headers, _request returns successfully.
  95. """
  96. request = MatrixFederationRequest(
  97. method="GET",
  98. destination="testserv:8008",
  99. path="foo/bar",
  100. )
  101. d = self.cl._send_request(request, timeout=10000)
  102. self.pump()
  103. conn = Mock()
  104. clients = self.reactor.tcpClients
  105. client = clients[0][2].buildProtocol(None)
  106. client.makeConnection(conn)
  107. # Deferred does not have a result
  108. self.assertFalse(d.called)
  109. # Send it the HTTP response
  110. client.dataReceived(b"HTTP/1.1 200 OK\r\nServer: Fake\r\n\r\n")
  111. # We should get a successful response
  112. r = self.successResultOf(d)
  113. self.assertEqual(r.code, 200)
  114. def test_client_headers_no_body(self):
  115. """
  116. If the HTTP request is connected, but gets no response before being
  117. timed out, it'll give a ResponseNeverReceived.
  118. """
  119. d = self.cl.post_json("testserv:8008", "foo/bar", timeout=10000)
  120. self.pump()
  121. conn = Mock()
  122. clients = self.reactor.tcpClients
  123. client = clients[0][2].buildProtocol(None)
  124. client.makeConnection(conn)
  125. # Deferred does not have a result
  126. self.assertFalse(d.called)
  127. # Send it the HTTP response
  128. client.dataReceived(
  129. (b"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n"
  130. b"Server: Fake\r\n\r\n")
  131. )
  132. # Push by enough to time it out
  133. self.reactor.advance(10.5)
  134. f = self.failureResultOf(d)
  135. self.assertIsInstance(f.value, TimeoutError)
  136. def test_client_sends_body(self):
  137. self.cl.post_json(
  138. "testserv:8008", "foo/bar", timeout=10000,
  139. data={"a": "b"}
  140. )
  141. self.pump()
  142. clients = self.reactor.tcpClients
  143. self.assertEqual(len(clients), 1)
  144. client = clients[0][2].buildProtocol(None)
  145. server = HTTPChannel()
  146. client.makeConnection(FakeTransport(server, self.reactor))
  147. server.makeConnection(FakeTransport(client, self.reactor))
  148. self.pump(0.1)
  149. self.assertEqual(len(server.requests), 1)
  150. request = server.requests[0]
  151. content = request.content.read()
  152. self.assertEqual(content, b'{"a":"b"}')