Skip to content

Commit a18c330

Browse files
authored
Merge branch 'master' into dougqh/atomics-benchmark
2 parents f12e40e + 3c59e03 commit a18c330

35 files changed

Lines changed: 2912 additions & 27 deletions

File tree

communication/src/main/java/datadog/communication/ddagent/DDAgentFeaturesDiscovery.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,14 @@ private void processInfoResponseHeaders(Response response) {
231231
private boolean processInfoResponse(State newState, String response) {
232232
try {
233233
Map<String, Object> map = RESPONSE_ADAPTER.fromJson(response);
234+
final Object endpointObj = map.get("endpoints");
235+
if (!(endpointObj instanceof List)) {
236+
log.debug("Bad response received from the agent. Ignoring it.");
237+
return false;
238+
}
234239
discoverStatsDPort(map);
235240
newState.version = (String) map.get("version");
236-
Set<String> endpoints = new HashSet<>((List<String>) map.get("endpoints"));
241+
Set<String> endpoints = new HashSet<>((List<String>) endpointObj);
237242

238243
String foundMetricsEndpoint = null;
239244
if (metricsEnabled) {

communication/src/test/groovy/datadog/communication/ddagent/DDAgentFeaturesDiscoveryTest.groovy

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,28 @@ class DDAgentFeaturesDiscoveryTest extends DDSpecification {
187187
0 * _
188188
}
189189

190+
def "test fallback when /info empty"() {
191+
setup:
192+
OkHttpClient client = Mock(OkHttpClient)
193+
DDAgentFeaturesDiscovery features = new DDAgentFeaturesDiscovery(client, monitoring, agentUrl, false, true)
194+
195+
when: "/info is empty"
196+
features.discover()
197+
198+
then:
199+
1 * client.newCall({ Request request -> request.url().toString() == "http://localhost:8125/info" }) >> { Request request -> infoResponse(request, "{}") }
200+
0 * client.newCall({ Request request -> request.url().toString() == "http://localhost:8125/v0.6/stats" }) >> { Request request -> clientError(request) }
201+
0 * client.newCall({ Request request -> request.url().toString() == "http://localhost:8125/v0.5/traces" }) >> { Request request -> success(request) }
202+
1 * client.newCall({ Request request -> request.url().toString() == "http://localhost:8125/v0.4/traces" }) >> { Request request -> success(request) }
203+
0 * client.newCall({ Request request -> request.url().toString() == "http://localhost:8125/v0.3/traces" }) >> { Request request -> success(request) }
204+
features.getMetricsEndpoint() == null
205+
!features.supportsMetrics()
206+
features.getTraceEndpoint() == V04_ENDPOINT
207+
!features.supportsLongRunning()
208+
features.state() == PROBE_STATE
209+
0 * _
210+
}
211+
190212
def "test fallback when /info not found"() {
191213
setup:
192214
OkHttpClient client = Mock(OkHttpClient)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
plugins {
2+
`java-library`
3+
`java-test-fixtures`
4+
}
5+
6+
apply(from = "$rootDir/gradle/java.gradle")
7+
8+
description = "HTTP Client API"
9+
10+
val minimumBranchCoverage by extra(0) // extra(0.7) -- need a library implementation
11+
val minimumInstructionCoverage by extra(0) // extra(0.7) -- need a library implementation
12+
13+
// Exclude interfaces for test coverage
14+
val excludedClassesCoverage by extra(
15+
listOf(
16+
"datadog.http.client.HttpClient",
17+
"datadog.http.client.HttpClient.Builder",
18+
"datadog.http.client.HttpRequest",
19+
"datadog.http.client.HttpRequest.Builder",
20+
"datadog.http.client.HttpRequestBody",
21+
"datadog.http.client.HttpRequestBody.MultipartBuilder",
22+
"datadog.http.client.HttpRequestListener",
23+
"datadog.http.client.HttpResponse",
24+
"datadog.http.client.HttpUrl",
25+
"datadog.http.client.HttpUrl.Builder",
26+
)
27+
)
28+
29+
dependencies {
30+
// Add API implementations to test providers
31+
// testRuntimeOnly(project(":components:http:http-lib-jdk"))
32+
// testRuntimeOnly(project(":components:http:http-lib-okhttp"))
33+
// Add MockServer for test fixtures
34+
testFixturesImplementation("org.mock-server:mockserver-junit-jupiter-no-dependencies:5.14.0")
35+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package datadog.http.client;
2+
3+
import java.io.File;
4+
import java.io.IOException;
5+
import java.net.Proxy;
6+
import java.time.Duration;
7+
import java.util.concurrent.CompletableFuture;
8+
import java.util.concurrent.Executor;
9+
import javax.annotation.Nullable;
10+
11+
/**
12+
* This interface is an abstraction for HTTP clients, providing request execution capabilities. This
13+
* abstraction is implementation-agnostic and can be backed by third party libraries of the JDK
14+
* itself.
15+
*
16+
* <p>HttpClient instances should be reused across requests for connection pooling.
17+
*/
18+
public interface HttpClient {
19+
/**
20+
* Executes an HTTP request synchronously and returns the response. The caller is responsible for
21+
* closing the response.
22+
*
23+
* @param request the request to execute
24+
* @return the HTTP response
25+
* @throws IOException if an I/O error occurs
26+
*/
27+
HttpResponse execute(HttpRequest request) throws IOException;
28+
29+
/**
30+
* Executes an HTTP request asynchronously and returns a {@link CompletableFuture}. The caller is
31+
* responsible for closing the response.
32+
*
33+
* @param request the request to execute
34+
* @return a CompletableFuture that completes with the HTTP response
35+
*/
36+
CompletableFuture<HttpResponse> executeAsync(HttpRequest request);
37+
38+
/**
39+
* Creates a new {@link Builder} for constructing HTTP clients.
40+
*
41+
* @return a new http client builder
42+
*/
43+
static Builder newBuilder() {
44+
return HttpProviders.newClientBuilder();
45+
}
46+
47+
/** Builder for constructing {@link HttpClient} instances. */
48+
interface Builder {
49+
/**
50+
* Sets the client timeouts, including the connection.
51+
*
52+
* @param timeout the timeout duration
53+
* @return this builder
54+
*/
55+
Builder connectTimeout(Duration timeout);
56+
57+
/**
58+
* Sets the proxy configuration.
59+
*
60+
* @param proxy the proxy to use
61+
* @return this builder
62+
*/
63+
Builder proxy(Proxy proxy);
64+
65+
/**
66+
* Sets proxy authentication credentials.
67+
*
68+
* @param username the proxy username
69+
* @param password the proxy password, or {@code null} to use an empty password
70+
* @return this builder
71+
*/
72+
Builder proxyAuthenticator(String username, @Nullable String password);
73+
74+
/**
75+
* Configures the client to use a Unix domain socket.
76+
*
77+
* @param socketFile the Unix domain socket file
78+
* @return this builder
79+
*/
80+
Builder unixDomainSocket(File socketFile);
81+
82+
/**
83+
* Configures the client to use a named pipe (Windows).
84+
*
85+
* @param pipeName the named pipe name
86+
* @return this builder
87+
*/
88+
Builder namedPipe(String pipeName);
89+
90+
/**
91+
* Forces clear text (HTTP) connections, disabling TLS.
92+
*
93+
* @param clearText {@code true} to force HTTP, {@code false} to allow HTTPS
94+
* @return this builder
95+
*/
96+
Builder clearText(boolean clearText);
97+
98+
/**
99+
* Sets a custom executor for executing async requests.
100+
*
101+
* @param executor the executor to use for async requests
102+
* @return this builder
103+
*/
104+
Builder executor(Executor executor);
105+
106+
/**
107+
* Builds the {@link HttpClient} with the configured settings.
108+
*
109+
* @return the constructed HttpClient
110+
*/
111+
HttpClient build();
112+
}
113+
}

0 commit comments

Comments
 (0)