diff --git a/quickfixj-core/src/main/doc/usermanual/usage/configuration.html b/quickfixj-core/src/main/doc/usermanual/usage/configuration.html
index e735cf3ac2..bfc1592391 100644
--- a/quickfixj-core/src/main/doc/usermanual/usage/configuration.html
+++ b/quickfixj-core/src/main/doc/usermanual/usage/configuration.html
@@ -626,6 +626,58 @@
QuickFIX Settings
Java default cipher suites |
+
+ Socks Proxy Options (Initiator only) |
+
+
+ ProxyType |
+ Proxy type |
+ http socks |
+ |
+
+
+ ProxyVersion |
+ Proxy HTTP or Socks version to use |
+ For socks: 4, 4a or 5 For http: 1.0 or 1.1 |
+ For socks: For http: 1.0 |
+
+
+ ProxyHost |
+ Proxy server hostname or IP |
+ valid IP address in the format of x.x.x.x or a domain name |
+ |
+
+
+ ProxyPort |
+ Proxy server port |
+ positive integer |
+ |
+
+
+ ProxyUser |
+ Proxy user |
+ |
+ |
+
+
+ ProxyPassword |
+ Proxy password |
+ |
+ |
+
+
+ ProxyDomain |
+ Proxy domain (For http proxy) |
+ |
+ |
+
+
+ ProxyWorkstation |
+ Proxy workstation (For http proxy) |
+ |
+ |
+
+
Socket Options (Acceptor or Initiator) |
diff --git a/quickfixj-core/src/main/java/quickfix/Initiator.java b/quickfixj-core/src/main/java/quickfix/Initiator.java
index 7dcab3020d..ffb39bf631 100644
--- a/quickfixj-core/src/main/java/quickfix/Initiator.java
+++ b/quickfixj-core/src/main/java/quickfix/Initiator.java
@@ -68,4 +68,52 @@ public interface Initiator extends Connector {
* @see quickfix.SessionFactory#SETTING_CONNECTION_TYPE
*/
String SETTING_SOCKET_LOCAL_PORT = "SocketLocalPort";
+
+ /**
+ * Initiator setting for proxy type. Only valid when session connection
+ * type is "initiator".
+ */
+ String SETTING_PROXY_TYPE = "ProxyType";
+
+ /**
+ * Initiator setting for proxy version. Only valid when session connection
+ * type is "initiator". - http 1.0 / 1.1
+ */
+ String SETTING_PROXY_VERSION = "ProxyVersion";
+
+ /**
+ * Initiator setting for proxy host. Only valid when session connection
+ * type is "initiator".
+ */
+ String SETTING_PROXY_HOST = "ProxyHost";
+
+ /**
+ * Initiator setting for proxy port. Only valid when session connection
+ * type is "initiator".
+ */
+ String SETTING_PROXY_PORT = "ProxyPort";
+
+ /**
+ * Initiator setting for proxy port. Only valid when session connection
+ * type is "initiator".
+ */
+ String SETTING_PROXY_USER = "ProxyUser";
+
+ /**
+ * Initiator setting for proxy port. Only valid when session connection
+ * type is "initiator".
+ */
+ String SETTING_PROXY_PASSWORD = "ProxyPassword";
+
+ /**
+ * Initiator setting for proxy domain. Only valid when session connection
+ * type is "initiator".
+ */
+ String SETTING_PROXY_DOMAIN = "ProxyDomain";
+
+ /**
+ * Initiator setting for proxy workstation. Only valid when session connection
+ * type is "initiator".
+ */
+ String SETTING_PROXY_WORKSTATION = "ProxyWorkstation";
}
diff --git a/quickfixj-core/src/main/java/quickfix/mina/ProtocolFactory.java b/quickfixj-core/src/main/java/quickfix/mina/ProtocolFactory.java
index 640247b74e..2046315577 100644
--- a/quickfixj-core/src/main/java/quickfix/mina/ProtocolFactory.java
+++ b/quickfixj-core/src/main/java/quickfix/mina/ProtocolFactory.java
@@ -21,14 +21,28 @@
import java.net.InetSocketAddress;
import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoConnector;
+import org.apache.mina.transport.socket.SocketConnector;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.apache.mina.transport.vmpipe.VmPipeAcceptor;
import org.apache.mina.transport.vmpipe.VmPipeAddress;
import org.apache.mina.transport.vmpipe.VmPipeConnector;
+import org.apache.mina.proxy.ProxyConnector;
+import org.apache.mina.proxy.handlers.ProxyRequest;
+import org.apache.mina.proxy.handlers.http.HttpAuthenticationMethods;
+import org.apache.mina.proxy.handlers.http.HttpProxyConstants;
+import org.apache.mina.proxy.handlers.http.HttpProxyRequest;
+import org.apache.mina.proxy.handlers.socks.SocksProxyConstants;
+import org.apache.mina.proxy.handlers.socks.SocksProxyRequest;
+import org.apache.mina.proxy.session.ProxyIoSession;
+
import quickfix.ConfigError;
import quickfix.RuntimeError;
@@ -58,7 +72,7 @@ public static String getTypeString(int type) {
public static SocketAddress createSocketAddress(int transportType, String host,
int port) throws ConfigError {
- if (transportType == SOCKET) {
+ if (transportType == SOCKET || transportType == PROXY) {
return host != null ? new InetSocketAddress(host, port) : new InetSocketAddress(port);
} else if (transportType == VM_PIPE) {
return new VmPipeAddress(port);
@@ -102,6 +116,109 @@ public static IoAcceptor createIoAcceptor(int transportType) {
}
}
+ public static ProxyConnector createIoProxyConnector(SocketConnector socketConnector,
+ InetSocketAddress address,
+ InetSocketAddress proxyAddress,
+ String proxyType,
+ String proxyVersion,
+ String proxyUser,
+ String proxyPassword,
+ String proxyDomain,
+ String proxyWorkstation ) throws ConfigError {
+
+ // Create proxy connector.
+ ProxyRequest req;
+
+ ProxyConnector connector = new ProxyConnector(socketConnector);
+ connector.setConnectTimeoutMillis(5000);
+
+ if (proxyType.equalsIgnoreCase("http")) {
+ req = createHttpProxyRequest(address, proxyVersion, proxyUser, proxyPassword, proxyDomain, proxyWorkstation);
+ } else if (proxyType.equalsIgnoreCase("socks")) {
+ req = createSocksProxyRequest(address, proxyVersion, proxyUser, proxyPassword);
+ } else {
+ throw new ConfigError("Proxy type must be http or socks");
+ }
+
+ ProxyIoSession proxyIoSession = new ProxyIoSession(proxyAddress, req);
+
+ List l = new ArrayList<>();
+ l.add(HttpAuthenticationMethods.NO_AUTH);
+ l.add(HttpAuthenticationMethods.DIGEST);
+ l.add(HttpAuthenticationMethods.BASIC);
+
+ proxyIoSession.setPreferedOrder(l);
+ connector.setProxyIoSession(proxyIoSession);
+
+ return connector;
+ }
+
+
+ private static ProxyRequest createHttpProxyRequest(InetSocketAddress address,
+ String proxyVersion,
+ String proxyUser,
+ String proxyPassword,
+ String proxyDomain,
+ String proxyWorkstation) {
+ String uri = "http://" + address.getAddress().getHostAddress() + ":" + address.getPort();
+ HashMap props = new HashMap<>();
+ props.put(HttpProxyConstants.USER_PROPERTY, proxyUser);
+ props.put(HttpProxyConstants.PWD_PROPERTY, proxyPassword);
+ if (proxyDomain != null && proxyWorkstation != null) {
+ props.put(HttpProxyConstants.DOMAIN_PROPERTY, proxyDomain);
+ props.put(HttpProxyConstants.WORKSTATION_PROPERTY, proxyWorkstation);
+ }
+
+ HttpProxyRequest req = new HttpProxyRequest(uri);
+ req.setProperties(props);
+ if (proxyVersion != null && proxyVersion.equalsIgnoreCase("1.1")) {
+ req.setHttpVersion(HttpProxyConstants.HTTP_1_1);
+ } else {
+ req.setHttpVersion(HttpProxyConstants.HTTP_1_0);
+ }
+
+ return req;
+ }
+
+
+ private static ProxyRequest createSocksProxyRequest(InetSocketAddress address,
+ String proxyVersion,
+ String proxyUser,
+ String proxyPassword) throws ConfigError {
+ SocksProxyRequest req;
+ if (proxyVersion.equalsIgnoreCase("4")) {
+ req = new SocksProxyRequest(
+ SocksProxyConstants.SOCKS_VERSION_4,
+ SocksProxyConstants.ESTABLISH_TCPIP_STREAM,
+ address,
+ proxyUser);
+
+ } else if (proxyVersion.equalsIgnoreCase("4a")) {
+ req = new SocksProxyRequest(
+ SocksProxyConstants.ESTABLISH_TCPIP_STREAM,
+ address.getAddress().getHostAddress(),
+ address.getPort(),
+ proxyUser);
+
+ } else if (proxyVersion.equalsIgnoreCase("5")) {
+ req = new SocksProxyRequest(
+ SocksProxyConstants.SOCKS_VERSION_5,
+ SocksProxyConstants.ESTABLISH_TCPIP_STREAM,
+ address,
+ proxyUser);
+
+ } else {
+ throw new ConfigError("SOCKS ProxyType must be 4,4a or 5");
+ }
+
+ if (proxyPassword != null) {
+ req.setPassword(proxyPassword);
+ }
+
+ return req;
+ }
+
+
public static IoConnector createIoConnector(SocketAddress address) throws ConfigError {
if (address instanceof InetSocketAddress) {
return new NioSocketConnector();
diff --git a/quickfixj-core/src/main/java/quickfix/mina/initiator/AbstractSocketInitiator.java b/quickfixj-core/src/main/java/quickfix/mina/initiator/AbstractSocketInitiator.java
index 10e02a5794..deb49a88d3 100644
--- a/quickfixj-core/src/main/java/quickfix/mina/initiator/AbstractSocketInitiator.java
+++ b/quickfixj-core/src/main/java/quickfix/mina/initiator/AbstractSocketInitiator.java
@@ -104,10 +104,46 @@ protected void createSessionInitiators()
sslConfig = SSLSupport.getSslConfig(getSettings(), sessionID);
}
+ String proxyUser = null;
+ String proxyPassword = null;
+ String proxyHost = null;
+
+ String proxyType = null;
+ String proxyVersion = null;
+
+ String proxyWorkstation = null;
+ String proxyDomain = null;
+
+ int proxyPort = -1;
+
+ if (getSettings().isSetting(sessionID, Initiator.SETTING_PROXY_TYPE)) {
+ proxyType = settings.getString(sessionID, Initiator.SETTING_PROXY_TYPE);
+ if (getSettings().isSetting(sessionID, Initiator.SETTING_PROXY_VERSION)) {
+ proxyVersion = settings.getString(sessionID,
+ Initiator.SETTING_PROXY_VERSION);
+ }
+
+ if (getSettings().isSetting(sessionID, Initiator.SETTING_PROXY_USER)) {
+ proxyUser = settings.getString(sessionID, Initiator.SETTING_PROXY_USER);
+ proxyPassword = settings.getString(sessionID,
+ Initiator.SETTING_PROXY_PASSWORD);
+ }
+ if (getSettings().isSetting(sessionID, Initiator.SETTING_PROXY_WORKSTATION)
+ && getSettings().isSetting(sessionID, Initiator.SETTING_PROXY_DOMAIN)) {
+ proxyWorkstation = settings.getString(sessionID,
+ Initiator.SETTING_PROXY_WORKSTATION);
+ proxyDomain = settings.getString(sessionID, Initiator.SETTING_PROXY_DOMAIN);
+ }
+
+ proxyHost = settings.getString(sessionID, Initiator.SETTING_PROXY_HOST);
+ proxyPort = (int) settings.getLong(sessionID, Initiator.SETTING_PROXY_PORT);
+ }
+
final IoSessionInitiator ioSessionInitiator = new IoSessionInitiator(session,
- socketAddresses, localAddress, reconnectingIntervals, getScheduledExecutorService(),
- networkingOptions, getEventHandlingStrategy(), getIoFilterChainBuilder(),
- sslEnabled, sslConfig);
+ socketAddresses, localAddress, reconnectingIntervals,
+ getScheduledExecutorService(), networkingOptions,
+ getEventHandlingStrategy(), getIoFilterChainBuilder(), sslEnabled, sslConfig,
+ proxyType, proxyVersion, proxyHost, proxyPort, proxyUser, proxyPassword, proxyDomain, proxyWorkstation);
initiators.add(ioSessionInitiator);
}
diff --git a/quickfixj-core/src/main/java/quickfix/mina/initiator/InitiatorProxyIoHandler.java b/quickfixj-core/src/main/java/quickfix/mina/initiator/InitiatorProxyIoHandler.java
new file mode 100644
index 0000000000..49bbdf620f
--- /dev/null
+++ b/quickfixj-core/src/main/java/quickfix/mina/initiator/InitiatorProxyIoHandler.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) quickfixengine.org All rights reserved.
+ *
+ * This file is part of the QuickFIX FIX Engine
+ *
+ * This file may be distributed under the terms of the quickfixengine.org
+ * license as defined by quickfixengine.org and appearing in the file
+ * LICENSE included in the packaging of this file.
+ *
+ * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+ * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE.
+ *
+ * See http://www.quickfixengine.org/LICENSE for licensing information.
+ *
+ * Contact ask@quickfixengine.org if any conditions of this licensing
+ * are not clear to you.
+ ******************************************************************************/
+
+package quickfix.mina.initiator;
+
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.proxy.AbstractProxyIoHandler;
+
+import quickfix.mina.ssl.SSLFilter;
+
+class InitiatorProxyIoHandler extends AbstractProxyIoHandler {
+ private final InitiatorIoHandler initiatorIoHandler;
+ private final SSLFilter sslFilter;
+
+ InitiatorProxyIoHandler(InitiatorIoHandler initiatorIoHandler, SSLFilter sslFilter) {
+ super();
+ this.initiatorIoHandler = initiatorIoHandler;
+ this.sslFilter = sslFilter;
+ }
+
+ @Override
+ public void sessionCreated(IoSession session) throws Exception {
+ this.initiatorIoHandler.sessionCreated(session);
+ }
+
+ @Override
+ public void sessionClosed(IoSession ioSession) throws Exception {
+ this.initiatorIoHandler.sessionClosed(ioSession);
+ }
+
+ @Override
+ public void messageReceived(IoSession session, Object message) throws Exception {
+ this.initiatorIoHandler.messageReceived(session, message);
+ }
+
+ @Override
+ public void messageSent(IoSession session, Object message) throws Exception {
+ this.initiatorIoHandler.messageSent(session, message);
+ }
+
+ @Override
+ public void exceptionCaught(IoSession ioSession, Throwable cause) throws Exception {
+ this.initiatorIoHandler.exceptionCaught(ioSession, cause);
+ }
+
+ @Override
+ public void proxySessionOpened(IoSession ioSession) throws Exception {
+ if (this.sslFilter != null) {
+ this.sslFilter.initiateHandshake(ioSession);
+ }
+ }
+}
diff --git a/quickfixj-core/src/main/java/quickfix/mina/initiator/IoSessionInitiator.java b/quickfixj-core/src/main/java/quickfix/mina/initiator/IoSessionInitiator.java
index bf02ed9634..71f1a8bf06 100644
--- a/quickfixj-core/src/main/java/quickfix/mina/initiator/IoSessionInitiator.java
+++ b/quickfixj-core/src/main/java/quickfix/mina/initiator/IoSessionInitiator.java
@@ -24,6 +24,9 @@
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.apache.mina.filter.logging.LoggingFilter;
+import org.apache.mina.proxy.ProxyConnector;
+import org.apache.mina.transport.socket.SocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import quickfix.ConfigError;
@@ -58,22 +61,27 @@ public class IoSessionInitiator {
private Future> reconnectFuture;
protected final static Logger log = LoggerFactory.getLogger("display." + IoSessionInitiator.class.getName());
- public IoSessionInitiator(Session fixSession, SocketAddress[] socketAddresses, SocketAddress localAddress,
- int[] reconnectIntervalInSeconds, ScheduledExecutorService executor,
- NetworkingOptions networkingOptions, EventHandlingStrategy eventHandlingStrategy,
- IoFilterChainBuilder userIoFilterChainBuilder, boolean sslEnabled, SSLConfig sslConfig) throws ConfigError {
+ public IoSessionInitiator(Session fixSession, SocketAddress[] socketAddresses,
+ SocketAddress localAddress, int[] reconnectIntervalInSeconds,
+ ScheduledExecutorService executor, NetworkingOptions networkingOptions,
+ EventHandlingStrategy eventHandlingStrategy,
+ IoFilterChainBuilder userIoFilterChainBuilder, boolean sslEnabled, SSLConfig sslConfig,
+ String proxyType, String proxyVersion, String proxyHost, int proxyPort,
+ String proxyUser, String proxyPassword, String proxyDomain, String proxyWorkstation) throws ConfigError {
this.executor = executor;
final long[] reconnectIntervalInMillis = new long[reconnectIntervalInSeconds.length];
for (int ii = 0; ii != reconnectIntervalInSeconds.length; ++ii) {
reconnectIntervalInMillis[ii] = reconnectIntervalInSeconds[ii] * 1000L;
}
try {
- reconnectTask = new ConnectTask(sslEnabled, socketAddresses, localAddress, userIoFilterChainBuilder,
- fixSession, reconnectIntervalInMillis, networkingOptions,
- eventHandlingStrategy, sslConfig);
+ reconnectTask = new ConnectTask(sslEnabled, socketAddresses, localAddress,
+ userIoFilterChainBuilder, fixSession, reconnectIntervalInMillis,
+ networkingOptions, eventHandlingStrategy, sslConfig,
+ proxyType, proxyVersion, proxyHost, proxyPort, proxyUser, proxyPassword, proxyDomain, proxyWorkstation);
} catch (GeneralSecurityException e) {
throw new ConfigError(e);
}
+
log.info("[" + fixSession.getSessionID() + "] " + Arrays.asList(socketAddresses));
}
@@ -96,10 +104,22 @@ private static class ConnectTask implements Runnable {
private int connectionFailureCount;
private ConnectFuture connectFuture;
+ private final String proxyType;
+ private final String proxyVersion;
+ private final String proxyHost;
+ private final int proxyPort;
+ private final String proxyUser;
+ private final String proxyPassword;
+ private final String proxyDomain;
+ private final String proxyWorkstation;
+
public ConnectTask(boolean sslEnabled, SocketAddress[] socketAddresses,
- SocketAddress localAddress, IoFilterChainBuilder userIoFilterChainBuilder, Session fixSession,
- long[] reconnectIntervalInMillis, NetworkingOptions networkingOptions,
- EventHandlingStrategy eventHandlingStrategy, SSLConfig sslConfig) throws ConfigError, GeneralSecurityException {
+ SocketAddress localAddress, IoFilterChainBuilder userIoFilterChainBuilder,
+ Session fixSession, long[] reconnectIntervalInMillis,
+ NetworkingOptions networkingOptions, EventHandlingStrategy eventHandlingStrategy, SSLConfig sslConfig,
+ String proxyType, String proxyVersion, String proxyHost,
+ int proxyPort, String proxyUser, String proxyPassword, String proxyDomain,
+ String proxyWorkstation) throws ConfigError, GeneralSecurityException {
this.sslEnabled = sslEnabled;
this.socketAddresses = socketAddresses;
this.localAddress = localAddress;
@@ -109,37 +129,69 @@ public ConnectTask(boolean sslEnabled, SocketAddress[] socketAddresses,
this.networkingOptions = networkingOptions;
this.eventHandlingStrategy = eventHandlingStrategy;
this.sslConfig = sslConfig;
+
+ this.proxyType = proxyType;
+ this.proxyVersion = proxyVersion;
+ this.proxyHost = proxyHost;
+ this.proxyPort = proxyPort;
+ this.proxyUser = proxyUser;
+ this.proxyPassword = proxyPassword;
+ this.proxyDomain = proxyDomain;
+ this.proxyWorkstation = proxyWorkstation;
+
setupIoConnector();
}
private void setupIoConnector() throws ConfigError, GeneralSecurityException {
- final IoConnector newConnector = ProtocolFactory.createIoConnector(socketAddresses[0]);
final CompositeIoFilterChainBuilder ioFilterChainBuilder = new CompositeIoFilterChainBuilder(userIoFilterChainBuilder);
+ boolean hasProxy = proxyType != null && proxyPort > 0 && socketAddresses[0] instanceof InetSocketAddress;
+
+ SSLFilter sslFilter = null;
if (sslEnabled) {
- installSslFilter(ioFilterChainBuilder);
+ sslFilter = installSslFilter(ioFilterChainBuilder, !hasProxy);
}
ioFilterChainBuilder.addLast(FIXProtocolCodecFactory.FILTER_NAME, new ProtocolCodecFilter(new FIXProtocolCodecFactory()));
- newConnector.setFilterChainBuilder(ioFilterChainBuilder);
+ IoConnector newConnector;
+ newConnector = ProtocolFactory.createIoConnector(socketAddresses[0]);
newConnector.setHandler(new InitiatorIoHandler(fixSession, networkingOptions, eventHandlingStrategy));
+ newConnector.setFilterChainBuilder(ioFilterChainBuilder);
+
+ if (hasProxy) {
+ ProxyConnector proxyConnector = ProtocolFactory.createIoProxyConnector(
+ (SocketConnector) newConnector,
+ (InetSocketAddress) socketAddresses[0],
+ new InetSocketAddress(proxyHost, proxyPort),
+ proxyType, proxyVersion, proxyUser, proxyPassword, proxyDomain, proxyWorkstation
+ );
+
+ proxyConnector.setHandler(new InitiatorProxyIoHandler(
+ new InitiatorIoHandler(fixSession, networkingOptions, eventHandlingStrategy),
+ sslFilter
+ ));
+
+ newConnector = proxyConnector;
+ }
+
if (ioConnector != null) {
ioConnector.dispose();
}
ioConnector = newConnector;
}
- private void installSslFilter(CompositeIoFilterChainBuilder ioFilterChainBuilder)
+ private SSLFilter installSslFilter(CompositeIoFilterChainBuilder ioFilterChainBuilder, boolean autoStart)
throws GeneralSecurityException {
final SSLContext sslContext = SSLContextFactory.getInstance(sslConfig);
- final SSLFilter sslFilter = new SSLFilter(sslContext);
+ final SSLFilter sslFilter = new SSLFilter(sslContext, autoStart);
sslFilter.setUseClientMode(true);
sslFilter.setCipherSuites(sslConfig.getEnabledCipherSuites() != null ? sslConfig.getEnabledCipherSuites()
: SSLSupport.getDefaultCipherSuites(sslContext));
sslFilter.setEnabledProtocols(sslConfig.getEnabledProtocols() != null ? sslConfig.getEnabledProtocols()
: SSLSupport.getSupportedProtocols(sslContext));
ioFilterChainBuilder.addLast(SSLSupport.FILTER_NAME, sslFilter);
+ return sslFilter;
}
public synchronized void run() {