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

Always use configured host on preconnect #14

Merged
merged 3 commits into from
Aug 20, 2020
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -101,7 +101,8 @@ public class DataSenderImpl implements DataSender {

private final HttpClientWrapper httpClientWrapper;

private volatile String host;
private final String originalHost;
private volatile String redirectHost;
private final int port;
private volatile String protocol;
private volatile boolean auditMode;
Expand Down Expand Up @@ -129,7 +130,8 @@ public DataSenderImpl(
this.logger = logger;
this.configService = configService;
logger.info(MessageFormat.format("Setting audit_mode to {0}", auditMode));
host = config.getHost();
originalHost = config.getHost();
redirectHost = config.getHost();
port = config.getPort();

protocol = config.isSSL() ? "https" : "http";
Expand Down Expand Up @@ -187,8 +189,8 @@ Object getAgentRunId() {
public Map<String, Object> connect(Map<String, Object> startupOptions) throws Exception {
String redirectHost = parsePreconnectAndReturnHost();
if (redirectHost != null) {
host = redirectHost;
logger.info(MessageFormat.format("Collector redirection to {0}:{1}", host, Integer.toString(port)));
this.redirectHost = redirectHost;
logger.info(MessageFormat.format("Collector redirection to {0}:{1}", this.redirectHost, Integer.toString(port)));
} else if (configService.getDefaultAgentConfig().laspEnabled()) {
throw new ForceDisconnectException("The agent did not receive one or more security policies that it expected and will shut down."
+ " Please contact support.");
Expand All @@ -207,7 +209,7 @@ private String parsePreconnectAndReturnHost() throws Exception {
}
token.put("high_security", agentConfig.isHighSecurity());
params.add(token);
Object response = invokeNoRunId(CollectorMethods.PRECONNECT, compressedEncoding, params);
Object response = invokeNoRunId(originalHost, CollectorMethods.PRECONNECT, compressedEncoding, params);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

preconnect should always use the original collector host.


if (response != null) {
Map<?, ?> returnValue = (Map<?, ?>) response;
Expand All @@ -232,7 +234,7 @@ private Map<String, Object> doConnect(Map<String, Object> startupOptions) throws
startupOptions.put(ENV_METADATA, metadata);

params.add(startupOptions);
Object response = invokeNoRunId(CollectorMethods.CONNECT, compressedEncoding, params);
Object response = invokeNoRunId(redirectHost, CollectorMethods.CONNECT, compressedEncoding, params);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

connect should always use the redirect host.

if (!(response instanceof Map)) {
throw new UnexpectedException(MessageFormat.format("Expected a map of connection data, got {0}", response));
}
Expand Down Expand Up @@ -487,17 +489,17 @@ void setMaxPayloadSizeInBytes(int payloadSizeInBytes) {

private Object invokeRunId(String method, String encoding, Object runId, JSONStreamAware params) throws Exception {
String uri = MessageFormat.format(agentRunIdUriPattern, method, runId.toString());
return invoke(method, encoding, uri, params);
return invoke(redirectHost, method, encoding, uri, params);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything that takes a run token should use the redirect host.

}

private Object invokeNoRunId(String method, String encoding, JSONStreamAware params) throws Exception {
private Object invokeNoRunId(String host, String method, String encoding, JSONStreamAware params) throws Exception {
String uri = MessageFormat.format(noAgentRunIdUriPattern, method);
return invoke(method, encoding, uri, params);
return invoke(host, method, encoding, uri, params);
}

private Object invoke(String method, String encoding, String uri, JSONStreamAware params) throws Exception {
private Object invoke(String host, String method, String encoding, String uri, JSONStreamAware params) throws Exception {
// ReadResult should be from a valid 2xx response at this point otherwise send method throws an exception here
ReadResult readResult = send(method, encoding, uri, params);
ReadResult readResult = send(host, method, encoding, uri, params);
Map<?, ?> responseMap = null;
String responseBody = readResult.getResponseBody();

Expand Down Expand Up @@ -535,7 +537,7 @@ private Object invoke(String method, String encoding, String uri, JSONStreamAwar
* response code value. The previous behavior of a 200 (“OK”) with an exact string in the body that should be
* matched/parsed has been deprecated.
*/
private ReadResult connectAndSend(String method, String encoding, String uri, JSONStreamAware params) throws Exception {
private ReadResult connectAndSend(String host, String method, String encoding, String uri, JSONStreamAware params) throws Exception {
byte[] data = writeData(encoding, params);

/*
Expand All @@ -554,7 +556,7 @@ private ReadResult connectAndSend(String method, String encoding, String uri, JS
final URL url = new URL(protocol, host, port, uri);
HttpClientWrapper.Request request = createRequest(method, encoding, url, data);

httpClientWrapper.captureSupportabilityMetrics(ServiceFactory.getStatsService(), host);
httpClientWrapper.captureSupportabilityMetrics(ServiceFactory.getStatsService(), this.redirectHost);

ReadResult result = httpClientWrapper.execute(request, new TimingEventHandler(method, ServiceFactory.getStatsService()));

Expand Down Expand Up @@ -636,9 +638,9 @@ private boolean methodShouldBeAudited(String method) {
return true;
}

private ReadResult send(String method, String encoding, String uri, JSONStreamAware params) throws Exception {
private ReadResult send(String host, String method, String encoding, String uri, JSONStreamAware params) throws Exception {
try {
return connectAndSend(method, encoding, uri, params);
return connectAndSend(host, method, encoding, uri, params);
} catch (MalformedURLException e) {
logger.log(Level.SEVERE, "You have requested a connection to New Relic via a protocol which is unavailable in your runtime: {0}", e.toString());
throw new ForceDisconnectException(e.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,35 @@ public void testFullPreconnectConnectCycleNoSecurity() throws Exception {
assertEquals("value", result.get("other"));
}

@Test
public void testReconnectCycleNoSecurity() throws Exception {
MockitoAnnotations.initMocks(this);

final Map<String, Object> settings = new HashMap<>();
settings.put(AgentConfigImpl.APP_NAME, "Unit Test");
settings.put(AgentConfigImpl.HOST, "no-collector.example.com");

final MockServiceManager serviceManager = new MockServiceManager(
ConfigServiceFactory.createConfigServiceUsingSettings(settings)
);
serviceManager.setStatsService(mockStatsService);

HttpClientWrapper wrapper = new ConnectCycleNoSecuritySuccessClientWrapper();

DataSenderImpl target = new DataSenderImpl(serviceManager.getConfigService().getDefaultAgentConfig(), wrapper, null, Agent.LOG,
ServiceFactory.getConfigService());
target.setAgentRunId("agent run id");

Map<String, Object> startupOptions = new HashMap<>();
startupOptions.put("test-sentinel", "test-value");
Map<String, Object> result = target.connect(startupOptions);
assertEquals("my-run-id", target.getAgentRunId());
assertEquals("value", result.get("other"));

// This will assert that preconnect was called with the original host, not the redirect host.
target.connect(startupOptions);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is unclear to me how this would do any assertions around preconnect. Does this test actually check what this comment says it is checking? If so, how does it do that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests use the DataSenderImpl with a customized HttpClientWrapper. HttpClientWrapper is the interface that masks almost all the Apache HTTP client code. On line 80, this test applies the ConnectCycleNoSecuritySuccessClientWrapper wrapper. When a preconnect URL comes through that wrapper, it verifies that the host is no-collector.example.com and sends a redirect host of new_host.example.com.

Without the code change, this code would fail because the second preconnect would use the host new_host.example.com.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thanks for clarifying...because that's pretty complicated for testing. At least it is tested. Thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - probably should have done it with mockito.

}

@Test
public void testFullPreconnectConnectCycleWithLasp() throws Exception {
MockitoAnnotations.initMocks(this);
Expand Down