12f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin/*
22f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin * Copyright (C) 2015 The Android Open Source Project
32f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin *
42f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin * Licensed under the Apache License, Version 2.0 (the "License");
52f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin * you may not use this file except in compliance with the License.
62f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin * You may obtain a copy of the License at
72f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin *
82f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin *      http://www.apache.org/licenses/LICENSE-2.0
92f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin *
102f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin * Unless required by applicable law or agreed to in writing, software
112f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin * distributed under the License is distributed on an "AS IS" BASIS,
122f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin * See the License for the specific language governing permissions and
142f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin * limitations under the License.
152f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin */
162f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
172f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinpackage libcore.net;
182f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
192f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport junit.framework.TestCase;
202f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport libcore.io.IoUtils;
212f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.io.Closeable;
222f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.io.IOException;
232f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.net.JarURLConnection;
242f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.net.ServerSocket;
252f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.net.Socket;
262f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.net.URL;
272f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.Arrays;
28b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubakerimport java.util.HashMap;
29b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubakerimport java.util.Map;
302f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.concurrent.Callable;
312f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.concurrent.Future;
322f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.concurrent.FutureTask;
332f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.concurrent.TimeUnit;
342f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.concurrent.TimeoutException;
352f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.logging.ErrorManager;
362f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.logging.Level;
372f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.logging.LogRecord;
382f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinimport java.util.logging.SocketHandler;
392f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
402f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubinpublic class NetworkSecurityPolicyTest extends TestCase {
412f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
427a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker    private NetworkSecurityPolicy mOriginalPolicy;
432f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
442f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    @Override
452f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    protected void setUp() throws Exception {
462f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        super.setUp();
477a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        mOriginalPolicy = NetworkSecurityPolicy.getInstance();
482f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    }
492f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
502f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    @Override
512f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    protected void tearDown() throws Exception {
522f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        try {
537a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker            NetworkSecurityPolicy.setInstance(mOriginalPolicy);
542f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        } finally {
552f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            super.tearDown();
562f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
572f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    }
582f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
592f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    public void testCleartextTrafficPolicySetterAndGetter() {
607a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(false));
617a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        assertEquals(false, NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted());
622f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
637a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(true));
647a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        assertEquals(true, NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted());
652f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
667a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(false));
677a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        assertEquals(false, NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted());
682f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
697a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(true));
707a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        assertEquals(true, NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted());
712f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    }
722f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
73b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker    public void testHostnameAwareCleartextTrafficPolicySetterAndGetter() {
74b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(false));
75b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        assertEquals(false,
76b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker                NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted("localhost"));
77b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker
78b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(true));
79b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        assertEquals(true,
80b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker                NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted("localhost"));
81b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker
82b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        TestNetworkSecurityPolicy policy = new TestNetworkSecurityPolicy(false);
83b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        policy.addHostMapping("localhost", true);
84b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        policy.addHostMapping("example.com", false);
85b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        NetworkSecurityPolicy.setInstance(policy);
86b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        assertEquals(false, NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted());
87b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        assertEquals(true,
88b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker                NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted("localhost"));
89b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        assertEquals(false,
90b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker                NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted("example.com"));
91b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker
92b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker    }
93b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker
945a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin    public void testCleartextTrafficPolicyWithHttpURLConnection() throws Exception {
955a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        // Assert that client transmits some data when cleartext traffic is permitted.
967a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(true));
975a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket()) {
985a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            URL url = new URL("http://localhost:" + server.getPort() + "/test.txt");
995a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            try {
1005a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin                url.openConnection().getContent();
1015a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin                fail();
1025a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            } catch (IOException expected) {
1035a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            }
1045a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            server.assertDataTransmittedByClient();
1055a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        }
1065a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin
1075a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        // Assert that client does not transmit any data when cleartext traffic is not permitted and
1085a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        // that URLConnection.openConnection or getContent fail with an IOException.
1097a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(false));
1105a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket()) {
1115a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            URL url = new URL("http://localhost:" + server.getPort() + "/test.txt");
1125a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            try {
1135a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin                url.openConnection().getContent();
1145a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin                fail();
1155a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            } catch (IOException expected) {
1165a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            }
1175a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            server.assertNoDataTransmittedByClient();
1185a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        }
1195a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin    }
1205a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin
1212f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    public void testCleartextTrafficPolicyWithFtpURLConnection() throws Exception {
1222f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        // Assert that client transmits some data when cleartext traffic is permitted.
1237a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(true));
1242f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        byte[] serverReplyOnConnect = "220\r\n".getBytes("US-ASCII");
1252f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
1262f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            URL url = new URL("ftp://localhost:" + server.getPort() + "/test.txt");
1272f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            try {
1282f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                url.openConnection().getContent();
1292f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                fail();
1302f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            } catch (IOException expected) {
1312f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
1322f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            server.assertDataTransmittedByClient();
1332f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
1342f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
1352f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        // Assert that client does not transmit any data when cleartext traffic is not permitted and
1362f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        // that URLConnection.openConnection or getContent fail with an IOException.
1377a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(false));
1382f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
1392f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            URL url = new URL("ftp://localhost:" + server.getPort() + "/test.txt");
1402f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            try {
1412f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                url.openConnection().getContent();
1422f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                fail();
1432f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            } catch (IOException expected) {
1442f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
1452f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            server.assertNoDataTransmittedByClient();
1462f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
1472f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    }
1482f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
1495a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin    public void testCleartextTrafficPolicyWithJarHttpURLConnection() throws Exception {
1505a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        // Assert that client transmits some data when cleartext traffic is permitted.
1517a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(true));
1525a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket()) {
1535a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            URL url = new URL("jar:http://localhost:" + server.getPort() + "/test.jar!/");
1545a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            try {
1555a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin                ((JarURLConnection) url.openConnection()).getManifest();
1565a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin                fail();
1575a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            } catch (IOException expected) {
1585a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            }
1595a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            server.assertDataTransmittedByClient();
1605a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        }
1615a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin
1625a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        // Assert that client does not transmit any data when cleartext traffic is not permitted and
1635a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        // that JarURLConnection.openConnection or getManifest fail with an IOException.
1647a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(false));
1655a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket()) {
1665a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            URL url = new URL("jar:http://localhost:" + server.getPort() + "/test.jar!/");
1675a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            try {
1685a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin                ((JarURLConnection) url.openConnection()).getManifest();
1695a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin                fail();
1705a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            } catch (IOException expected) {
1715a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            }
1725a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin            server.assertNoDataTransmittedByClient();
1735a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin        }
1745a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin    }
1755a0a80d050d1bc7da77af16b7b2ad8a94d8e9bf6Alex Klyubin
1762f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    public void testCleartextTrafficPolicyWithJarFtpURLConnection() throws Exception {
1772f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        // Assert that client transmits some data when cleartext traffic is permitted.
1787a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(true));
1792f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        byte[] serverReplyOnConnect = "220\r\n".getBytes("US-ASCII");
1802f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
1812f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            URL url = new URL("jar:ftp://localhost:" + server.getPort() + "/test.jar!/");
1822f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            try {
1832f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                ((JarURLConnection) url.openConnection()).getManifest();
1842f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                fail();
1852f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            } catch (IOException expected) {
1862f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
1872f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            server.assertDataTransmittedByClient();
1882f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
1892f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
1902f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        // Assert that client does not transmit any data when cleartext traffic is not permitted and
1912f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        // that JarURLConnection.openConnection or getManifest fail with an IOException.
1927a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(false));
1932f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket(serverReplyOnConnect)) {
1942f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            URL url = new URL("jar:ftp://localhost:" + server.getPort() + "/test.jar!/");
1952f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            try {
1962f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                ((JarURLConnection) url.openConnection()).getManifest();
1972f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                fail();
1982f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            } catch (IOException expected) {
1992f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
2002f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            server.assertNoDataTransmittedByClient();
2012f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
2022f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    }
2032f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2042f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    public void testCleartextTrafficPolicyWithLoggingSocketHandler() throws Exception {
2052f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        // Assert that client transmits some data when cleartext traffic is permitted.
2067a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(true));
2072f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket()) {
2082f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            SocketHandler logger = new SocketHandler("localhost", server.getPort());
2092f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            MockErrorManager mockErrorManager = new MockErrorManager();
2102f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            logger.setErrorManager(mockErrorManager);
2112f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            logger.setLevel(Level.ALL);
2122f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            LogRecord record = new LogRecord(Level.INFO, "A log record");
2132f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            assertTrue(logger.isLoggable(record));
2142f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            logger.publish(record);
2152f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            assertNull(mockErrorManager.getMostRecentException());
2162f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            server.assertDataTransmittedByClient();
2172f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
2182f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2192f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        // Assert that client does not transmit any data when cleartext traffic is not permitted.
2207a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        NetworkSecurityPolicy.setInstance(new TestNetworkSecurityPolicy(false));
2212f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        try (CapturingServerSocket server = new CapturingServerSocket()) {
2222f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            try {
2232f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                new SocketHandler("localhost", server.getPort());
2242f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                fail();
2252f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            } catch (IOException expected) {
2262f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
2272f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            server.assertNoDataTransmittedByClient();
2282f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
2292f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    }
2302f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2312f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    /**
2322f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin     * Server socket which listens on a local port and captures the first chunk of data transmitted
2332f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin     * by the client.
2342f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin     */
2352f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    private static class CapturingServerSocket implements Closeable {
2362f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        private final ServerSocket mSocket;
2372f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        private final int mPort;
2382f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        private final Thread mListeningThread;
2392f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        private final FutureTask<byte[]> mFirstChunkReceivedFuture;
2402f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2412f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        /**
2422f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin         * Constructs a new socket listening on a local port.
2432f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin         */
2442f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        public CapturingServerSocket() throws IOException {
2452f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            this(null);
2462f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
2472f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2482f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        /**
2492f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin         * Constructs a new socket listening on a local port, which sends the provided reply as
2502f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin         * soon as a client connects to it.
2512f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin         */
2522f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        public CapturingServerSocket(final byte[] replyOnConnect) throws IOException {
2532f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            mSocket = new ServerSocket(0);
2542f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            mPort = mSocket.getLocalPort();
2552f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            mFirstChunkReceivedFuture = new FutureTask<byte[]>(new Callable<byte[]>() {
2562f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                @Override
2572f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                public byte[] call() throws Exception {
2582f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                    try (Socket client = mSocket.accept()) {
2592f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                        // Reply (if requested)
2602f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                        if (replyOnConnect != null) {
2612f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                            client.getOutputStream().write(replyOnConnect);
2622f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                            client.getOutputStream().flush();
2632f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                        }
2642f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2652f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                        // Read request
2662f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                        byte[] buf = new byte[64 * 1024];
2672f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                        int chunkSize = client.getInputStream().read(buf);
2682f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                        if (chunkSize == -1) {
2692f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                            // Connection closed without any data received
2702f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                            return new byte[0];
2712f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                        }
2722f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                        // Received some data
2732f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                        return Arrays.copyOf(buf, chunkSize);
2742f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                    } finally {
2752f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                        IoUtils.closeQuietly(mSocket);
2762f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                    }
2772f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                }
2782f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            });
2792f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            mListeningThread = new Thread(mFirstChunkReceivedFuture);
2802f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            mListeningThread.start();
2812f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
2822f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2832f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        public int getPort() {
2842f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            return mPort;
2852f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
2862f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2872f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        public Future<byte[]> getFirstReceivedChunkFuture() {
2882f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            return mFirstChunkReceivedFuture;
2892f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
2902f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2912f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        @Override
2922f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        public void close() {
2932f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            IoUtils.closeQuietly(mSocket);
2942f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            mListeningThread.interrupt();
2952f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
2962f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
2972f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        private void assertDataTransmittedByClient()
2982f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                throws Exception {
2992f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            byte[] firstChunkFromClient = getFirstReceivedChunkFuture().get(2, TimeUnit.SECONDS);
3002f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            if ((firstChunkFromClient == null) || (firstChunkFromClient.length == 0)) {
3012f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                fail("Client did not transmit any data to server");
3022f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
3032f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
3042f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
3052f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        private void assertNoDataTransmittedByClient()
3062f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                throws Exception {
3072f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            byte[] firstChunkFromClient;
3082f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            try {
3092f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                firstChunkFromClient = getFirstReceivedChunkFuture().get(2, TimeUnit.SECONDS);
3102f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            } catch (TimeoutException expected) {
3112f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                return;
3122f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
3132f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            if ((firstChunkFromClient != null) && (firstChunkFromClient.length > 0)) {
3142f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                fail("Client transmitted " + firstChunkFromClient.length+ " bytes: "
3152f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                        + new String(firstChunkFromClient, "US-ASCII"));
3162f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
3172f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
3182f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    }
3192f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
3202f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    private static class MockErrorManager extends ErrorManager {
3212f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        private Exception mMostRecentException;
3222f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
3232f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        public Exception getMostRecentException() {
3242f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            synchronized (this) {
3252f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                return mMostRecentException;
3262f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
3272f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
3282f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin
3292f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        @Override
3302f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        public void error(String message, Exception exception, int errorCode) {
3312f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            synchronized (this) {
3322f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin                mMostRecentException = exception;
3332f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin            }
3342f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin        }
3352f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin    }
3367a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker
3377a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker    private static class TestNetworkSecurityPolicy extends NetworkSecurityPolicy {
3387a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        private final boolean mCleartextTrafficPermitted;
339b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        private final Map<String, Boolean> mHostMap = new HashMap<String, Boolean>();
3407a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker
3417a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        public TestNetworkSecurityPolicy(boolean cleartextTrafficPermitted) {
3427a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker            mCleartextTrafficPermitted = cleartextTrafficPermitted;
3437a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        }
3447a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker
345b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        public void addHostMapping(String hostname, boolean isCleartextTrafficPermitted) {
346b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker            mHostMap.put(hostname, isCleartextTrafficPermitted);
347b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        }
348b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker
3497a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        @Override
3507a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        public boolean isCleartextTrafficPermitted() {
3517a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker            return mCleartextTrafficPermitted;
3527a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker        }
353b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker
354b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        @Override
355b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        public boolean isCleartextTrafficPermitted(String hostname) {
356b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker            if (mHostMap.containsKey(hostname)) {
357b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker                return mHostMap.get(hostname);
358b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker            }
359b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker
360b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker            return isCleartextTrafficPermitted();
361b80fefa18b3a185422222cfad3c7210c4903c37fChad Brubaker        }
3627a6f1687cfa0929e68ac7813b7432259b8088b4dChad Brubaker    }
3632f4d25190a7d2cc8328a202417ca9077a02e6d9aAlex Klyubin}
364