Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTTPS proxy support #390

Merged
merged 1 commit into from
Aug 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 71 additions & 4 deletions lib/src/main/java/xyz/gianlu/librespot/core/Session.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
Expand Down Expand Up @@ -156,6 +158,10 @@ public Request authenticate(Route route, @NotNull Response response) {
}
});
}
if (conf.proxyType == Proxy.Type.HTTP && conf.proxySSL) {
// builder.socketFactory(SSLSocketFactory.getDefault()) throws an error on some okhttp versions
builder.socketFactory(new DelegatingSocketFactory(SSLSocketFactory.getDefault()));
}
}

builder.addInterceptor(chain -> {
Expand Down Expand Up @@ -1029,6 +1035,7 @@ public final static class Configuration {
// Proxy
public final boolean proxyEnabled;
public final Proxy.Type proxyType;
public final boolean proxySSL;
public final String proxyAddress;
public final int proxyPort;
public final boolean proxyAuth;
Expand All @@ -1054,13 +1061,15 @@ public final static class Configuration {
// Network
public final int connectionTimeout;

private Configuration(boolean proxyEnabled, Proxy.Type proxyType, String proxyAddress, int proxyPort, boolean proxyAuth, String proxyUsername, String proxyPassword,
private Configuration(boolean proxyEnabled, Proxy.Type proxyType, boolean proxySSL, String proxyAddress,
int proxyPort, boolean proxyAuth, String proxyUsername, String proxyPassword,
TimeProvider.Method timeSynchronizationMethod, int timeManualCorrection,
boolean cacheEnabled, File cacheDir, boolean doCacheCleanUp,
boolean storeCredentials, File storedCredentialsFile,
boolean retryOnChunkError, int connectionTimeout) {
this.proxyEnabled = proxyEnabled;
this.proxyType = proxyType;
this.proxySSL = proxySSL;
this.proxyAddress = proxyAddress;
this.proxyPort = proxyPort;
this.proxyAuth = proxyAuth;
Expand All @@ -1081,6 +1090,7 @@ public static final class Builder {
// Proxy
private boolean proxyEnabled = false;
private Proxy.Type proxyType;
private boolean proxySSL = false;
private String proxyAddress;
private int proxyPort;
private boolean proxyAuth;
Expand Down Expand Up @@ -1119,6 +1129,11 @@ public Builder setProxyType(Proxy.Type proxyType) {
return this;
}

public Builder setProxySSL(boolean proxySSL) {
this.proxySSL = proxySSL;
return this;
}

public Builder setProxyAddress(String proxyAddress) {
this.proxyAddress = proxyAddress;
return this;
Expand Down Expand Up @@ -1191,7 +1206,8 @@ public Builder setConnectionTimeout(int connectionTimeout) {

@NotNull
public Configuration build() {
return new Configuration(proxyEnabled, proxyType, proxyAddress, proxyPort, proxyAuth, proxyUsername, proxyPassword,
return new Configuration(proxyEnabled, proxyType, proxySSL, proxyAddress, proxyPort, proxyAuth,
proxyUsername, proxyPassword,
timeSynchronizationMethod, timeManualCorrection,
cacheEnabled, cacheDir, doCacheCleanUp,
storeCredentials, storedCredentialsFile,
Expand Down Expand Up @@ -1245,7 +1261,12 @@ static ConnectionHolder create(@NotNull String addr, @NotNull Configuration conf

switch (conf.proxyType) {
case HTTP:
Socket sock = new Socket(conf.proxyAddress, conf.proxyPort);
Socket sock;
if (conf.proxySSL) {
sock = SSLSocketFactory.getDefault().createSocket(conf.proxyAddress, conf.proxyPort);
} else{
sock = new Socket(conf.proxyAddress, conf.proxyPort);
}
OutputStream out = sock.getOutputStream();
DataInputStream in = new DataInputStream(sock.getInputStream());

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

LOGGER.info("Successfully connected to the HTTP proxy.");
LOGGER.info(String.format("Successfully connected to the %s proxy.", conf.proxySSL ? "HTTPS" : "HTTP"));
return new ConnectionHolder(sock);
case SOCKS:
if (conf.proxyAuth) {
Expand Down Expand Up @@ -1399,4 +1420,50 @@ public void run() {
LOGGER.trace("Session.Receiver stopped");
}
}

/**
* A {@link SocketFactory} that delegates calls. Sockets can be configured after creation by
* overriding {@link #configureSocket(java.net.Socket)}.
*
* Copy/pasted from okhttp3 tests sources for HTTPS proxy support
*/
public static class DelegatingSocketFactory extends SocketFactory {
private final SocketFactory delegate;

public DelegatingSocketFactory(SocketFactory delegate) {
this.delegate = delegate;
}

@Override public Socket createSocket() throws IOException {
Socket socket = delegate.createSocket();
return configureSocket(socket);
}

@Override public Socket createSocket(String host, int port) throws IOException {
Socket socket = delegate.createSocket(host, port);
return configureSocket(socket);
}

@Override public Socket createSocket(String host, int port, InetAddress localAddress,
int localPort) throws IOException {
Socket socket = delegate.createSocket(host, port, localAddress, localPort);
return configureSocket(socket);
}

@Override public Socket createSocket(InetAddress host, int port) throws IOException {
Socket socket = delegate.createSocket(host, port);
return configureSocket(socket);
}

@Override public Socket createSocket(InetAddress host, int port, InetAddress localAddress,
int localPort) throws IOException {
Socket socket = delegate.createSocket(host, port, localAddress, localPort);
return configureSocket(socket);
}

protected Socket configureSocket(Socket socket) throws IOException {
// No-op by default.
return socket;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ public Session.Configuration toSession() {
.setTimeManualCorrection(config.get("time.manualCorrection"))
.setProxyEnabled(config.get("proxy.enabled"))
.setProxyType(config.getEnum("proxy.type", Proxy.Type.class))
.setProxySSL(config.get("proxy.ssl"))
.setProxyAddress(config.get("proxy.address"))
.setProxyPort(config.get("proxy.port"))
.setProxyAuth(config.get("proxy.auth"))
Expand Down
1 change: 1 addition & 0 deletions player/src/main/resources/default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ host = "0.0.0.0" # API listen interface (`api` module only)
[proxy] ### Proxy ###
enabled = false # Whether the proxy is enabled
type = "HTTP" # The proxy type (HTTP, SOCKS)
ssl = false # Connect to proxy using SSL (HTTP only)
address = "" # The proxy hostname
port = 0 # The proxy port
auth = false # Whether authentication is enabled on the server
Expand Down