Skip to content

Commit

Permalink
🎉 fix #28: ignore system proxy for localhost requests
Browse files Browse the repository at this point in the history
  • Loading branch information
danikula committed Apr 23, 2017
1 parent 8726693 commit c300b2e
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ private HttpProxyCacheServer(Config config) {
InetAddress inetAddress = InetAddress.getByName(PROXY_HOST);
this.serverSocket = new ServerSocket(0, 8, inetAddress);
this.port = serverSocket.getLocalPort();
IgnoreHostProxySelector.install(PROXY_HOST, port);
CountDownLatch startSignal = new CountDownLatch(1);
this.waitConnectionThread = new Thread(new WaitRequestsRunnable(startSignal));
this.waitConnectionThread.start();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.danikula.videocache;

import java.io.IOException;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.URI;
import java.util.Arrays;
import java.util.List;

import static com.danikula.videocache.Preconditions.checkNotNull;

/**
* {@link ProxySelector} that ignore system default proxies for concrete host.
* <p>
* It is important to <a href="https://github.com/danikula/AndroidVideoCache/issues/28">ignore system proxy</a> for localhost connection.
*
* @author Alexey Danilov (danikula@gmail.com).
*/
class IgnoreHostProxySelector extends ProxySelector {

private static final List<Proxy> NO_PROXY_LIST = Arrays.asList(Proxy.NO_PROXY);

private final ProxySelector defaultProxySelector;
private final String hostToIgnore;
private final int portToIgnore;

IgnoreHostProxySelector(ProxySelector defaultProxySelector, String hostToIgnore, int portToIgnore) {
this.defaultProxySelector = checkNotNull(defaultProxySelector);
this.hostToIgnore = checkNotNull(hostToIgnore);
this.portToIgnore = portToIgnore;
}

static void install(String hostToIgnore, int portToIgnore) {
ProxySelector defaultProxySelector = ProxySelector.getDefault();
ProxySelector ignoreHostProxySelector = new IgnoreHostProxySelector(defaultProxySelector, hostToIgnore, portToIgnore);
ProxySelector.setDefault(ignoreHostProxySelector);
}

@Override
public List<Proxy> select(URI uri) {
boolean ignored = hostToIgnore.equals(uri.getHost()) && portToIgnore == uri.getPort();
List<Proxy> proxies = ignored ? NO_PROXY_LIST : defaultProxySelector.select(uri);
return proxies;
}

@Override
public void connectFailed(URI uri, SocketAddress address, IOException failure) {
defaultProxySelector.connectFailed(uri, address, failure);
}
}
4 changes: 2 additions & 2 deletions library/src/main/java/com/danikula/videocache/Pinger.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ boolean ping(int maxAttempts, int startTimeout) {
}

private List<Proxy> getDefaultProxies() {
ProxySelector proxySelector = ProxySelector.getDefault();
try {
return proxySelector.select(new URI("https://github.com"));
ProxySelector defaultProxySelector = ProxySelector.getDefault();
return defaultProxySelector.select(new URI(getPingUrl()));
} catch (URISyntaxException e) {
throw new IllegalStateException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import com.danikula.videocache.support.ProxyCacheTestUtils;
import com.danikula.videocache.support.Response;

import junit.framework.Assert;

import org.junit.Before;
import org.junit.Test;
import org.robolectric.RuntimeEnvironment;
Expand All @@ -31,8 +33,11 @@
import static com.danikula.videocache.support.ProxyCacheTestUtils.HTTP_DATA_URL_ONE_REDIRECT;
import static com.danikula.videocache.support.ProxyCacheTestUtils.getFileContent;
import static com.danikula.videocache.support.ProxyCacheTestUtils.getPort;
import static com.danikula.videocache.support.ProxyCacheTestUtils.getPortWithoutPing;
import static com.danikula.videocache.support.ProxyCacheTestUtils.installExternalSystemProxy;
import static com.danikula.videocache.support.ProxyCacheTestUtils.loadAssetFile;
import static com.danikula.videocache.support.ProxyCacheTestUtils.readProxyResponse;
import static com.danikula.videocache.support.ProxyCacheTestUtils.resetSystemProxy;
import static org.fest.assertions.api.Assertions.assertThat;

/**
Expand All @@ -47,6 +52,7 @@ public void setup() throws Exception {
cacheFolder = ProxyCacheTestUtils.newCacheFile();
createDirectory(cacheFolder);
cleanDirectory(cacheFolder);
resetSystemProxy();
}

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

@Test // https://github.com/danikula/AndroidVideoCache/issues/28
public void testWorkWithExternalProxy() throws Exception {
installExternalSystemProxy();

Pair<File, Response> response = readProxyData(HTTP_DATA_URL, 0);
assertThat(response.second.data).isEqualTo(loadAssetFile(ASSETS_DATA_NAME));
}

@Test(expected = IOException.class) // https://github.com/danikula/AndroidVideoCache/issues/28
public void testDoesNotWorkWithoutCustomProxySelector() throws Exception {
HttpProxyCacheServer httpProxyCacheServer = new HttpProxyCacheServer(RuntimeEnvironment.application);
// IgnoreHostProxySelector is set in HttpProxyCacheServer constructor. So let reset it by custom.
installExternalSystemProxy();

String proxiedUrl = "http://127.0.0.1:" + getPortWithoutPing(httpProxyCacheServer) + "/" + HTTP_DATA_URL;
readProxyResponse(httpProxyCacheServer, proxiedUrl);
Assert.fail(); // should throw IOException on the previous line
}

private Pair<File, Response> readProxyData(String url, int offset) throws IOException {
File file = file(cacheFolder, url);
HttpProxyCacheServer proxy = newProxy(cacheFolder);
Expand Down
32 changes: 32 additions & 0 deletions test/src/test/java/com/danikula/videocache/PingerTest.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
package com.danikula.videocache;

import junit.framework.Assert;

import org.junit.Before;
import org.junit.Test;
import org.robolectric.RuntimeEnvironment;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.Socket;

import static com.danikula.videocache.support.ProxyCacheTestUtils.HTTP_DATA_URL;
import static com.danikula.videocache.support.ProxyCacheTestUtils.getPort;
import static com.danikula.videocache.support.ProxyCacheTestUtils.getPortWithoutPing;
import static com.danikula.videocache.support.ProxyCacheTestUtils.installExternalSystemProxy;
import static com.danikula.videocache.support.ProxyCacheTestUtils.readProxyResponse;
import static com.danikula.videocache.support.ProxyCacheTestUtils.resetSystemProxy;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
Expand All @@ -18,6 +27,11 @@
*/
public class PingerTest extends BaseTest {

@Before
public void setup() throws Exception {
resetSystemProxy();
}

@Test
public void testPingSuccess() throws Exception {
HttpProxyCacheServer server = new HttpProxyCacheServer(RuntimeEnvironment.application);
Expand Down Expand Up @@ -52,5 +66,23 @@ public void testResponseToPing() throws Exception {
assertThat(out.toString()).isEqualTo("HTTP/1.1 200 OK\n\nping ok");
}

@Test // https://github.com/danikula/AndroidVideoCache/issues/28
public void testPingedWithExternalProxy() throws Exception {
installExternalSystemProxy();

HttpProxyCacheServer server = new HttpProxyCacheServer(RuntimeEnvironment.application);
Pinger pinger = new Pinger("127.0.0.1", getPortWithoutPing(server));
assertThat(pinger.ping(1, 100)).isTrue();
}

@Test(expected = IOException.class) // https://github.com/danikula/AndroidVideoCache/issues/28
public void testIsNotPingedWithoutCustomProxySelector() throws Exception {
HttpProxyCacheServer httpProxyCacheServer = new HttpProxyCacheServer(RuntimeEnvironment.application);
// IgnoreHostProxySelector is set in HttpProxyCacheServer constructor. So let reset it by custom.
installExternalSystemProxy();

String proxiedUrl = "http://127.0.0.1:" + getPortWithoutPing(httpProxyCacheServer) + "/" + HTTP_DATA_URL;
readProxyResponse(httpProxyCacheServer, proxiedUrl);
Assert.fail(); // should throw IOException on the previous line
}
}
54 changes: 54 additions & 0 deletions test/src/test/java/com/danikula/videocache/ProxySelectorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.danikula.videocache;

import com.google.common.collect.Lists;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URI;
import java.util.List;

import static com.danikula.videocache.support.ProxyCacheTestUtils.resetSystemProxy;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

/**
* Tests {@link IgnoreHostProxySelector}.
*
* @author Alexey Danilov (danikula@gmail.com).
*/
public class ProxySelectorTest extends BaseTest {

@Before
public void setup() throws Exception {
resetSystemProxy();
}

@Test // https://github.com/danikula/AndroidVideoCache/issues/28
public void testIgnoring() throws Exception {
InetSocketAddress proxyAddress = new InetSocketAddress("proxy.com", 80);
Proxy systemProxy = new Proxy(Proxy.Type.HTTP, proxyAddress);
ProxySelector mockedProxySelector = Mockito.mock(ProxySelector.class);
when(mockedProxySelector.select(Mockito.<URI>any())).thenReturn(Lists.newArrayList(systemProxy));
ProxySelector.setDefault(mockedProxySelector);

IgnoreHostProxySelector.install("localhost", 42);

ProxySelector proxySelector = ProxySelector.getDefault();
List<Proxy> githubProxies = proxySelector.select(new URI("http://github.com"));
assertThat(githubProxies).hasSize(1);
assertThat(githubProxies.get(0).address()).isEqualTo(proxyAddress);

List<Proxy> localhostProxies = proxySelector.select(new URI("http://localhost:42"));
assertThat(localhostProxies).hasSize(1);
assertThat(localhostProxies.get(0)).isEqualTo(Proxy.NO_PROXY);

List<Proxy> localhostPort69Proxies = proxySelector.select(new URI("http://localhost:69"));
assertThat(localhostPort69Proxies).hasSize(1);
assertThat(localhostPort69Proxies.get(0).address()).isEqualTo(proxyAddress);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
import com.danikula.videocache.ProxyCacheException;
import com.danikula.videocache.Source;
import com.danikula.videocache.sourcestorage.SourceInfoStorage;
import com.google.common.collect.Lists;
import com.google.common.io.Files;

import org.apache.tools.ant.util.ReflectUtil;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.robolectric.RuntimeEnvironment;
Expand All @@ -18,6 +21,10 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URL;
import java.util.Random;
import java.util.UUID;
Expand All @@ -31,6 +38,7 @@
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

/**
* @author Alexey Danilov (danikula@gmail.com).
Expand Down Expand Up @@ -147,4 +155,22 @@ public static int getPort(HttpProxyCacheServer server) {
String portAsString = matcher.group(1);
return Integer.parseInt(portAsString);
}

public static int getPortWithoutPing(HttpProxyCacheServer server) {
return (Integer) ReflectUtil.getField(server, "port");
}

public static void installExternalSystemProxy() {
// see proxies list at http://proxylist.hidemyass.com/
Proxy systemProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("162.8.230.7", 11180));
ProxySelector mockedProxySelector = Mockito.mock(ProxySelector.class);
when(mockedProxySelector.select(Mockito.<URI>any())).thenReturn(Lists.newArrayList(systemProxy));
ProxySelector.setDefault(mockedProxySelector);
}

public static void resetSystemProxy() {
ProxySelector mockedProxySelector = Mockito.mock(ProxySelector.class);
when(mockedProxySelector.select(Mockito.<URI>any())).thenReturn(Lists.newArrayList(Proxy.NO_PROXY));
ProxySelector.setDefault(mockedProxySelector);
}
}

0 comments on commit c300b2e

Please sign in to comment.