Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@

package com.datadoghq.dogstatsd.http;

import java.net.URI;
import java.util.Map;

/** Provides common parameters to the forwarder implementations. */
public class ForwarderContext {
private final URI baseUri;
private final String localData;
private final String externalData;

private ForwarderContext(final String localData, final String externalData) {
private ForwarderContext(final URI baseUri, final String localData, final String externalData) {
this.baseUri = baseUri;
this.localData = localData;
this.externalData = externalData;
}
Expand All @@ -28,6 +31,10 @@ public static Builder builder() {
return new Builder();
}

public URI baseUri() {
return baseUri;
}

/**
* Returns the local-data value: a container ID, an {@code in-<inode>} cgroup fallback, or null
* when neither could be determined.
Expand Down Expand Up @@ -63,6 +70,7 @@ public static final class Builder {
private CgroupReader cgroupReader = new CgroupReader();
private String localData;
private String externalData;
private String baseUri;

private Builder() {}

Expand Down Expand Up @@ -100,7 +108,19 @@ public Builder originDetectionEnabled(final boolean val) {
return this;
}

Builder environment(final Map<String, String> val) {
/**
* Sets the base URI the series and sketches endpoints are resolved against. Defaults to the
* value of the {@code DD_DOGSTATSD_HTTP_URL} environment variable.
*
* @param val the base URI, or null to use the default.
* @return this builder.
*/
public Builder baseUri(final String uri) {
baseUri = uri;
return this;
}

public Builder environment(final Map<String, String> val) {
env = new EnvMap(val);
return this;
}
Expand All @@ -114,6 +134,7 @@ Builder cgroupReader(final CgroupReader val) {
* Builds the context, running detection for any value not set explicitly.
*
* @return a new context.
* @throws URISyntaxException if baseUri value is not a valid URI.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Should be IllegalStateException or IllegalArgumentException instead

*/
public ForwarderContext build() {
String local = localData;
Expand All @@ -128,7 +149,7 @@ public ForwarderContext build() {
}
}

return new ForwarderContext(local, external);
return new ForwarderContext(resolveBaseUri(), local, external);
}

boolean resolveOriginDetectionEnabled() {
Expand All @@ -147,5 +168,20 @@ boolean resolveOriginDetectionEnabled() {
|| "n".equals(normalized)
|| "off".equals(normalized));
}

URI resolveBaseUri() {
if (baseUri == null) {
baseUri = env.get("DD_DOGSTATSD_HTTP_URL");
}
if (baseUri == null) {
throw new IllegalStateException(
"baseUri is not set and DD_DOGSTATSD_HTTP_URL is not defined");
}
// Make sure baseUri acts as a prefix when we use it with URI#resolve later.
if (!baseUri.endsWith("/")) {
baseUri += "/";
}
return URI.create(baseUri);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public String getContainerID() {

private static ForwarderContext.Builder builder(Map<String, String> env, String containerID) {
return ForwarderContext.builder()
.baseUri("http://localhost:8125")
.environment(env)
.cgroupReader(new StubCgroupReader(containerID));
}
Expand Down Expand Up @@ -166,7 +167,10 @@ public void originDetectionEnabledEnvVar() {
}

boolean detects =
ForwarderContext.builder().environment(env).resolveOriginDetectionEnabled();
ForwarderContext.builder()
.environment(env)
.baseUri("http://localhost:8125")
.resolveOriginDetectionEnabled();
assertEquals(c.value, c.detects, detects);
}
}
Expand Down Expand Up @@ -234,6 +238,7 @@ public void emptyExternalEnvIsPassedThrough() {
public void emptyEnvironmentDetectsLocalDataOnly() {
ForwarderContext ctx =
ForwarderContext.builder()
.baseUri("http://localhost:8125")
.environment(Collections.<String, String>emptyMap())
.cgroupReader(new StubCgroupReader("container-id"))
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class Forwarder extends Thread {
final Duration requestTimeout;
final Random rng = new Random();

final URI baseUri;
final String localData;
final String externalData;

Expand All @@ -61,6 +62,7 @@ public static Builder builder() {
builder.whenFull,
this.telemetry);
this.requestTimeout = builder.requestTimeout;
this.baseUri = builder.baseUri;
this.localData = builder.localData;
this.externalData = builder.externalData;

Expand Down Expand Up @@ -118,13 +120,12 @@ public void send(URI url, byte[] payload) throws InterruptedException {

void runOnce(Map.Entry<BoundedQueue.Key, Payload> item) throws InterruptedException {
Payload payload = item.getValue();
final URI url = baseUri.resolve(payload.url);
logger.log(
Level.INFO,
"sending {0} bytes to {1}",
new Object[] {payload.bytes.length, payload.url});
Level.INFO, "sending {0} bytes to {1}", new Object[] {payload.bytes.length, url});

HttpRequest.Builder builder =
HttpRequest.newBuilder(payload.url).POST(BodyPublishers.ofByteArray(payload.bytes));
HttpRequest.newBuilder(url).POST(BodyPublishers.ofByteArray(payload.bytes));
if (requestTimeout != null) {
builder.timeout(requestTimeout);
}
Expand Down Expand Up @@ -242,6 +243,7 @@ public static final class Builder {
private String localData;
private String externalData;
private boolean contextSet;
private URI baseUri;

private Builder() {}

Expand Down Expand Up @@ -322,18 +324,15 @@ public Builder requestTimeout(final Duration val) {
*
* <p>Defaults to {@code ForwarderContext.defaults()}.
*
* @param context the context to take the values from, or {@code null}.
* @param context the context to take the values from.
* @return this builder.
*/
public Builder context(final ForwarderContext context) {
contextSet = true;
if (context == null) {
localData = null;
externalData = null;
} else {
localData = validateHeaderValue(context.localData());
externalData = validateHeaderValue(context.externalData());
}
Objects.requireNonNull(context);
baseUri = context.baseUri();
localData = validateHeaderValue(context.localData());
externalData = validateHeaderValue(context.externalData());
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,26 @@
import com.datadoghq.dogstatsd.http.ForwarderContext;
import java.net.URI;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Test;

public class ForwarderTest {
private static final URI URL = URI.create("http://localhost:0/");
private static final Map<String, String> emptyMap = new HashMap();

private static ForwarderContext.Builder contextBuilder() {
return ForwarderContext.builder().environment(emptyMap).baseUri("http://localhost:8125");
}

private static Forwarder.Builder builder() {
return Forwarder.builder().context(contextBuilder().build());
}

private static Forwarder newForwarder(long maxBytes, WhenFull whenFull) {
return Forwarder.builder()
.maxRequestsBytes(maxBytes)
.maxTries(1)
.whenFull(whenFull)
.build();
return builder().maxRequestsBytes(maxBytes).maxTries(1).whenFull(whenFull).build();
}

@Test
Expand All @@ -47,7 +53,7 @@ public void builderRejectsInvalidValues() {
/** A null request timeout is legal and means requests have no timeout at all. */
@Test
public void nullRequestTimeoutIsAllowed() {
Forwarder f = Forwarder.builder().requestTimeout(null).build();
Forwarder f = builder().requestTimeout(null).build();
assertNull(f.requestTimeout);
}

Expand All @@ -56,26 +62,16 @@ public void contextSuppliesOriginDetectionHeaders() {
Forwarder f =
Forwarder.builder()
.context(
ForwarderContext.builder()
.localData("ci-abc")
.externalData("en-xyz")
.build())
contextBuilder().localData("ci-abc").externalData("en-xyz").build())
.build();
assertEquals("ci-abc", f.localData);
assertEquals("en-xyz", f.externalData);
}

@Test
public void nullContextOmitsOriginDetectionHeaders() {
Forwarder f = Forwarder.builder().context(null).build();
assertNull(f.localData);
assertNull(f.externalData);
}

/** Values that can't be sent as a header value are rejected where they're supplied. */
@Test
public void contextRejectsUnsendableHeaderValue() {
ForwarderContext ctx = ForwarderContext.builder().localData("bad\nvalue").build();
ForwarderContext ctx = contextBuilder().localData("bad\nvalue").build();
Forwarder.Builder b = Forwarder.builder();
assertThrows(IllegalArgumentException.class, () -> b.context(ctx));
}
Expand Down