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 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)