Skip to content

Commit 91d9f1d

Browse files
author
Mateusz Rzeszutek
authored
Implement Host header parsing in HTTP client attributes extractor (#7288)
Continuation (and refactoring) of #6892
1 parent e2f9603 commit 91d9f1d

File tree

10 files changed

+195
-44
lines changed

10 files changed

+195
-44
lines changed

instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientAttributesExtractor.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ public static <REQUEST, RESPONSE> HttpClientAttributesExtractorBuilder<REQUEST,
8282
super(httpAttributesGetter, capturedRequestHeaders, capturedResponseHeaders);
8383
internalNetExtractor =
8484
new InternalNetClientAttributesExtractor<>(
85-
netAttributesGetter, this::shouldCapturePeerPort);
85+
netAttributesGetter,
86+
this::shouldCapturePeerPort,
87+
new HttpNetNamePortGetter<>(httpAttributesGetter));
8688
}
8789

8890
@Override

instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpCommonAttributesExtractor.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@
99
import static io.opentelemetry.instrumentation.api.instrumenter.http.CapturedHttpHeadersUtil.requestAttributeKey;
1010
import static io.opentelemetry.instrumentation.api.instrumenter.http.CapturedHttpHeadersUtil.responseAttributeKey;
1111
import static io.opentelemetry.instrumentation.api.internal.AttributesExtractorUtil.internalSet;
12+
import static java.util.logging.Level.FINE;
1213

1314
import io.opentelemetry.api.common.AttributesBuilder;
1415
import io.opentelemetry.context.Context;
1516
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
17+
import io.opentelemetry.instrumentation.api.instrumenter.net.internal.FallbackNamePortGetter;
1618
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
1719
import java.util.List;
20+
import java.util.logging.Logger;
1821
import javax.annotation.Nullable;
1922

2023
/**
@@ -26,6 +29,8 @@ abstract class HttpCommonAttributesExtractor<
2629
REQUEST, RESPONSE, GETTER extends HttpCommonAttributesGetter<REQUEST, RESPONSE>>
2730
implements AttributesExtractor<REQUEST, RESPONSE> {
2831

32+
private static final Logger logger = Logger.getLogger(HttpCommonAttributesGetter.class.getName());
33+
2934
final GETTER getter;
3035
private final List<String> capturedRequestHeaders;
3136
private final List<String> capturedResponseHeaders;
@@ -114,4 +119,43 @@ private static Long parseNumber(@Nullable String number) {
114119
return null;
115120
}
116121
}
122+
123+
static final class HttpNetNamePortGetter<REQUEST> implements FallbackNamePortGetter<REQUEST> {
124+
125+
private final HttpCommonAttributesGetter<REQUEST, ?> getter;
126+
127+
HttpNetNamePortGetter(HttpCommonAttributesGetter<REQUEST, ?> getter) {
128+
this.getter = getter;
129+
}
130+
131+
@Nullable
132+
@Override
133+
public String name(REQUEST request) {
134+
String host = firstHeaderValue(getter.requestHeader(request, "host"));
135+
if (host == null) {
136+
return null;
137+
}
138+
int hostHeaderSeparator = host.indexOf(':');
139+
return hostHeaderSeparator == -1 ? host : host.substring(0, hostHeaderSeparator);
140+
}
141+
142+
@Nullable
143+
@Override
144+
public Integer port(REQUEST request) {
145+
String host = firstHeaderValue(getter.requestHeader(request, "host"));
146+
if (host == null) {
147+
return null;
148+
}
149+
int hostHeaderSeparator = host.indexOf(':');
150+
if (hostHeaderSeparator == -1) {
151+
return null;
152+
}
153+
try {
154+
return Integer.parseInt(host.substring(hostHeaderSeparator + 1));
155+
} catch (NumberFormatException e) {
156+
logger.log(FINE, e.getMessage(), e);
157+
}
158+
return null;
159+
}
160+
}
117161
}

instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerAttributesExtractor.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ public static <REQUEST, RESPONSE> HttpServerAttributesExtractorBuilder<REQUEST,
7979
super(httpAttributesGetter, capturedRequestHeaders, capturedResponseHeaders);
8080
internalNetExtractor =
8181
new InternalNetServerAttributesExtractor<>(
82-
netAttributesGetter, this::shouldCaptureHostPort);
82+
netAttributesGetter,
83+
this::shouldCaptureHostPort,
84+
new HttpNetNamePortGetter<>(httpAttributesGetter));
8385
this.httpRouteHolderGetter = httpRouteHolderGetter;
8486
}
8587

@@ -95,7 +97,7 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST
9597
internalSet(attributes, SemanticAttributes.HTTP_ROUTE, getter.route(request));
9698
internalSet(attributes, SemanticAttributes.HTTP_CLIENT_IP, clientIp(request));
9799

98-
internalNetExtractor.onStart(attributes, request, host(request));
100+
internalNetExtractor.onStart(attributes, request);
99101
}
100102

101103
private boolean shouldCaptureHostPort(int port, REQUEST request) {
@@ -122,11 +124,6 @@ public void onEnd(
122124
internalSet(attributes, SemanticAttributes.HTTP_ROUTE, httpRouteHolderGetter.apply(context));
123125
}
124126

125-
@Nullable
126-
private String host(REQUEST request) {
127-
return firstHeaderValue(getter.requestHeader(request, "host"));
128-
}
129-
130127
@Nullable
131128
private String forwardedProto(REQUEST request) {
132129
// try Forwarded

instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetClientAttributesExtractor.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import io.opentelemetry.api.common.AttributesBuilder;
99
import io.opentelemetry.context.Context;
1010
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
11+
import io.opentelemetry.instrumentation.api.instrumenter.net.internal.FallbackNamePortGetter;
1112
import io.opentelemetry.instrumentation.api.instrumenter.net.internal.InternalNetClientAttributesExtractor;
1213
import javax.annotation.Nullable;
1314

@@ -31,7 +32,9 @@ public static <REQUEST, RESPONSE> NetClientAttributesExtractor<REQUEST, RESPONSE
3132
}
3233

3334
private NetClientAttributesExtractor(NetClientAttributesGetter<REQUEST, RESPONSE> getter) {
34-
internalExtractor = new InternalNetClientAttributesExtractor<>(getter, (port, request) -> true);
35+
internalExtractor =
36+
new InternalNetClientAttributesExtractor<>(
37+
getter, (port, request) -> true, FallbackNamePortGetter.noop());
3538
}
3639

3740
@Override

instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/NetServerAttributesExtractor.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import io.opentelemetry.api.common.AttributesBuilder;
99
import io.opentelemetry.context.Context;
1010
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
11+
import io.opentelemetry.instrumentation.api.instrumenter.net.internal.FallbackNamePortGetter;
1112
import io.opentelemetry.instrumentation.api.instrumenter.net.internal.InternalNetServerAttributesExtractor;
1213
import javax.annotation.Nullable;
1314

@@ -29,12 +30,13 @@ public static <REQUEST, RESPONSE> NetServerAttributesExtractor<REQUEST, RESPONSE
2930

3031
private NetServerAttributesExtractor(NetServerAttributesGetter<REQUEST> getter) {
3132
internalExtractor =
32-
new InternalNetServerAttributesExtractor<>(getter, (integer, request) -> true);
33+
new InternalNetServerAttributesExtractor<>(
34+
getter, (integer, request) -> true, FallbackNamePortGetter.noop());
3335
}
3436

3537
@Override
3638
public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST request) {
37-
internalExtractor.onStart(attributes, request, null);
39+
internalExtractor.onStart(attributes, request);
3840
}
3941

4042
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.instrumenter.net.internal;
7+
8+
import javax.annotation.Nullable;
9+
10+
/**
11+
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
12+
* any time.
13+
*/
14+
public interface FallbackNamePortGetter<REQUEST> {
15+
16+
@Nullable
17+
String name(REQUEST request);
18+
19+
@Nullable
20+
Integer port(REQUEST request);
21+
22+
@SuppressWarnings("unchecked")
23+
static <REQUEST> FallbackNamePortGetter<REQUEST> noop() {
24+
return (FallbackNamePortGetter<REQUEST>) NoopNamePortGetter.INSTANCE;
25+
}
26+
}

instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/internal/InternalNetClientAttributesExtractor.java

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,24 @@ public final class InternalNetClientAttributesExtractor<REQUEST, RESPONSE> {
2121

2222
private final NetClientAttributesGetter<REQUEST, RESPONSE> getter;
2323
private final BiPredicate<Integer, REQUEST> capturePeerPortCondition;
24+
private final FallbackNamePortGetter<REQUEST> fallbackNamePortGetter;
2425

2526
public InternalNetClientAttributesExtractor(
2627
NetClientAttributesGetter<REQUEST, RESPONSE> getter,
27-
BiPredicate<Integer, REQUEST> capturePeerPortCondition) {
28+
BiPredicate<Integer, REQUEST> capturePeerPortCondition,
29+
FallbackNamePortGetter<REQUEST> fallbackNamePortGetter) {
2830
this.getter = getter;
2931
this.capturePeerPortCondition = capturePeerPortCondition;
32+
this.fallbackNamePortGetter = fallbackNamePortGetter;
3033
}
3134

3235
public void onStart(AttributesBuilder attributes, REQUEST request) {
33-
String peerName = getter.peerName(request);
34-
Integer peerPort = getter.peerPort(request);
35-
36-
// TODO: add host header parsing
36+
String peerName = extractPeerName(request);
3737

3838
if (peerName != null) {
3939
internalSet(attributes, SemanticAttributes.NET_PEER_NAME, peerName);
40+
41+
Integer peerPort = extractPeerPort(request);
4042
if (peerPort != null && peerPort > 0 && capturePeerPortCondition.test(peerPort, request)) {
4143
internalSet(attributes, SemanticAttributes.NET_PEER_PORT, (long) peerPort);
4244
}
@@ -47,13 +49,13 @@ public void onEnd(AttributesBuilder attributes, REQUEST request, @Nullable RESPO
4749

4850
internalSet(attributes, SemanticAttributes.NET_TRANSPORT, getter.transport(request, response));
4951

50-
String peerName = getter.peerName(request);
51-
Integer peerPort = getter.peerPort(request);
52+
String peerName = extractPeerName(request);
5253

5354
String sockPeerAddr = getter.sockPeerAddr(request, response);
5455
if (sockPeerAddr != null && !sockPeerAddr.equals(peerName)) {
5556
internalSet(attributes, SemanticAttributes.NET_SOCK_PEER_ADDR, sockPeerAddr);
5657

58+
Integer peerPort = extractPeerPort(request);
5759
Integer sockPeerPort = getter.sockPeerPort(request, response);
5860
if (sockPeerPort != null && sockPeerPort > 0 && !sockPeerPort.equals(peerPort)) {
5961
internalSet(attributes, SemanticAttributes.NET_SOCK_PEER_PORT, (long) sockPeerPort);
@@ -70,4 +72,20 @@ public void onEnd(AttributesBuilder attributes, REQUEST request, @Nullable RESPO
7072
}
7173
}
7274
}
75+
76+
private String extractPeerName(REQUEST request) {
77+
String peerName = getter.peerName(request);
78+
if (peerName == null) {
79+
peerName = fallbackNamePortGetter.name(request);
80+
}
81+
return peerName;
82+
}
83+
84+
private Integer extractPeerPort(REQUEST request) {
85+
Integer peerPort = getter.peerPort(request);
86+
if (peerPort == null) {
87+
peerPort = fallbackNamePortGetter.port(request);
88+
}
89+
return peerPort;
90+
}
7391
}

instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/net/internal/InternalNetServerAttributesExtractor.java

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,32 @@
66
package io.opentelemetry.instrumentation.api.instrumenter.net.internal;
77

88
import static io.opentelemetry.instrumentation.api.internal.AttributesExtractorUtil.internalSet;
9-
import static java.util.logging.Level.FINE;
109

1110
import io.opentelemetry.api.common.AttributesBuilder;
1211
import io.opentelemetry.instrumentation.api.instrumenter.net.NetServerAttributesGetter;
1312
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
1413
import java.util.function.BiPredicate;
15-
import java.util.logging.Logger;
16-
import javax.annotation.Nullable;
1714

1815
/**
1916
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
2017
* any time.
2118
*/
2219
public final class InternalNetServerAttributesExtractor<REQUEST> {
2320

24-
private static final Logger logger =
25-
Logger.getLogger(InternalNetServerAttributesExtractor.class.getName());
26-
2721
private final NetServerAttributesGetter<REQUEST> getter;
2822
private final BiPredicate<Integer, REQUEST> captureHostPortCondition;
23+
private final FallbackNamePortGetter<REQUEST> fallbackNamePortGetter;
2924

3025
public InternalNetServerAttributesExtractor(
3126
NetServerAttributesGetter<REQUEST> getter,
32-
BiPredicate<Integer, REQUEST> captureHostPortCondition) {
27+
BiPredicate<Integer, REQUEST> captureHostPortCondition,
28+
FallbackNamePortGetter<REQUEST> fallbackNamePortGetter) {
3329
this.getter = getter;
3430
this.captureHostPortCondition = captureHostPortCondition;
31+
this.fallbackNamePortGetter = fallbackNamePortGetter;
3532
}
3633

37-
public void onStart(AttributesBuilder attributes, REQUEST request, @Nullable String hostHeader) {
34+
public void onStart(AttributesBuilder attributes, REQUEST request) {
3835
internalSet(attributes, SemanticAttributes.NET_TRANSPORT, getter.transport(request));
3936

4037
boolean setSockFamily = false;
@@ -51,24 +48,8 @@ public void onStart(AttributesBuilder attributes, REQUEST request, @Nullable Str
5148
}
5249
}
5350

54-
String hostName = getter.hostName(request);
55-
Integer hostPort = getter.hostPort(request);
56-
57-
int hostHeaderSeparator = -1;
58-
if (hostHeader != null) {
59-
hostHeaderSeparator = hostHeader.indexOf(':');
60-
}
61-
if (hostName == null && hostHeader != null) {
62-
hostName =
63-
hostHeaderSeparator == -1 ? hostHeader : hostHeader.substring(0, hostHeaderSeparator);
64-
}
65-
if (hostPort == null && hostHeader != null && hostHeaderSeparator != -1) {
66-
try {
67-
hostPort = Integer.parseInt(hostHeader.substring(hostHeaderSeparator + 1));
68-
} catch (NumberFormatException e) {
69-
logger.log(FINE, e.getMessage(), e);
70-
}
71-
}
51+
String hostName = extractHostName(request);
52+
Integer hostPort = extractHostPort(request);
7253

7354
if (hostName != null) {
7455
internalSet(attributes, SemanticAttributes.NET_HOST_NAME, hostName);
@@ -97,4 +78,20 @@ public void onStart(AttributesBuilder attributes, REQUEST request, @Nullable Str
9778
}
9879
}
9980
}
81+
82+
private String extractHostName(REQUEST request) {
83+
String peerName = getter.hostName(request);
84+
if (peerName == null) {
85+
peerName = fallbackNamePortGetter.name(request);
86+
}
87+
return peerName;
88+
}
89+
90+
private Integer extractHostPort(REQUEST request) {
91+
Integer peerPort = getter.hostPort(request);
92+
if (peerPort == null) {
93+
peerPort = fallbackNamePortGetter.port(request);
94+
}
95+
return peerPort;
96+
}
10097
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.instrumenter.net.internal;
7+
8+
import javax.annotation.Nullable;
9+
10+
enum NoopNamePortGetter implements FallbackNamePortGetter<Object> {
11+
INSTANCE;
12+
13+
@Nullable
14+
@Override
15+
public String name(Object o) {
16+
return null;
17+
}
18+
19+
@Nullable
20+
@Override
21+
public Integer port(Object o) {
22+
return null;
23+
}
24+
}

0 commit comments

Comments
 (0)