Skip to content
Draft
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
29 changes: 27 additions & 2 deletions quickfixj-core/src/main/java/quickfix/mina/ProtocolFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


import org.apache.mina.core.service.IoAcceptor;
Expand Down Expand Up @@ -145,22 +150,42 @@ private static ProxyRequest createHttpProxyRequest(InetSocketAddress address,
String proxyPassword,
String proxyDomain,
String proxyWorkstation) {
HttpProxyRequest req = new HttpProxyRequest(address);

HashMap<String, String> 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(address);
req.setProperties(props);

if (proxyVersion != null && proxyVersion.equalsIgnoreCase("1.1")) {
req.setHttpVersion(HttpProxyConstants.HTTP_1_1);
} else {
req.setHttpVersion(HttpProxyConstants.HTTP_1_0);
}

// Set Proxy-Authorization header if credentials are provided
// Some proxy servers require this header to be set upfront rather than
// waiting for a 407 response
if (proxyUser != null && proxyPassword != null) {
Map<String, List<String>> headers = new HashMap<>();

// Use NTLM authentication if domain and workstation are provided
if (proxyDomain != null && proxyWorkstation != null) {
headers.put("Proxy-Authorization", Collections.singletonList("NTLM"));
} else {
// Use Basic authentication
String credentials = proxyUser + ":" + proxyPassword;
String encodedCredentials = Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.UTF_8));
headers.put("Proxy-Authorization", Collections.singletonList("Basic " + encodedCredentials));
}

req.setHeaders(headers);
}

return req;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,22 @@

import org.apache.mina.core.service.IoConnector;
import org.apache.mina.proxy.ProxyConnector;
import org.apache.mina.proxy.handlers.http.HttpProxyRequest;
import org.apache.mina.proxy.session.ProxyIoSession;
import org.apache.mina.transport.socket.SocketConnector;
import org.apache.mina.util.AvailablePortFinder;
import org.junit.Test;
import quickfix.ConfigError;

import java.net.InetSocketAddress;
import java.util.Base64;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

public class ProtocolFactoryTest {

Expand All @@ -46,4 +53,78 @@ public void shouldCreateProxyConnectorWithoutPreferredAuthOrder() throws ConfigE
ProxyIoSession proxySession = proxyConnector.getProxyIoSession();
assertNull(proxySession.getPreferedOrder());
}

@Test
public void shouldSetBasicAuthorizationHeaderForHttpProxy() throws ConfigError {
InetSocketAddress address = new InetSocketAddress(AvailablePortFinder.getNextAvailable());
InetSocketAddress proxyAddress = new InetSocketAddress(AvailablePortFinder.getNextAvailable());

IoConnector connector = ProtocolFactory.createIoConnector(address);
ProxyConnector proxyConnector = ProtocolFactory
.createIoProxyConnector((SocketConnector) connector, address, proxyAddress, "http", "1.0", "testuser",
"testpassword", null, null);

ProxyIoSession proxySession = proxyConnector.getProxyIoSession();
HttpProxyRequest request = (HttpProxyRequest) proxySession.getRequest();

Map<String, List<String>> headers = request.getHeaders();
assertNotNull("Headers should not be null", headers);
assertTrue("Headers should contain Proxy-Authorization", headers.containsKey("Proxy-Authorization"));

List<String> authHeaders = headers.get("Proxy-Authorization");
assertNotNull("Proxy-Authorization header should not be null", authHeaders);
assertEquals("Should have exactly one Proxy-Authorization header", 1, authHeaders.size());

String authHeader = authHeaders.get(0);
assertTrue("Auth header should start with 'Basic '", authHeader.startsWith("Basic "));

// Verify the encoded credentials
String encodedPart = authHeader.substring("Basic ".length());
String decoded = new String(Base64.getDecoder().decode(encodedPart));
assertEquals("Decoded credentials should match", "testuser:testpassword", decoded);
}

@Test
public void shouldSetNTLMAuthorizationHeaderForHttpProxyWithDomain() throws ConfigError {
InetSocketAddress address = new InetSocketAddress(AvailablePortFinder.getNextAvailable());
InetSocketAddress proxyAddress = new InetSocketAddress(AvailablePortFinder.getNextAvailable());

IoConnector connector = ProtocolFactory.createIoConnector(address);
ProxyConnector proxyConnector = ProtocolFactory
.createIoProxyConnector((SocketConnector) connector, address, proxyAddress, "http", "1.0", "testuser",
"testpassword", "TESTDOMAIN", "TESTWORKSTATION");

ProxyIoSession proxySession = proxyConnector.getProxyIoSession();
HttpProxyRequest request = (HttpProxyRequest) proxySession.getRequest();

Map<String, List<String>> headers = request.getHeaders();
assertNotNull("Headers should not be null", headers);
assertTrue("Headers should contain Proxy-Authorization", headers.containsKey("Proxy-Authorization"));

List<String> authHeaders = headers.get("Proxy-Authorization");
assertNotNull("Proxy-Authorization header should not be null", authHeaders);
assertEquals("Should have exactly one Proxy-Authorization header", 1, authHeaders.size());

String authHeader = authHeaders.get(0);
assertEquals("Auth header should be 'NTLM'", "NTLM", authHeader);
}

@Test
public void shouldNotSetAuthorizationHeaderWhenCredentialsNotProvided() throws ConfigError {
InetSocketAddress address = new InetSocketAddress(AvailablePortFinder.getNextAvailable());
InetSocketAddress proxyAddress = new InetSocketAddress(AvailablePortFinder.getNextAvailable());

IoConnector connector = ProtocolFactory.createIoConnector(address);
ProxyConnector proxyConnector = ProtocolFactory
.createIoProxyConnector((SocketConnector) connector, address, proxyAddress, "http", "1.0", null,
null, null, null);

ProxyIoSession proxySession = proxyConnector.getProxyIoSession();
HttpProxyRequest request = (HttpProxyRequest) proxySession.getRequest();

Map<String, List<String>> headers = request.getHeaders();
// Headers should either be null or not contain Proxy-Authorization
assertTrue("Headers should be null or empty when no credentials provided",
headers == null || !headers.containsKey("Proxy-Authorization"));
}
}