def setUp(self): """Initialize this testcase.""" yield super(TunnelIntegrationTestCase, self).setUp() self.ws = MockWebServer() self.addCleanup(self.ws.stop) self.dest_url = self.ws.get_iri().encode("utf-8") + SIMPLERESOURCE self.cookie = FAKE_COOKIE self.tunnel_server = tunnel_server.TunnelServer(self.cookie) self.addCleanup(self.tunnel_server.shutdown)
def setUp(self): """Initialize this testcase.""" yield super(RemoteSocketTestCase, self).setUp() self.ws = MockWebServer() self.addCleanup(self.ws.stop) self.dest_url = self.ws.get_iri().encode("utf-8") + SIMPLERESOURCE self.addCleanup(tunnel_server.QNetworkProxy.setApplicationProxy, tunnel_server.QNetworkProxy.applicationProxy()) settings = {"http": self.get_proxy_settings()} proxy = tunnel_server.build_proxy(settings) tunnel_server.QNetworkProxy.setApplicationProxy(proxy)
class TunnelClientTestCase(SquidTestCase): """Test the client for the tunnel.""" timeout = 3 @defer.inlineCallbacks def setUp(self): """Initialize this testcase.""" yield super(TunnelClientTestCase, self).setUp() self.ws = MockWebServer() self.addCleanup(self.ws.stop) self.dest_url = self.ws.get_iri().encode("utf-8") + SIMPLERESOURCE self.dest_ssl_url = self.ws.get_ssl_iri().encode("utf-8") + SIMPLERESOURCE self.cookie = FAKE_COOKIE self.tunnel_server = TunnelServer(self.cookie) self.addCleanup(self.tunnel_server.shutdown) @defer.inlineCallbacks def test_connects_right(self): """Uses the CONNECT method on the tunnel.""" tunnel_client = TunnelClient("0.0.0.0", self.tunnel_server.port, self.cookie) factory = client.HTTPClientFactory(self.dest_url) scheme, host, port, path = client._parse(self.dest_url) tunnel_client.connectTCP(host, port, factory) result = yield factory.deferred self.assertEqual(result, SAMPLE_CONTENT) @defer.inlineCallbacks def test_starts_tls_connection(self): """TLS is started after connecting; control passed to the client.""" tunnel_client = TunnelClient("0.0.0.0", self.tunnel_server.port, self.cookie) factory = client.HTTPClientFactory(self.dest_ssl_url) scheme, host, port, path = client._parse(self.dest_ssl_url) context_factory = ssl.ClientContextFactory() tunnel_client.connectSSL(host, port, factory, context_factory) result = yield factory.deferred self.assertEqual(result, SAMPLE_CONTENT)
def setUp(self): """Initialize this test instance.""" yield super(ServerTunnelProtocolTestCase, self).setUp() self.ws = MockWebServer() self.addCleanup(self.ws.stop) self.dest_url = self.ws.get_iri().encode("utf-8") + SIMPLERESOURCE self.transport = FakeTransport() self.transport.cookie = FAKE_COOKIE self.fake_client = FakeClient() self.proto = tunnel_server.ServerTunnelProtocol( lambda _: self.fake_client) self.fake_client.protocol = self.proto self.proto.transport = self.transport self.cookie_line = "%s: %s" % (tunnel_server.TUNNEL_COOKIE_HEADER, FAKE_COOKIE)
class TunnelIntegrationTestCase(SquidTestCase): """Basic tunnel integration tests.""" timeout = 3 @defer.inlineCallbacks def setUp(self): """Initialize this testcase.""" yield super(TunnelIntegrationTestCase, self).setUp() self.ws = MockWebServer() self.addCleanup(self.ws.stop) self.dest_url = self.ws.get_iri().encode("utf-8") + SIMPLERESOURCE self.cookie = FAKE_COOKIE self.tunnel_server = tunnel_server.TunnelServer(self.cookie) self.addCleanup(self.tunnel_server.shutdown) def test_init(self): """The tunnel is started.""" self.assertNotEqual(self.tunnel_server.port, 0) @defer.inlineCallbacks def test_accepts_connections(self): """The tunnel accepts incoming connections.""" ncf = DisconnectingClientFactory() reactor.connectTCP("0.0.0.0", self.tunnel_server.port, ncf) yield ncf.connected @defer.inlineCallbacks def test_complete_connection(self): """Test from the tunnel server down.""" url = urlparse(self.dest_url) fake_session = FAKE_SESSION_TEMPLATE % ( url.netloc, self.cookie, url.path) client = FakeClientFactory(fake_session) reactor.connectTCP("0.0.0.0", self.tunnel_server.port, client) response = yield client.response self.assertIn(SAMPLE_CONTENT, response)
class RemoteSocketTestCase(SquidTestCase): """Tests for the client that connects to the other side.""" timeout = 3 def get_proxy_settings(self): return {} @defer.inlineCallbacks def setUp(self): """Initialize this testcase.""" yield super(RemoteSocketTestCase, self).setUp() self.ws = MockWebServer() self.addCleanup(self.ws.stop) self.dest_url = self.ws.get_iri().encode("utf-8") + SIMPLERESOURCE self.addCleanup(tunnel_server.QNetworkProxy.setApplicationProxy, tunnel_server.QNetworkProxy.applicationProxy()) settings = {"http": self.get_proxy_settings()} proxy = tunnel_server.build_proxy(settings) tunnel_server.QNetworkProxy.setApplicationProxy(proxy) def test_invalid_port(self): """A request with an invalid port fails with a 400.""" protocol = tunnel_server.ServerTunnelProtocol( tunnel_server.RemoteSocket) protocol.transport = FakeTransport() protocol.dataReceived("CONNECT 127.0.0.1:wrong_port HTTP/1.0" + CRLF * 2) status_line = protocol.transport.getvalue() self.assertTrue(status_line.startswith("HTTP/1.0 400 "), "The port must be an integer.") @defer.inlineCallbacks def test_connection_is_finished_when_stopping(self): """The client disconnects when requested.""" fake_protocol = FakeServerTunnelProtocol() client = tunnel_server.RemoteSocket(fake_protocol) url = urlparse(self.dest_url) yield client.connect(url.netloc) yield client.stop() @defer.inlineCallbacks def test_stop_but_never_connected(self): """Stop but it was never connected.""" fake_protocol = FakeServerTunnelProtocol() client = tunnel_server.RemoteSocket(fake_protocol) yield client.stop() @defer.inlineCallbacks def test_client_write(self): """Data written to the client is sent to the other side.""" fake_protocol = FakeServerTunnelProtocol() client = tunnel_server.RemoteSocket(fake_protocol) self.addCleanup(client.stop) url = urlparse(self.dest_url) yield client.connect(url.netloc) client.write("GET /simpleresource HTTP/1.0" + CRLF * 2) yield self.ws.simple_resource.rendered @defer.inlineCallbacks def test_client_read(self): """Data received by the client is written into the transport.""" fake_protocol = FakeServerTunnelProtocol() client = tunnel_server.RemoteSocket(fake_protocol) self.addCleanup(client.stop) url = urlparse(self.dest_url) yield client.connect(url.netloc) client.write("GET /simpleresource HTTP/1.0" + CRLF * 2) yield self.ws.simple_resource.rendered data = yield fake_protocol.response_received _headers, content = str(data).split(CRLF * 2, 1) self.assertEqual(content, SAMPLE_CONTENT)
class ServerTunnelProtocolTestCase(SquidTestCase): """Tests for the ServerTunnelProtocol.""" @defer.inlineCallbacks def setUp(self): """Initialize this test instance.""" yield super(ServerTunnelProtocolTestCase, self).setUp() self.ws = MockWebServer() self.addCleanup(self.ws.stop) self.dest_url = self.ws.get_iri().encode("utf-8") + SIMPLERESOURCE self.transport = FakeTransport() self.transport.cookie = FAKE_COOKIE self.fake_client = FakeClient() self.proto = tunnel_server.ServerTunnelProtocol( lambda _: self.fake_client) self.fake_client.protocol = self.proto self.proto.transport = self.transport self.cookie_line = "%s: %s" % (tunnel_server.TUNNEL_COOKIE_HEADER, FAKE_COOKIE) def test_broken_request(self): """Broken request.""" self.proto.dataReceived("Broken request." + CRLF) self.assertTrue(self.transport.getvalue().startswith("HTTP/1.0 400 "), "A broken request must fail.") def test_wrong_method(self): """Wrong method.""" self.proto.dataReceived("GET http://slashdot.org HTTP/1.0" + CRLF) self.assertTrue(self.transport.getvalue().startswith("HTTP/1.0 405 "), "Using a wrong method fails.") def test_invalid_http_version(self): """Invalid HTTP version.""" self.proto.dataReceived("CONNECT 127.0.0.1:9999 HTTP/1.1" + CRLF) self.assertTrue(self.transport.getvalue().startswith("HTTP/1.0 505 "), "Invalid http version is not allowed.") def test_connection_is_established(self): """The response code is sent.""" expected = "HTTP/1.0 200 Proxy connection established" + CRLF self.proto.dataReceived("CONNECT 127.0.0.1:9999 HTTP/1.0" + CRLF + self.cookie_line + CRLF * 2) self.assertTrue(self.transport.getvalue().startswith(expected), "First line must be the response status") def test_connection_fails(self): """The connection to the other end fails, and it's handled.""" error = tunnel_server.ConnectionError() self.patch(self.fake_client, "connection_result", defer.fail(error)) expected = "HTTP/1.0 500 Connection error" + CRLF self.proto.dataReceived("CONNECT 127.0.0.1:9999 HTTP/1.0" + CRLF + self.cookie_line + CRLF * 2) self.assertTrue(self.transport.getvalue().startswith(expected), "The connection should fail at this point.") def test_headers_stored(self): """The request headers are stored.""" expected = [ ("Header1", "value1"), ("Header2", "value2"), ] self.proto.dataReceived("CONNECT 127.0.0.1:9999 HTTP/1.0" + CRLF + "Header1: value1" + CRLF + "Header2: value2" + CRLF + CRLF) self.assertEqual(self.proto.received_headers, expected) def test_cookie_header_present(self): """The cookie header must be present.""" self.proto.received_headers = [ (tunnel_server.TUNNEL_COOKIE_HEADER, FAKE_COOKIE), ] self.proto.verify_cookie() def test_cookie_header_absent(self): """The tunnel should refuse connections without the cookie.""" self.proto.received_headers = [] exception = self.assertRaises(tunnel_server.ConnectionError, self.proto.verify_cookie) self.assertEqual(exception.code, 418) def test_successful_connect(self): """A successful connect thru the tunnel.""" url = urlparse(self.dest_url) data = FAKE_SESSION_TEMPLATE % (url.netloc, self.transport.cookie, url.path) self.proto.dataReceived(data) lines = self.transport.getvalue().split(CRLF) self.assertEqual(lines[-1], SAMPLE_CONTENT) def test_header_split(self): """Test a header with many colons.""" self.proto.header_line("key: host:port") self.assertIn("key", dict(self.proto.received_headers)) @defer.inlineCallbacks def test_keyring_credentials_are_retried(self): """Wrong credentials are retried with values from keyring.""" self.fake_client.check_credentials = True self.patch(self.proto, "verify_cookie", lambda: None) self.patch(self.proto, "error_response", lambda code, desc: self.fail(desc)) self.proto.proxy_domain = "xxx" self.patch(tunnel_server.Keyring, "get_credentials", lambda _, domain: defer.succeed(FAKE_CREDS)) yield self.proto.headers_done() def test_creds_are_not_logged(self): """The proxy credentials are not logged.""" log = [] self.patch(tunnel_server.logger, "info", lambda text, *args: log.append(text % args)) proxy = tunnel_server.build_proxy(FAKE_AUTH_SETTINGS) authenticator = QAuthenticator() username = FAKE_AUTH_SETTINGS["http"]["username"] password = FAKE_AUTH_SETTINGS["http"]["password"] self.proto.proxy_credentials = { "username": username, "password": password, } self.proto.proxy_domain = proxy.hostName() self.proto.proxy_auth_required(proxy, authenticator) for line in log: self.assertNotIn(username, line) self.assertNotIn(password, line)