com.github.kristofa.brave.internal.MaybeAddClientAddress Maven / Gradle / Ivy
package com.github.kristofa.brave.internal;
import com.github.kristofa.brave.Brave;
import com.github.kristofa.brave.ServerSpan;
import com.twitter.zipkin.gen.Endpoint;
import com.twitter.zipkin.gen.Span;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import zipkin.Constants;
import static com.github.kristofa.brave.internal.Util.checkNotNull;
/**
* Parses the {@link Constants#CLIENT_ADDR client address}, possibly by looking at
* "X-Forwarded-For", then the remote address of the input. This performs no DNS lookups.
*
* This is a hack as {@code com.github.kristofa.brave.http.HttpServerRequest} is an interface and
* would break api if we changed it. Moreover, this can work on non-http input types.
*/
public abstract class MaybeAddClientAddress {
final Brave brave;
protected MaybeAddClientAddress(Brave brave) { // accepts brave so we can re-factor thread state
this.brave = checkNotNull(brave, "brave");
}
public final void accept(T input) {
// Kick out if we can't read the current span
ServerSpan serverSpan = brave.serverSpanThreadBinder().getCurrentServerSpan();
Span span = serverSpan != null ? serverSpan.getSpan() : null;
if (span == null) return;
// Kick out if we can't cheaply read the address
byte[] addressBytes;
try {
addressBytes = parseAddressBytes(input);
if (addressBytes == null) return;
} catch (RuntimeException e) {
return;
}
// Build an endpoint with no service name (rather than risk cluttering the service list!)
Endpoint.Builder builder = Endpoint.builder().serviceName("");
if (addressBytes.length == 4) {
builder.ipv4(ByteBuffer.wrap(addressBytes).getInt());
} else if (addressBytes.length == 16) {
builder.ipv6(addressBytes);
} else {
return; // invalid
}
try {
int port = parsePort(input);
if (port > 0) builder.port(port);
} catch (RuntimeException ignore) {
// still store the ip address
}
Endpoint ca = builder.build();
Internal.instance.setClientAddress(brave, ca);
}
/**
* Returns the 4 byte ipv4 address or the 16-byte ipv6 address associated with the input type.
*
* {@code
* byte[] addressBytes = ipStringToBytes(input.getHeader("X-Forwarded-For"));
* if (addressBytes == null) addressBytes = ipStringToBytes(input.getRemoteAddr());
* return addressBytes;
* }
*/
protected abstract byte[] parseAddressBytes(T input);
/** Returns port associated with the input or <=0 if unreadable. */
protected abstract int parsePort(T input);
/**
* Returns the {@link InetAddress#getAddress()} having the given string representation or null if
* unable to parse.
*
* This deliberately avoids all nameservice lookups (e.g. no DNS).
*
* @param ipString {@code String} containing an IPv4 or IPv6 string literal, e.g. {@code
* "192.168.0.1"} or {@code "2001:db8::1"}
*/
@Nullable
protected byte[] ipStringToBytes(String ipString) {
return InetAddresses.ipStringToBytes(ipString);
}
}