Skip to content

Commit c300b2e

Browse files
committed
🎉 fix #28: ignore system proxy for localhost requests
1 parent 8726693 commit c300b2e

7 files changed

Lines changed: 191 additions & 2 deletions

File tree

library/src/main/java/com/danikula/videocache/HttpProxyCacheServer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ private HttpProxyCacheServer(Config config) {
7373
InetAddress inetAddress = InetAddress.getByName(PROXY_HOST);
7474
this.serverSocket = new ServerSocket(0, 8, inetAddress);
7575
this.port = serverSocket.getLocalPort();
76+
IgnoreHostProxySelector.install(PROXY_HOST, port);
7677
CountDownLatch startSignal = new CountDownLatch(1);
7778
this.waitConnectionThread = new Thread(new WaitRequestsRunnable(startSignal));
7879
this.waitConnectionThread.start();
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.danikula.videocache;
2+
3+
import java.io.IOException;
4+
import java.net.Proxy;
5+
import java.net.ProxySelector;
6+
import java.net.SocketAddress;
7+
import java.net.URI;
8+
import java.util.Arrays;
9+
import java.util.List;
10+
11+
import static com.danikula.videocache.Preconditions.checkNotNull;
12+
13+
/**
14+
* {@link ProxySelector} that ignore system default proxies for concrete host.
15+
* <p>
16+
* It is important to <a href="https://github.com/danikula/AndroidVideoCache/issues/28">ignore system proxy</a> for localhost connection.
17+
*
18+
* @author Alexey Danilov (danikula@gmail.com).
19+
*/
20+
class IgnoreHostProxySelector extends ProxySelector {
21+
22+
private static final List<Proxy> NO_PROXY_LIST = Arrays.asList(Proxy.NO_PROXY);
23+
24+
private final ProxySelector defaultProxySelector;
25+
private final String hostToIgnore;
26+
private final int portToIgnore;
27+
28+
IgnoreHostProxySelector(ProxySelector defaultProxySelector, String hostToIgnore, int portToIgnore) {
29+
this.defaultProxySelector = checkNotNull(defaultProxySelector);
30+
this.hostToIgnore = checkNotNull(hostToIgnore);
31+
this.portToIgnore = portToIgnore;
32+
}
33+
34+
static void install(String hostToIgnore, int portToIgnore) {
35+
ProxySelector defaultProxySelector = ProxySelector.getDefault();
36+
ProxySelector ignoreHostProxySelector = new IgnoreHostProxySelector(defaultProxySelector, hostToIgnore, portToIgnore);
37+
ProxySelector.setDefault(ignoreHostProxySelector);
38+
}
39+
40+
@Override
41+
public List<Proxy> select(URI uri) {
42+
boolean ignored = hostToIgnore.equals(uri.getHost()) && portToIgnore == uri.getPort();
43+
List<Proxy> proxies = ignored ? NO_PROXY_LIST : defaultProxySelector.select(uri);
44+
return proxies;
45+
}
46+
47+
@Override
48+
public void connectFailed(URI uri, SocketAddress address, IOException failure) {
49+
defaultProxySelector.connectFailed(uri, address, failure);
50+
}
51+
}

library/src/main/java/com/danikula/videocache/Pinger.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ boolean ping(int maxAttempts, int startTimeout) {
7575
}
7676

7777
private List<Proxy> getDefaultProxies() {
78-
ProxySelector proxySelector = ProxySelector.getDefault();
7978
try {
80-
return proxySelector.select(new URI("https://github.com"));
79+
ProxySelector defaultProxySelector = ProxySelector.getDefault();
80+
return defaultProxySelector.select(new URI(getPingUrl()));
8181
} catch (URISyntaxException e) {
8282
throw new IllegalStateException(e);
8383
}

test/src/test/java/com/danikula/videocache/HttpProxyCacheServerTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import com.danikula.videocache.support.ProxyCacheTestUtils;
1010
import com.danikula.videocache.support.Response;
1111

12+
import junit.framework.Assert;
13+
1214
import org.junit.Before;
1315
import org.junit.Test;
1416
import org.robolectric.RuntimeEnvironment;
@@ -31,8 +33,11 @@
3133
import static com.danikula.videocache.support.ProxyCacheTestUtils.HTTP_DATA_URL_ONE_REDIRECT;
3234
import static com.danikula.videocache.support.ProxyCacheTestUtils.getFileContent;
3335
import static com.danikula.videocache.support.ProxyCacheTestUtils.getPort;
36+
import static com.danikula.videocache.support.ProxyCacheTestUtils.getPortWithoutPing;
37+
import static com.danikula.videocache.support.ProxyCacheTestUtils.installExternalSystemProxy;
3438
import static com.danikula.videocache.support.ProxyCacheTestUtils.loadAssetFile;
3539
import static com.danikula.videocache.support.ProxyCacheTestUtils.readProxyResponse;
40+
import static com.danikula.videocache.support.ProxyCacheTestUtils.resetSystemProxy;
3641
import static org.fest.assertions.api.Assertions.assertThat;
3742

3843
/**
@@ -47,6 +52,7 @@ public void setup() throws Exception {
4752
cacheFolder = ProxyCacheTestUtils.newCacheFile();
4853
createDirectory(cacheFolder);
4954
cleanDirectory(cacheFolder);
55+
resetSystemProxy();
5056
}
5157

5258
@Test
@@ -338,6 +344,25 @@ public void testTrimFileCacheForTotalSizeLru() throws Exception {
338344
assertThat(new File(cacheFolder, fileNameGenerator.generate(HTTP_DATA_URL))).doesNotExist();
339345
}
340346

347+
@Test // https://github.com/danikula/AndroidVideoCache/issues/28
348+
public void testWorkWithExternalProxy() throws Exception {
349+
installExternalSystemProxy();
350+
351+
Pair<File, Response> response = readProxyData(HTTP_DATA_URL, 0);
352+
assertThat(response.second.data).isEqualTo(loadAssetFile(ASSETS_DATA_NAME));
353+
}
354+
355+
@Test(expected = IOException.class) // https://github.com/danikula/AndroidVideoCache/issues/28
356+
public void testDoesNotWorkWithoutCustomProxySelector() throws Exception {
357+
HttpProxyCacheServer httpProxyCacheServer = new HttpProxyCacheServer(RuntimeEnvironment.application);
358+
// IgnoreHostProxySelector is set in HttpProxyCacheServer constructor. So let reset it by custom.
359+
installExternalSystemProxy();
360+
361+
String proxiedUrl = "http://127.0.0.1:" + getPortWithoutPing(httpProxyCacheServer) + "/" + HTTP_DATA_URL;
362+
readProxyResponse(httpProxyCacheServer, proxiedUrl);
363+
Assert.fail(); // should throw IOException on the previous line
364+
}
365+
341366
private Pair<File, Response> readProxyData(String url, int offset) throws IOException {
342367
File file = file(cacheFolder, url);
343368
HttpProxyCacheServer proxy = newProxy(cacheFolder);

test/src/test/java/com/danikula/videocache/PingerTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
package com.danikula.videocache;
22

3+
import junit.framework.Assert;
4+
5+
import org.junit.Before;
36
import org.junit.Test;
47
import org.robolectric.RuntimeEnvironment;
58

69
import java.io.ByteArrayOutputStream;
10+
import java.io.IOException;
711
import java.net.Socket;
812

13+
import static com.danikula.videocache.support.ProxyCacheTestUtils.HTTP_DATA_URL;
914
import static com.danikula.videocache.support.ProxyCacheTestUtils.getPort;
15+
import static com.danikula.videocache.support.ProxyCacheTestUtils.getPortWithoutPing;
16+
import static com.danikula.videocache.support.ProxyCacheTestUtils.installExternalSystemProxy;
17+
import static com.danikula.videocache.support.ProxyCacheTestUtils.readProxyResponse;
18+
import static com.danikula.videocache.support.ProxyCacheTestUtils.resetSystemProxy;
1019
import static org.fest.assertions.api.Assertions.assertThat;
1120
import static org.mockito.Mockito.mock;
1221
import static org.mockito.Mockito.when;
@@ -18,6 +27,11 @@
1827
*/
1928
public class PingerTest extends BaseTest {
2029

30+
@Before
31+
public void setup() throws Exception {
32+
resetSystemProxy();
33+
}
34+
2135
@Test
2236
public void testPingSuccess() throws Exception {
2337
HttpProxyCacheServer server = new HttpProxyCacheServer(RuntimeEnvironment.application);
@@ -52,5 +66,23 @@ public void testResponseToPing() throws Exception {
5266
assertThat(out.toString()).isEqualTo("HTTP/1.1 200 OK\n\nping ok");
5367
}
5468

69+
@Test // https://github.com/danikula/AndroidVideoCache/issues/28
70+
public void testPingedWithExternalProxy() throws Exception {
71+
installExternalSystemProxy();
72+
73+
HttpProxyCacheServer server = new HttpProxyCacheServer(RuntimeEnvironment.application);
74+
Pinger pinger = new Pinger("127.0.0.1", getPortWithoutPing(server));
75+
assertThat(pinger.ping(1, 100)).isTrue();
76+
}
77+
78+
@Test(expected = IOException.class) // https://github.com/danikula/AndroidVideoCache/issues/28
79+
public void testIsNotPingedWithoutCustomProxySelector() throws Exception {
80+
HttpProxyCacheServer httpProxyCacheServer = new HttpProxyCacheServer(RuntimeEnvironment.application);
81+
// IgnoreHostProxySelector is set in HttpProxyCacheServer constructor. So let reset it by custom.
82+
installExternalSystemProxy();
5583

84+
String proxiedUrl = "http://127.0.0.1:" + getPortWithoutPing(httpProxyCacheServer) + "/" + HTTP_DATA_URL;
85+
readProxyResponse(httpProxyCacheServer, proxiedUrl);
86+
Assert.fail(); // should throw IOException on the previous line
87+
}
5688
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.danikula.videocache;
2+
3+
import com.google.common.collect.Lists;
4+
5+
import org.junit.Before;
6+
import org.junit.Test;
7+
import org.mockito.Mockito;
8+
9+
import java.net.InetSocketAddress;
10+
import java.net.Proxy;
11+
import java.net.ProxySelector;
12+
import java.net.URI;
13+
import java.util.List;
14+
15+
import static com.danikula.videocache.support.ProxyCacheTestUtils.resetSystemProxy;
16+
import static org.fest.assertions.api.Assertions.assertThat;
17+
import static org.mockito.Mockito.when;
18+
19+
/**
20+
* Tests {@link IgnoreHostProxySelector}.
21+
*
22+
* @author Alexey Danilov (danikula@gmail.com).
23+
*/
24+
public class ProxySelectorTest extends BaseTest {
25+
26+
@Before
27+
public void setup() throws Exception {
28+
resetSystemProxy();
29+
}
30+
31+
@Test // https://github.com/danikula/AndroidVideoCache/issues/28
32+
public void testIgnoring() throws Exception {
33+
InetSocketAddress proxyAddress = new InetSocketAddress("proxy.com", 80);
34+
Proxy systemProxy = new Proxy(Proxy.Type.HTTP, proxyAddress);
35+
ProxySelector mockedProxySelector = Mockito.mock(ProxySelector.class);
36+
when(mockedProxySelector.select(Mockito.<URI>any())).thenReturn(Lists.newArrayList(systemProxy));
37+
ProxySelector.setDefault(mockedProxySelector);
38+
39+
IgnoreHostProxySelector.install("localhost", 42);
40+
41+
ProxySelector proxySelector = ProxySelector.getDefault();
42+
List<Proxy> githubProxies = proxySelector.select(new URI("http://github.com"));
43+
assertThat(githubProxies).hasSize(1);
44+
assertThat(githubProxies.get(0).address()).isEqualTo(proxyAddress);
45+
46+
List<Proxy> localhostProxies = proxySelector.select(new URI("http://localhost:42"));
47+
assertThat(localhostProxies).hasSize(1);
48+
assertThat(localhostProxies.get(0)).isEqualTo(Proxy.NO_PROXY);
49+
50+
List<Proxy> localhostPort69Proxies = proxySelector.select(new URI("http://localhost:69"));
51+
assertThat(localhostPort69Proxies).hasSize(1);
52+
assertThat(localhostPort69Proxies.get(0).address()).isEqualTo(proxyAddress);
53+
}
54+
}

test/src/test/java/com/danikula/videocache/support/ProxyCacheTestUtils.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77
import com.danikula.videocache.ProxyCacheException;
88
import com.danikula.videocache.Source;
99
import com.danikula.videocache.sourcestorage.SourceInfoStorage;
10+
import com.google.common.collect.Lists;
1011
import com.google.common.io.Files;
1112

13+
import org.apache.tools.ant.util.ReflectUtil;
14+
import org.mockito.Mockito;
1215
import org.mockito.invocation.InvocationOnMock;
1316
import org.mockito.stubbing.Answer;
1417
import org.robolectric.RuntimeEnvironment;
@@ -18,6 +21,10 @@
1821
import java.io.IOException;
1922
import java.io.InputStream;
2023
import java.net.HttpURLConnection;
24+
import java.net.InetSocketAddress;
25+
import java.net.Proxy;
26+
import java.net.ProxySelector;
27+
import java.net.URI;
2128
import java.net.URL;
2229
import java.util.Random;
2330
import java.util.UUID;
@@ -31,6 +38,7 @@
3138
import static org.mockito.Mockito.doThrow;
3239
import static org.mockito.Mockito.mock;
3340
import static org.mockito.Mockito.spy;
41+
import static org.mockito.Mockito.when;
3442

3543
/**
3644
* @author Alexey Danilov (danikula@gmail.com).
@@ -147,4 +155,22 @@ public static int getPort(HttpProxyCacheServer server) {
147155
String portAsString = matcher.group(1);
148156
return Integer.parseInt(portAsString);
149157
}
158+
159+
public static int getPortWithoutPing(HttpProxyCacheServer server) {
160+
return (Integer) ReflectUtil.getField(server, "port");
161+
}
162+
163+
public static void installExternalSystemProxy() {
164+
// see proxies list at http://proxylist.hidemyass.com/
165+
Proxy systemProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("162.8.230.7", 11180));
166+
ProxySelector mockedProxySelector = Mockito.mock(ProxySelector.class);
167+
when(mockedProxySelector.select(Mockito.<URI>any())).thenReturn(Lists.newArrayList(systemProxy));
168+
ProxySelector.setDefault(mockedProxySelector);
169+
}
170+
171+
public static void resetSystemProxy() {
172+
ProxySelector mockedProxySelector = Mockito.mock(ProxySelector.class);
173+
when(mockedProxySelector.select(Mockito.<URI>any())).thenReturn(Lists.newArrayList(Proxy.NO_PROXY));
174+
ProxySelector.setDefault(mockedProxySelector);
175+
}
150176
}

0 commit comments

Comments
 (0)