Skip to content

Commit 5af8a38

Browse files
committed
HTTPS proxy support
Uses SSLSocketFactory to create the proxy socket, otherwise identical to using an HTTP proxy. As Proxy.Type is used throughout the code and does not support HTTPS a proxySSL flag was added to Session.Configuration, and proxy.ssl was added to the config file.
1 parent 56c7a2c commit 5af8a38

3 files changed

Lines changed: 73 additions & 4 deletions

File tree

lib/src/main/java/xyz/gianlu/librespot/core/Session.java

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
import javax.crypto.Cipher;
5757
import javax.crypto.Mac;
5858
import javax.crypto.spec.SecretKeySpec;
59+
import javax.net.SocketFactory;
60+
import javax.net.ssl.SSLSocketFactory;
5961
import javax.xml.parsers.DocumentBuilder;
6062
import javax.xml.parsers.DocumentBuilderFactory;
6163
import javax.xml.parsers.ParserConfigurationException;
@@ -156,6 +158,10 @@ public Request authenticate(Route route, @NotNull Response response) {
156158
}
157159
});
158160
}
161+
if (conf.proxyType == Proxy.Type.HTTP && conf.proxySSL) {
162+
// builder.socketFactory(SSLSocketFactory.getDefault()) throws an error on some okhttp versions
163+
builder.socketFactory(new DelegatingSocketFactory(SSLSocketFactory.getDefault()));
164+
}
159165
}
160166

161167
builder.addInterceptor(chain -> {
@@ -1029,6 +1035,7 @@ public final static class Configuration {
10291035
// Proxy
10301036
public final boolean proxyEnabled;
10311037
public final Proxy.Type proxyType;
1038+
public final boolean proxySSL;
10321039
public final String proxyAddress;
10331040
public final int proxyPort;
10341041
public final boolean proxyAuth;
@@ -1054,13 +1061,15 @@ public final static class Configuration {
10541061
// Network
10551062
public final int connectionTimeout;
10561063

1057-
private Configuration(boolean proxyEnabled, Proxy.Type proxyType, String proxyAddress, int proxyPort, boolean proxyAuth, String proxyUsername, String proxyPassword,
1064+
private Configuration(boolean proxyEnabled, Proxy.Type proxyType, boolean proxySSL, String proxyAddress,
1065+
int proxyPort, boolean proxyAuth, String proxyUsername, String proxyPassword,
10581066
TimeProvider.Method timeSynchronizationMethod, int timeManualCorrection,
10591067
boolean cacheEnabled, File cacheDir, boolean doCacheCleanUp,
10601068
boolean storeCredentials, File storedCredentialsFile,
10611069
boolean retryOnChunkError, int connectionTimeout) {
10621070
this.proxyEnabled = proxyEnabled;
10631071
this.proxyType = proxyType;
1072+
this.proxySSL = proxySSL;
10641073
this.proxyAddress = proxyAddress;
10651074
this.proxyPort = proxyPort;
10661075
this.proxyAuth = proxyAuth;
@@ -1081,6 +1090,7 @@ public static final class Builder {
10811090
// Proxy
10821091
private boolean proxyEnabled = false;
10831092
private Proxy.Type proxyType;
1093+
private boolean proxySSL = false;
10841094
private String proxyAddress;
10851095
private int proxyPort;
10861096
private boolean proxyAuth;
@@ -1119,6 +1129,11 @@ public Builder setProxyType(Proxy.Type proxyType) {
11191129
return this;
11201130
}
11211131

1132+
public Builder setProxySSL(boolean proxySSL) {
1133+
this.proxySSL = proxySSL;
1134+
return this;
1135+
}
1136+
11221137
public Builder setProxyAddress(String proxyAddress) {
11231138
this.proxyAddress = proxyAddress;
11241139
return this;
@@ -1191,7 +1206,8 @@ public Builder setConnectionTimeout(int connectionTimeout) {
11911206

11921207
@NotNull
11931208
public Configuration build() {
1194-
return new Configuration(proxyEnabled, proxyType, proxyAddress, proxyPort, proxyAuth, proxyUsername, proxyPassword,
1209+
return new Configuration(proxyEnabled, proxyType, proxySSL, proxyAddress, proxyPort, proxyAuth,
1210+
proxyUsername, proxyPassword,
11951211
timeSynchronizationMethod, timeManualCorrection,
11961212
cacheEnabled, cacheDir, doCacheCleanUp,
11971213
storeCredentials, storedCredentialsFile,
@@ -1245,7 +1261,12 @@ static ConnectionHolder create(@NotNull String addr, @NotNull Configuration conf
12451261

12461262
switch (conf.proxyType) {
12471263
case HTTP:
1248-
Socket sock = new Socket(conf.proxyAddress, conf.proxyPort);
1264+
Socket sock;
1265+
if (conf.proxySSL) {
1266+
sock = SSLSocketFactory.getDefault().createSocket(conf.proxyAddress, conf.proxyPort);
1267+
} else{
1268+
sock = new Socket(conf.proxyAddress, conf.proxyPort);
1269+
}
12491270
OutputStream out = sock.getOutputStream();
12501271
DataInputStream in = new DataInputStream(sock.getInputStream());
12511272

@@ -1264,7 +1285,7 @@ static ConnectionHolder create(@NotNull String addr, @NotNull Configuration conf
12641285
// Read all headers
12651286
}
12661287

1267-
LOGGER.info("Successfully connected to the HTTP proxy.");
1288+
LOGGER.info(String.format("Successfully connected to the %s proxy.", conf.proxySSL ? "HTTPS" : "HTTP"));
12681289
return new ConnectionHolder(sock);
12691290
case SOCKS:
12701291
if (conf.proxyAuth) {
@@ -1399,4 +1420,50 @@ public void run() {
13991420
LOGGER.trace("Session.Receiver stopped");
14001421
}
14011422
}
1423+
1424+
/**
1425+
* A {@link SocketFactory} that delegates calls. Sockets can be configured after creation by
1426+
* overriding {@link #configureSocket(java.net.Socket)}.
1427+
*
1428+
* Copy/pasted from okhttp3 tests sources for HTTPS proxy support
1429+
*/
1430+
public static class DelegatingSocketFactory extends SocketFactory {
1431+
private final SocketFactory delegate;
1432+
1433+
public DelegatingSocketFactory(SocketFactory delegate) {
1434+
this.delegate = delegate;
1435+
}
1436+
1437+
@Override public Socket createSocket() throws IOException {
1438+
Socket socket = delegate.createSocket();
1439+
return configureSocket(socket);
1440+
}
1441+
1442+
@Override public Socket createSocket(String host, int port) throws IOException {
1443+
Socket socket = delegate.createSocket(host, port);
1444+
return configureSocket(socket);
1445+
}
1446+
1447+
@Override public Socket createSocket(String host, int port, InetAddress localAddress,
1448+
int localPort) throws IOException {
1449+
Socket socket = delegate.createSocket(host, port, localAddress, localPort);
1450+
return configureSocket(socket);
1451+
}
1452+
1453+
@Override public Socket createSocket(InetAddress host, int port) throws IOException {
1454+
Socket socket = delegate.createSocket(host, port);
1455+
return configureSocket(socket);
1456+
}
1457+
1458+
@Override public Socket createSocket(InetAddress host, int port, InetAddress localAddress,
1459+
int localPort) throws IOException {
1460+
Socket socket = delegate.createSocket(host, port, localAddress, localPort);
1461+
return configureSocket(socket);
1462+
}
1463+
1464+
protected Socket configureSocket(Socket socket) throws IOException {
1465+
// No-op by default.
1466+
return socket;
1467+
}
1468+
}
14021469
}

player/src/main/java/xyz/gianlu/librespot/player/FileConfiguration.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ public Session.Configuration toSession() {
411411
.setTimeManualCorrection(config.get("time.manualCorrection"))
412412
.setProxyEnabled(config.get("proxy.enabled"))
413413
.setProxyType(config.getEnum("proxy.type", Proxy.Type.class))
414+
.setProxySSL(config.get("proxy.ssl"))
414415
.setProxyAddress(config.get("proxy.address"))
415416
.setProxyPort(config.get("proxy.port"))
416417
.setProxyAuth(config.get("proxy.auth"))

player/src/main/resources/default.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ host = "0.0.0.0" # API listen interface (`api` module only)
5858
[proxy] ### Proxy ###
5959
enabled = false # Whether the proxy is enabled
6060
type = "HTTP" # The proxy type (HTTP, SOCKS)
61+
ssl = false # Connect to proxy using SSL (HTTP only)
6162
address = "" # The proxy hostname
6263
port = 0 # The proxy port
6364
auth = false # Whether authentication is enabled on the server

0 commit comments

Comments
 (0)