Skip to content

Commit 708da3b

Browse files
author
Federico Fissore
committed
Introducing CustomProxySelector
1 parent d884a2d commit 708da3b

File tree

7 files changed

+267
-39
lines changed

7 files changed

+267
-39
lines changed

app/src/cc/arduino/view/preferences/Preferences.java

Lines changed: 25 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
package cc.arduino.view.preferences;
3131

32+
import cc.arduino.Constants;
3233
import processing.app.Base;
3334
import processing.app.BaseNoGui;
3435
import processing.app.I18n;
@@ -46,20 +47,6 @@
4647

4748
public class Preferences extends javax.swing.JDialog {
4849

49-
public static final String PROXY_TYPE_NONE = "none";
50-
public static final String PROXY_TYPE_AUTO = "auto";
51-
public static final String PROXY_TYPE_MANUAL = "manual";
52-
public static final String PROXY_MANUAL_TYPE_HTTP = "HTTP";
53-
public static final String PROXY_MANUAL_TYPE_SOCKS = "SOCKS";
54-
55-
public static final String PREF_PROXY_MANUAL_TYPE = "proxy.manual.type";
56-
public static final String PREF_PROXY_TYPE = "proxy.type";
57-
public static final String PREF_PROXY_PAC_URL = "proxy.pac.url";
58-
public static final String PREF_PROXY_MANUAL_HOSTNAME = "proxy.manual.hostname";
59-
public static final String PREF_PROXY_MANUAL_PORT = "proxy.manual.port";
60-
public static final String PREF_PROXY_MANUAL_USERNAME = "proxy.manual.username";
61-
public static final String PREF_PROXY_MANUAL_PASSWORD = "proxy.manual.password";
62-
6350
private final Language[] languages;
6451
private final Language[] missingLanguages;
6552
private final WarningItem[] warningItems;
@@ -460,11 +447,11 @@ public void mouseEntered(java.awt.event.MouseEvent evt) {
460447

461448
proxyTypeButtonGroup.add(noProxy);
462449
noProxy.setText(tr("No proxy"));
463-
noProxy.setActionCommand(PROXY_TYPE_NONE);
450+
noProxy.setActionCommand(Constants.PROXY_TYPE_NONE);
464451

465452
proxyTypeButtonGroup.add(autoProxy);
466453
autoProxy.setText(tr("Auto-detect proxy settings"));
467-
autoProxy.setActionCommand(PROXY_TYPE_AUTO);
454+
autoProxy.setActionCommand(Constants.PROXY_TYPE_AUTO);
468455
autoProxy.addItemListener(new java.awt.event.ItemListener() {
469456
public void itemStateChanged(java.awt.event.ItemEvent evt) {
470457
autoProxyItemStateChanged(evt);
@@ -473,7 +460,7 @@ public void itemStateChanged(java.awt.event.ItemEvent evt) {
473460

474461
proxyTypeButtonGroup.add(manualProxy);
475462
manualProxy.setText(tr("Manual proxy configuration"));
476-
manualProxy.setActionCommand(PROXY_TYPE_MANUAL);
463+
manualProxy.setActionCommand(Constants.PROXY_TYPE_MANUAL);
477464
manualProxy.addItemListener(new java.awt.event.ItemListener() {
478465
public void itemStateChanged(java.awt.event.ItemEvent evt) {
479466
manualProxyItemStateChanged(evt);
@@ -489,11 +476,11 @@ public void itemStateChanged(java.awt.event.ItemEvent evt) {
489476

490477
manualProxyTypeButtonGroup.add(manualProxyHTTP);
491478
manualProxyHTTP.setText("HTTP");
492-
manualProxyHTTP.setActionCommand(PROXY_MANUAL_TYPE_HTTP);
479+
manualProxyHTTP.setActionCommand(Constants.PROXY_MANUAL_TYPE_HTTP);
493480

494481
manualProxyTypeButtonGroup.add(manualProxySOCKS);
495482
manualProxySOCKS.setText("SOCKS");
496-
manualProxySOCKS.setActionCommand(PROXY_MANUAL_TYPE_SOCKS);
483+
manualProxySOCKS.setActionCommand(Constants.PROXY_MANUAL_TYPE_SOCKS);
497484

498485
manualProxyHostNameLabel.setText(tr("Host name:"));
499486

@@ -803,13 +790,13 @@ private void savePreferencesData() {
803790

804791
PreferencesData.set("boardsmanager.additional.urls", additionalBoardsManagerField.getText().replace("\r\n", "\n").replace("\r", "\n").replace("\n", ","));
805792

806-
PreferencesData.set(PREF_PROXY_TYPE, proxyTypeButtonGroup.getSelection().getActionCommand());
807-
PreferencesData.set(PREF_PROXY_PAC_URL, autoProxyUsePAC.isSelected() ? autoProxyPACURL.getText() : "");
808-
PreferencesData.set(PREF_PROXY_MANUAL_TYPE, manualProxyTypeButtonGroup.getSelection().getActionCommand());
809-
PreferencesData.set(PREF_PROXY_MANUAL_HOSTNAME, manualProxyHostName.getText());
810-
PreferencesData.set(PREF_PROXY_MANUAL_PORT, manualProxyPort.getText());
811-
PreferencesData.set(PREF_PROXY_MANUAL_USERNAME, manualProxyUsername.getText());
812-
PreferencesData.set(PREF_PROXY_MANUAL_PASSWORD, String.valueOf(manualProxyPassword.getPassword()));
793+
PreferencesData.set(Constants.PREF_PROXY_TYPE, proxyTypeButtonGroup.getSelection().getActionCommand());
794+
PreferencesData.set(Constants.PREF_PROXY_PAC_URL, autoProxyUsePAC.isSelected() ? autoProxyPACURL.getText() : "");
795+
PreferencesData.set(Constants.PREF_PROXY_MANUAL_TYPE, manualProxyTypeButtonGroup.getSelection().getActionCommand());
796+
PreferencesData.set(Constants.PREF_PROXY_MANUAL_HOSTNAME, manualProxyHostName.getText());
797+
PreferencesData.set(Constants.PREF_PROXY_MANUAL_PORT, manualProxyPort.getText());
798+
PreferencesData.set(Constants.PREF_PROXY_MANUAL_USERNAME, manualProxyUsername.getText());
799+
PreferencesData.set(Constants.PREF_PROXY_MANUAL_PASSWORD, String.valueOf(manualProxyPassword.getPassword()));
813800
}
814801

815802
private void showPrerefencesData() {
@@ -852,29 +839,29 @@ private void showPrerefencesData() {
852839
additionalBoardsManagerField.setText(PreferencesData.get("boardsmanager.additional.urls"));
853840

854841
disableAllProxyFields();
855-
String proxyType = PreferencesData.get(PREF_PROXY_TYPE, PROXY_TYPE_AUTO);
842+
String proxyType = PreferencesData.get(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_AUTO);
856843

857-
if (PROXY_TYPE_NONE.equals(proxyType)) {
844+
if (Constants.PROXY_TYPE_NONE.equals(proxyType)) {
858845
noProxy.setSelected(true);
859-
} else if (PROXY_TYPE_AUTO.equals(proxyType)) {
846+
} else if (Constants.PROXY_TYPE_AUTO.equals(proxyType)) {
860847
autoProxy.setSelected(true);
861848
autoProxyFieldsSetEnabled(true);
862-
if (!PreferencesData.get(PREF_PROXY_PAC_URL, "").isEmpty()) {
849+
if (!PreferencesData.get(Constants.PREF_PROXY_PAC_URL, "").isEmpty()) {
863850
autoProxyUsePAC.setSelected(true);
864-
autoProxyPACURL.setText(PreferencesData.get(PREF_PROXY_PAC_URL));
851+
autoProxyPACURL.setText(PreferencesData.get(Constants.PREF_PROXY_PAC_URL));
865852
}
866853
} else {
867854
manualProxy.setSelected(true);
868855
manualProxyFieldsSetEnabled(true);
869-
manualProxyHostName.setText(PreferencesData.get(PREF_PROXY_MANUAL_HOSTNAME));
870-
manualProxyPort.setText(PreferencesData.get(PREF_PROXY_MANUAL_PORT));
871-
manualProxyUsername.setText(PreferencesData.get(PREF_PROXY_MANUAL_USERNAME));
872-
manualProxyPassword.setText(PreferencesData.get(PREF_PROXY_MANUAL_PASSWORD));
856+
manualProxyHostName.setText(PreferencesData.get(Constants.PREF_PROXY_MANUAL_HOSTNAME));
857+
manualProxyPort.setText(PreferencesData.get(Constants.PREF_PROXY_MANUAL_PORT));
858+
manualProxyUsername.setText(PreferencesData.get(Constants.PREF_PROXY_MANUAL_USERNAME));
859+
manualProxyPassword.setText(PreferencesData.get(Constants.PREF_PROXY_MANUAL_PASSWORD));
873860
}
874861

875-
String selectedManualProxyType = PreferencesData.get(PREF_PROXY_MANUAL_TYPE, PROXY_MANUAL_TYPE_HTTP);
876-
manualProxyHTTP.setSelected(PROXY_MANUAL_TYPE_HTTP.equals(selectedManualProxyType));
877-
manualProxySOCKS.setSelected(PROXY_MANUAL_TYPE_SOCKS.equals(selectedManualProxyType));
862+
String selectedManualProxyType = PreferencesData.get(Constants.PREF_PROXY_MANUAL_TYPE, Constants.PROXY_MANUAL_TYPE_HTTP);
863+
manualProxyHTTP.setSelected(Constants.PROXY_MANUAL_TYPE_HTTP.equals(selectedManualProxyType));
864+
manualProxySOCKS.setSelected(Constants.PROXY_MANUAL_TYPE_SOCKS.equals(selectedManualProxyType));
878865
}
879866

880867
private void manualProxyFieldsSetEnabled(boolean enabled) {
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package cc.arduino.net;
2+
3+
import cc.arduino.Constants;
4+
import org.junit.Before;
5+
import org.junit.Test;
6+
7+
import java.net.*;
8+
import java.util.HashMap;
9+
import java.util.Map;
10+
11+
import static org.junit.Assert.assertEquals;
12+
13+
public class CustomProxySelectorTest {
14+
15+
private Map<String, String> preferences;
16+
private URI uri;
17+
18+
@Before
19+
public void setUp() throws Exception {
20+
System.setProperty("java.net.useSystemProxies", "true");
21+
uri = new URL("https://www.arduino.cc").toURI();
22+
preferences = new HashMap<>();
23+
}
24+
25+
@Test
26+
public void testNoProxy() throws Exception {
27+
preferences.put(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_NONE);
28+
CustomProxySelector proxySelector = new CustomProxySelector(preferences);
29+
Proxy proxy = proxySelector.getProxyFor(uri);
30+
31+
assertEquals(Proxy.NO_PROXY, proxy);
32+
}
33+
34+
@Test
35+
public void testSystemProxy() throws Exception {
36+
preferences.put(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_AUTO);
37+
CustomProxySelector proxySelector = new CustomProxySelector(preferences);
38+
Proxy proxy = proxySelector.getProxyFor(uri);
39+
40+
assertEquals(ProxySelector.getDefault().select(uri).get(0), proxy);
41+
}
42+
43+
@Test
44+
public void testProxyPACHTTP() throws Exception {
45+
preferences.put(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_AUTO);
46+
preferences.put(Constants.PREF_PROXY_PAC_URL, CustomProxySelectorTest.class.getResource("proxy_http.pac").toExternalForm());
47+
CustomProxySelector proxySelector = new CustomProxySelector(preferences);
48+
Proxy proxy = proxySelector.getProxyFor(uri);
49+
50+
assertEquals(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.example.com", 8080)), proxy);
51+
}
52+
53+
@Test
54+
public void testProxyPACSOCKS() throws Exception {
55+
preferences.put(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_AUTO);
56+
preferences.put(Constants.PREF_PROXY_PAC_URL, CustomProxySelectorTest.class.getResource("proxy_socks.pac").toExternalForm());
57+
CustomProxySelector proxySelector = new CustomProxySelector(preferences);
58+
Proxy proxy = proxySelector.getProxyFor(uri);
59+
60+
assertEquals(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("proxy.example.com", 8080)), proxy);
61+
}
62+
63+
@Test
64+
public void testProxyPACDirect() throws Exception {
65+
preferences.put(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_AUTO);
66+
preferences.put(Constants.PREF_PROXY_PAC_URL, CustomProxySelectorTest.class.getResource("proxy_direct.pac").toExternalForm());
67+
CustomProxySelector proxySelector = new CustomProxySelector(preferences);
68+
Proxy proxy = proxySelector.getProxyFor(uri);
69+
70+
assertEquals(Proxy.NO_PROXY, proxy);
71+
}
72+
73+
@Test
74+
public void testManualProxy() throws Exception {
75+
preferences.put(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_MANUAL);
76+
preferences.put(Constants.PREF_PROXY_MANUAL_TYPE, Constants.PROXY_MANUAL_TYPE_HTTP);
77+
preferences.put(Constants.PREF_PROXY_MANUAL_HOSTNAME, "localhost");
78+
preferences.put(Constants.PREF_PROXY_MANUAL_PORT, "8080");
79+
80+
CustomProxySelector proxySelector = new CustomProxySelector(preferences);
81+
Proxy proxy = proxySelector.getProxyFor(uri);
82+
83+
assertEquals(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", 8080)), proxy);
84+
}
85+
86+
@Test
87+
public void testManualProxyWithLogin() throws Exception {
88+
preferences.put(Constants.PREF_PROXY_TYPE, Constants.PROXY_TYPE_MANUAL);
89+
preferences.put(Constants.PREF_PROXY_MANUAL_TYPE, Constants.PROXY_MANUAL_TYPE_HTTP);
90+
preferences.put(Constants.PREF_PROXY_MANUAL_HOSTNAME, "localhost");
91+
preferences.put(Constants.PREF_PROXY_MANUAL_PORT, "8080");
92+
preferences.put(Constants.PREF_PROXY_MANUAL_USERNAME, "username");
93+
preferences.put(Constants.PREF_PROXY_MANUAL_PASSWORD, "pwd");
94+
95+
CustomProxySelector proxySelector = new CustomProxySelector(preferences);
96+
Proxy proxy = proxySelector.getProxyFor(uri);
97+
98+
assertEquals(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", 8080)), proxy);
99+
100+
PasswordAuthentication authentication = Authenticator.requestPasswordAuthentication(null, 8080, uri.toURL().getProtocol(), "ciao", "");
101+
assertEquals(authentication.getUserName(), "username");
102+
assertEquals(String.valueOf(authentication.getPassword()), "pwd");
103+
}
104+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
function FindProxyForURL(url, host)
2+
{
3+
return "DIRECT";
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
function FindProxyForURL(url, host)
2+
{
3+
return "PROXY proxy.example.com:8080; DIRECT";
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
function FindProxyForURL(url, host)
2+
{
3+
return "SOCKS proxy.example.com:8080; DIRECT";
4+
}

arduino-core/src/cc/arduino/Constants.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,26 @@ public class Constants {
4040

4141
public static final String DEFAULT_INDEX_FILE_NAME = "package_index.json";
4242
public static final List<String> PROTECTED_PACKAGE_NAMES = Arrays.asList("arduino", "Intel");
43-
public static final String PACKAGE_INDEX_URL;
4443

4544
public static final String LIBRARY_DEVELOPMENT_FLAG_FILE = ".development";
4645

4746
public static final long BOARDS_LIBS_UPDATABLE_CHECK_START_PERIOD = 60000;
4847
public static final int NOTIFICATION_POPUP_AUTOCLOSE_DELAY = 10000;
4948

49+
public static final String PROXY_TYPE_NONE = "none";
50+
public static final String PROXY_TYPE_AUTO = "auto";
51+
public static final String PROXY_TYPE_MANUAL = "manual";
52+
public static final String PROXY_MANUAL_TYPE_HTTP = "HTTP";
53+
public static final String PROXY_MANUAL_TYPE_SOCKS = "SOCKS";
54+
public static final String PREF_PROXY_MANUAL_TYPE = "proxy.manual.type";
55+
public static final String PREF_PROXY_TYPE = "proxy.type";
56+
public static final String PREF_PROXY_PAC_URL = "proxy.pac.url";
57+
public static final String PREF_PROXY_MANUAL_HOSTNAME = "proxy.manual.hostname";
58+
public static final String PREF_PROXY_MANUAL_PORT = "proxy.manual.port";
59+
public static final String PREF_PROXY_MANUAL_USERNAME = "proxy.manual.username";
60+
public static final String PREF_PROXY_MANUAL_PASSWORD = "proxy.manual.password";
61+
62+
public static final String PACKAGE_INDEX_URL;
5063
public static final String LIBRARY_INDEX_URL;
5164
public static final String LIBRARY_INDEX_URL_GZ;
5265

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package cc.arduino.net;
2+
3+
import cc.arduino.Constants;
4+
import org.apache.commons.compress.utils.IOUtils;
5+
6+
import javax.script.Invocable;
7+
import javax.script.ScriptEngine;
8+
import javax.script.ScriptEngineManager;
9+
import javax.script.ScriptException;
10+
import java.io.IOException;
11+
import java.net.*;
12+
import java.nio.charset.Charset;
13+
import java.util.Map;
14+
15+
public class CustomProxySelector {
16+
17+
private final Map<String, String> preferences;
18+
19+
public CustomProxySelector(Map<String, String> preferences) {
20+
this.preferences = preferences;
21+
clearPreviousAuthenticator();
22+
}
23+
24+
public Proxy getProxyFor(URI uri) throws IOException, ScriptException, NoSuchMethodException {
25+
if (Constants.PROXY_TYPE_NONE.equals(preferences.get(Constants.PREF_PROXY_TYPE))) {
26+
return Proxy.NO_PROXY;
27+
}
28+
29+
if (Constants.PROXY_TYPE_AUTO.equals(preferences.get(Constants.PREF_PROXY_TYPE))) {
30+
String pac = preferences.get(Constants.PREF_PROXY_PAC_URL);
31+
if (pac == null || pac.isEmpty()) {
32+
return ProxySelector.getDefault().select(uri).get(0);
33+
}
34+
35+
return pacProxy(pac, uri);
36+
}
37+
38+
if (preferences.get(Constants.PREF_PROXY_TYPE).equals(Constants.PROXY_TYPE_MANUAL)) {
39+
return manualProxy();
40+
}
41+
42+
throw new IllegalStateException("Unable to understand proxy settings");
43+
}
44+
45+
private Proxy pacProxy(String pac, URI uri) throws IOException, ScriptException, NoSuchMethodException {
46+
URLConnection urlConnection = new URL(pac).openConnection();
47+
urlConnection.connect();
48+
if (urlConnection instanceof HttpURLConnection) {
49+
int responseCode = ((HttpURLConnection) urlConnection).getResponseCode();
50+
if (responseCode != 200) {
51+
throw new IOException("Unable to fetch PAC file at " + pac + ". Response code is " + responseCode);
52+
}
53+
}
54+
String pacScript = new String(IOUtils.toByteArray(urlConnection.getInputStream()), Charset.forName("ASCII"));
55+
56+
ScriptEngine nashorn = new ScriptEngineManager().getEngineByName("nashorn");
57+
nashorn.eval(pacScript);
58+
String proxyConfigs = callFindProxyForURL(uri, nashorn);
59+
return makeProxyFrom(proxyConfigs);
60+
}
61+
62+
private Proxy makeProxyFrom(String proxyConfigs) {
63+
String proxyConfig = proxyConfigs.split(";")[0];
64+
if (proxyConfig.startsWith("DIRECT")) {
65+
return Proxy.NO_PROXY;
66+
}
67+
Proxy.Type type;
68+
if (proxyConfig.startsWith("PROXY")) {
69+
type = Proxy.Type.HTTP;
70+
proxyConfig = proxyConfig.replace("PROXY ", "");
71+
} else {
72+
type = Proxy.Type.SOCKS;
73+
proxyConfig = proxyConfig.replace("SOCKS ", "");
74+
}
75+
String[] hostPort = proxyConfig.split(":");
76+
return new Proxy(type, new InetSocketAddress(hostPort[0], Integer.valueOf(hostPort[1])));
77+
}
78+
79+
private String callFindProxyForURL(URI uri, ScriptEngine nashorn) throws ScriptException, NoSuchMethodException {
80+
Invocable script = (Invocable) nashorn;
81+
return (String) script.invokeFunction("FindProxyForURL", toUrl(uri).toExternalForm());
82+
}
83+
84+
private URL toUrl(URI uri) {
85+
try {
86+
return uri.toURL();
87+
} catch (MalformedURLException e) {
88+
throw new RuntimeException(e);
89+
}
90+
}
91+
92+
private Proxy manualProxy() {
93+
setAuthenticator();
94+
Proxy.Type type = Proxy.Type.valueOf(preferences.get(Constants.PREF_PROXY_MANUAL_TYPE));
95+
return new Proxy(type, new InetSocketAddress(preferences.get(Constants.PREF_PROXY_MANUAL_HOSTNAME), Integer.valueOf(preferences.get(Constants.PREF_PROXY_MANUAL_PORT))));
96+
}
97+
98+
private void clearPreviousAuthenticator() {
99+
Authenticator.setDefault(null);
100+
}
101+
102+
private void setAuthenticator() {
103+
Authenticator.setDefault(
104+
new Authenticator() {
105+
public PasswordAuthentication getPasswordAuthentication() {
106+
return new PasswordAuthentication(
107+
preferences.get(Constants.PREF_PROXY_MANUAL_USERNAME), preferences.get(Constants.PREF_PROXY_MANUAL_PASSWORD).toCharArray());
108+
}
109+
}
110+
);
111+
}
112+
}

0 commit comments

Comments
 (0)