Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,20 @@
*/
package org.jivesoftware.smack.websocket.okhttp;

import java.util.logging.Level;

import javax.net.ssl.SSLSession;

import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.proxy.ProxyInfo;
import org.jivesoftware.smack.websocket.impl.AbstractWebSocket;
import org.jivesoftware.smack.websocket.rce.WebSocketRemoteConnectionEndpoint;

import java.net.InetSocketAddress;
import java.net.Proxy;
import java.time.Duration;
import java.util.logging.Level;

import javax.net.ssl.SSLSession;

import okhttp3.Authenticator;
import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
Expand All @@ -36,6 +42,13 @@ public final class OkHttpWebSocket extends AbstractWebSocket {

OkHttpWebSocket(WebSocketRemoteConnectionEndpoint endpoint,
ModularXmppClientToServerConnectionInternal connectionInternal) {
this(endpoint, null, connectionInternal);
}

OkHttpWebSocket(WebSocketRemoteConnectionEndpoint endpoint,
Long connectionTimeout,
ModularXmppClientToServerConnectionInternal connectionInternal) {

super(endpoint, connectionInternal);

var okHttpClientBuilder = new OkHttpClient.Builder();
Expand All @@ -46,13 +59,19 @@ public final class OkHttpWebSocket extends AbstractWebSocket {
okHttpClientBuilder.sslSocketFactory(tlsContext.sslContext.getSocketFactory(), customTrustManager);
}
}

if (connectionTimeout != null)
okHttpClientBuilder.connectTimeout(Duration.ofMillis(connectionTimeout));

// Checking if connection is not null is only necessary since we use mocked objects where it may be null in test
// cases. Otherwise, the 'connection' field will always be non-null.
if (connectionInternal.connection != null) {
var customHostnameVerifier = connectionInternal.connection.getConfiguration().getHostnameVerifier();
if (customHostnameVerifier != null) {
okHttpClientBuilder.hostnameVerifier(customHostnameVerifier);
}

applyProxySettings(okHttpClientBuilder, connectionInternal.connection.getConfiguration().getProxyInfo());
}
var okHttpClient = okHttpClientBuilder.build();

Expand All @@ -65,6 +84,86 @@ public final class OkHttpWebSocket extends AbstractWebSocket {
okHttpWebSocket = okHttpClient.newWebSocket(request, listener);
}

private void applyProxySettings(OkHttpClient.Builder okHttpClientBuilder, ProxyInfo proxyInfo) {
if (proxyInfo == null || proxyInfo.getProxyType() == null) {
return;
}

switch (proxyInfo.getProxyType()) {
case HTTP:
applyHttpProxy(okHttpClientBuilder, proxyInfo);
break;
case SOCKS4:
case SOCKS5:
applySOCKSProxy(okHttpClientBuilder, proxyInfo);
break;
default:
throw new IllegalStateException("Unsupported proxy type");
}
}

private void applySOCKSProxy(OkHttpClient.Builder okHttpClientBuilder, ProxyInfo proxyInfo) {
if (proxyInfo == null) {
return;
}
String proxyHost = proxyInfo.getProxyAddress();
int proxyPort = proxyInfo.getProxyPort();
if (proxyHost == null || proxyHost.isBlank()
|| proxyPort == 0) {
return;
}


Proxy proxy = new Proxy(Proxy.Type.SOCKS, InetSocketAddress.createUnresolved(proxyHost, proxyPort));
okHttpClientBuilder.proxy(proxy);
// The authenticator must be set e.g. via java.net.Authenticator.setDefault();
}

private void applyHttpProxy(OkHttpClient.Builder okHttpClientBuilder, ProxyInfo proxyInfo) {

final String PROXY_AUTH_HEADER = "Proxy-Authorization";

if (proxyInfo == null) {
return;
}
String proxyHost = proxyInfo.getProxyAddress();
int proxyPort = proxyInfo.getProxyPort();
if (proxyHost == null || proxyHost.isBlank()
|| proxyPort == 0) {
return;
}


var proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
okHttpClientBuilder.proxy(proxy);

String username = proxyInfo.getProxyUsername();
if (username == null || username.isBlank()) {
return;
}
String password = proxyInfo.getProxyPassword() != null ? proxyInfo.getProxyPassword() : "";

Authenticator proxyAuthenticator = (route, response) ->
{
// If the proxy header is already set, the credentials are probably wrong.
// So do nothing!
if (response.request().header(PROXY_AUTH_HEADER) != null) {
return null;
}

// Generate Basic Auth credentials
String credentials = Credentials.basic(username, password);

// Add the 'Proxy-Authorization' header to the request
return response.request()
.newBuilder()
.header(PROXY_AUTH_HEADER, credentials)
.build();
};
okHttpClientBuilder.proxyAuthenticator(proxyAuthenticator);

}

private final WebSocketListener listener = new WebSocketListener() {

@Override
Expand Down
Loading