Rework Proxy API

Instead of extending SocketFactory, the proxy support classes now
implement ProxySocketConnection. This removes a lot of unnecessary
code.

Also re-enables proxy support, which was broken in previous versions
because none of extended SocketFactories did override
createSocket() (SMACK-683).
This commit is contained in:
Florian Schmaus 2015-07-17 08:50:19 +02:00
parent 24365f0f1e
commit 7032688123
9 changed files with 104 additions and 266 deletions

View File

@ -37,6 +37,12 @@ public final class BOSHConfiguration extends ConnectionConfiguration {
private BOSHConfiguration(Builder builder) {
super(builder);
if (proxy != null) {
if (proxy.getProxyType() != ProxyInfo.ProxyType.HTTP) {
throw new IllegalArgumentException(
"Only HTTP proxies are support with BOSH connections");
}
}
https = builder.https;
if (builder.file.charAt(0) != '/') {
file = '/' + builder.file;
@ -46,7 +52,7 @@ public final class BOSHConfiguration extends ConnectionConfiguration {
}
public boolean isProxyEnabled() {
return (proxy != null && proxy.getProxyType() != ProxyInfo.ProxyType.NONE);
return proxy != null;
}
public ProxyInfo getProxyInfo() {

View File

@ -125,14 +125,7 @@ public abstract class ConnectionConfiguration {
port = builder.port;
proxy = builder.proxy;
if (proxy != null) {
if (builder.socketFactory != null) {
throw new IllegalArgumentException("Can not use proxy together with custom socket factory");
}
socketFactory = proxy.getSocketFactory();
} else {
socketFactory = builder.socketFactory;
}
socketFactory = builder.socketFactory;
securityMode = builder.securityMode;
keystoreType = builder.keystoreType;
@ -300,6 +293,15 @@ public abstract class ConnectionConfiguration {
return this.socketFactory;
}
/**
* Get the configured proxy information (if any).
*
* @return the configured proxy information or <code>null</code>.
*/
public ProxyInfo getProxyInfo() {
return proxy;
}
/**
* An enumeration for TLS security modes that are available when making a connection
* to the XMPP server.

View File

@ -1,70 +0,0 @@
/**
*
* Copyright the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.proxy;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.net.SocketFactory;
/**
* SocketFactory for direct connection
*
* @author Atul Aggarwal
*/
class DirectSocketFactory
extends SocketFactory
{
public DirectSocketFactory()
{
}
public Socket createSocket(String host, int port)
throws IOException, UnknownHostException
{
Socket newSocket = new Socket(Proxy.NO_PROXY);
newSocket.connect(new InetSocketAddress(host,port));
return newSocket;
}
public Socket createSocket(String host ,int port, InetAddress localHost,
int localPort)
throws IOException, UnknownHostException
{
return new Socket(host,port,localHost,localPort);
}
public Socket createSocket(InetAddress host, int port)
throws IOException
{
Socket newSocket = new Socket(Proxy.NO_PROXY);
newSocket.connect(new InetSocketAddress(host,port));
return newSocket;
}
public Socket createSocket( InetAddress address, int port,
InetAddress localAddress, int localPort)
throws IOException
{
return new Socket(address,port,localAddress,localPort);
}
}

View File

@ -22,10 +22,8 @@ import java.io.InputStream;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.net.SocketFactory;
import org.jivesoftware.smack.util.stringencoder.Base64;
@ -33,56 +31,26 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Http Proxy Socket Factory which returns socket connected to Http Proxy
* HTTP Proxy Socket Connection which connects the socket using a HTTP Proxy.
*
* @author Atul Aggarwal
*/
class HTTPProxySocketFactory
extends SocketFactory
{
class HTTPProxySocketConnection implements ProxySocketConnection {
private ProxyInfo proxy;
private final ProxyInfo proxy;
public HTTPProxySocketFactory(ProxyInfo proxy)
HTTPProxySocketConnection(ProxyInfo proxy)
{
this.proxy = proxy;
}
public Socket createSocket(String host, int port)
throws IOException, UnknownHostException
{
return httpProxifiedSocket(host, port);
}
public Socket createSocket(String host ,int port, InetAddress localHost,
int localPort)
throws IOException, UnknownHostException
{
return httpProxifiedSocket(host, port);
}
public Socket createSocket(InetAddress host, int port)
throws IOException
{
return httpProxifiedSocket(host.getHostAddress(), port);
}
public Socket createSocket( InetAddress address, int port,
InetAddress localAddress, int localPort)
throws IOException
{
return httpProxifiedSocket(address.getHostAddress(), port);
}
private Socket httpProxifiedSocket(String host, int port)
throws IOException
{
@Override
public void connect(Socket socket, InetAddress inetAddress, int port, int timeout)
throws IOException {
String proxyhost = proxy.getProxyAddress();
int proxyPort = proxy.getProxyPort();
@SuppressWarnings("resource")
Socket socket = new Socket(proxyhost,proxyPort);
String hostport = "CONNECT " + host + ":" + port;
socket.connect(new InetSocketAddress(proxyhost, proxyPort));
String hostport = "CONNECT " + inetAddress.getCanonicalHostName() + ":" + port;
String proxyLine;
String username = proxy.getProxyUsername();
if (username == null)
@ -164,8 +132,6 @@ class HTTPProxySocketFactory
{
throw new ProxyException(ProxyInfo.ProxyType.HTTP);
}
return socket;
}
private static final Pattern RESPONSE_PATTERN

View File

@ -16,8 +16,6 @@
*/
package org.jivesoftware.smack.proxy;
import javax.net.SocketFactory;
/**
* Class which stores proxy information such as proxy type, host, port,
* authentication etc.
@ -29,7 +27,6 @@ public class ProxyInfo
{
public static enum ProxyType
{
NONE,
HTTP,
SOCKS4,
SOCKS5
@ -40,6 +37,7 @@ public class ProxyInfo
private String proxyUsername;
private String proxyPassword;
private ProxyType proxyType;
private final ProxySocketConnection proxySocketConnection;
public ProxyInfo( ProxyType pType, String pHost, int pPort, String pUser,
String pPass)
@ -49,6 +47,19 @@ public class ProxyInfo
this.proxyPort = pPort;
this.proxyUsername = pUser;
this.proxyPassword = pPass;
switch (proxyType) {
case HTTP:
proxySocketConnection = new HTTPProxySocketConnection(this);
break;
case SOCKS4:
proxySocketConnection = new Socks4ProxySocketConnection(this);
break;
case SOCKS5:
proxySocketConnection = new Socks5ProxySocketConnection(this);
break;
default:
throw new IllegalStateException();
}
}
public static ProxyInfo forHttpProxy(String pHost, int pPort, String pUser,
@ -69,16 +80,6 @@ public class ProxyInfo
return new ProxyInfo(ProxyType.SOCKS5, pHost, pPort, pUser, pPass);
}
public static ProxyInfo forNoProxy()
{
return new ProxyInfo(ProxyType.NONE, null, 0, null, null);
}
public static ProxyInfo forDefaultProxy()
{
return new ProxyInfo(ProxyType.NONE, null, 0, null, null);
}
public ProxyType getProxyType()
{
return proxyType;
@ -104,27 +105,7 @@ public class ProxyInfo
return proxyPassword;
}
public SocketFactory getSocketFactory()
{
if(proxyType == ProxyType.NONE)
{
return new DirectSocketFactory();
}
else if(proxyType == ProxyType.HTTP)
{
return new HTTPProxySocketFactory(this);
}
else if(proxyType == ProxyType.SOCKS4)
{
return new Socks4ProxySocketFactory(this);
}
else if(proxyType == ProxyType.SOCKS5)
{
return new Socks5ProxySocketFactory(this);
}
else
{
return null;
}
public ProxySocketConnection getProxySocketConnection() {
return proxySocketConnection;
}
}

View File

@ -0,0 +1,28 @@
/**
*
* Copyright 2015 Florian Schmaus.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.proxy;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
public interface ProxySocketConnection {
public void connect(Socket socket, InetAddress inetAddress, int port, int timeout)
throws IOException;
}

View File

@ -20,59 +20,25 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.net.SocketFactory;
/**
* Socket factory for socks4 proxy.
*
* @author Atul Aggarwal
*/
public class Socks4ProxySocketFactory
extends SocketFactory
{
private ProxyInfo proxy;
public class Socks4ProxySocketConnection implements ProxySocketConnection {
private final ProxyInfo proxy;
public Socks4ProxySocketFactory(ProxyInfo proxy)
Socks4ProxySocketConnection(ProxyInfo proxy)
{
this.proxy = proxy;
}
public Socket createSocket(String host, int port)
throws IOException, UnknownHostException
{
return socks4ProxifiedSocket(host,port);
}
public Socket createSocket(String host ,int port, InetAddress localHost,
int localPort)
throws IOException, UnknownHostException
{
return socks4ProxifiedSocket(host,port);
}
public Socket createSocket(InetAddress host, int port)
throws IOException
{
return socks4ProxifiedSocket(host.getHostAddress(),port);
}
public Socket createSocket( InetAddress address, int port,
InetAddress localAddress, int localPort)
throws IOException
{
return socks4ProxifiedSocket(address.getHostAddress(),port);
}
@SuppressWarnings("resource")
private Socket socks4ProxifiedSocket(String host, int port)
throws IOException
{
Socket socket = null;
@Override
public void connect(Socket socket, InetAddress inetAddress, int port, int timeout)
throws IOException {
InputStream in = null;
OutputStream out = null;
String proxy_host = proxy.getProxyAddress();
@ -81,7 +47,7 @@ public class Socks4ProxySocketFactory
try
{
socket=new Socket(proxy_host, proxy_port);
socket.connect(new InetSocketAddress(proxy_host, proxy_port), timeout);
in=socket.getInputStream();
out=socket.getOutputStream();
socket.setTcpNoDelay(true);
@ -114,19 +80,10 @@ public class Socks4ProxySocketFactory
buf[index++]=(byte)(port>>>8);
buf[index++]=(byte)(port&0xff);
try
byte[] byteAddress = inetAddress.getAddress();
for (int i = 0; i < byteAddress.length; i++)
{
InetAddress addr=InetAddress.getByName(host);
byte[] byteAddress = addr.getAddress();
for (int i = 0; i < byteAddress.length; i++)
{
buf[index++]=byteAddress[i];
}
}
catch(UnknownHostException uhe)
{
throw new ProxyException(ProxyInfo.ProxyType.SOCKS4,
uhe.toString(), uhe);
buf[index++]=byteAddress[i];
}
if(user!=null)
@ -195,7 +152,6 @@ public class Socks4ProxySocketFactory
}
byte[] temp = new byte[2];
in.read(temp, 0, 2);
return socket;
}
catch(RuntimeException e)
{
@ -205,7 +161,7 @@ public class Socks4ProxySocketFactory
{
try
{
if(socket!=null)socket.close();
socket.close();
}
catch(Exception eee)
{
@ -213,4 +169,5 @@ public class Socks4ProxySocketFactory
throw new ProxyException(ProxyInfo.ProxyType.SOCKS4, e.toString());
}
}
}

View File

@ -20,61 +20,25 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.net.SocketFactory;
/**
* Socket factory for Socks5 proxy.
*
* @author Atul Aggarwal
*/
public class Socks5ProxySocketFactory
extends SocketFactory
{
private ProxyInfo proxy;
public class Socks5ProxySocketConnection implements ProxySocketConnection {
private final ProxyInfo proxy;
public Socks5ProxySocketFactory(ProxyInfo proxy)
Socks5ProxySocketConnection(ProxyInfo proxy)
{
this.proxy = proxy;
}
public Socket createSocket(String host, int port)
throws IOException, UnknownHostException
{
return socks5ProxifiedSocket(host,port);
}
public Socket createSocket(String host ,int port, InetAddress localHost,
int localPort)
throws IOException, UnknownHostException
{
return socks5ProxifiedSocket(host,port);
}
public Socket createSocket(InetAddress host, int port)
throws IOException
{
return socks5ProxifiedSocket(host.getHostAddress(),port);
}
public Socket createSocket( InetAddress address, int port,
InetAddress localAddress, int localPort)
throws IOException
{
return socks5ProxifiedSocket(address.getHostAddress(),port);
}
private Socket socks5ProxifiedSocket(String host, int port)
throws IOException
{
Socket socket = null;
@Override
public void connect(Socket socket, InetAddress inetAddress, int port, int timeout)
throws IOException {
InputStream in = null;
OutputStream out = null;
String proxy_host = proxy.getProxyAddress();
@ -84,7 +48,7 @@ public class Socks5ProxySocketFactory
try
{
socket=new Socket(proxy_host, proxy_port);
socket.connect(new InetSocketAddress(proxy_host, proxy_port), timeout);
in=socket.getInputStream();
out=socket.getOutputStream();
@ -247,7 +211,7 @@ public class Socks5ProxySocketFactory
buf[index++]=1; // CONNECT
buf[index++]=0;
byte[] hostb=host.getBytes();
byte[] hostb= inetAddress.getCanonicalHostName().getBytes();
int len=hostb.length;
buf[index++]=3; // DOMAINNAME
buf[index++]=(byte)(len);
@ -327,8 +291,6 @@ public class Socks5ProxySocketFactory
break;
default:
}
return socket;
}
catch(RuntimeException e)
{
@ -338,10 +300,7 @@ public class Socks5ProxySocketFactory
{
try
{
if(socket!=null)
{
socket.close();
}
socket.close();
}
catch(Exception eee)
{
@ -371,4 +330,5 @@ public class Socks5ProxySocketFactory
s+=i;
}
}
}

View File

@ -68,6 +68,7 @@ import org.jivesoftware.smack.sm.predicates.Predicate;
import org.jivesoftware.smack.sm.provider.ParseStreamManagement;
import org.jivesoftware.smack.packet.Nonza;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.proxy.ProxyInfo;
import org.jivesoftware.smack.util.ArrayBlockingQueueWithShutdown;
import org.jivesoftware.smack.util.Async;
import org.jivesoftware.smack.util.PacketParserUtils;
@ -531,6 +532,8 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
private void connectUsingConfiguration() throws IOException, ConnectionException {
List<HostAddress> failedAddresses = populateHostAddresses();
SocketFactory socketFactory = config.getSocketFactory();
ProxyInfo proxyInfo = config.getProxyInfo();
int timeout = config.getConnectTimeout();
if (socketFactory == null) {
socketFactory = SocketFactory.getDefault();
}
@ -550,7 +553,12 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
final String inetAddressAndPort = inetAddress + " at port " + port;
LOGGER.finer("Trying to establish TCP connection to " + inetAddressAndPort);
try {
socket.connect(new InetSocketAddress(inetAddress, port), config.getConnectTimeout());
if (proxyInfo == null) {
socket.connect(new InetSocketAddress(inetAddress, port), timeout);
}
else {
proxyInfo.getProxySocketConnection().connect(socket, inetAddress, port, timeout);
}
} catch (Exception e) {
if (inetAddresses.hasNext()) {
continue innerloop;