Skip to content

Commit defd7cb

Browse files
authored
Test undertow and armeria http2 server (#11361)
1 parent bae4c3b commit defd7cb

File tree

12 files changed

+219
-89
lines changed

12 files changed

+219
-89
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.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 final class HttpProtocolUtil {
15+
16+
public static String getProtocol(@Nullable String protocol) {
17+
if (protocol != null && protocol.startsWith("HTTP/")) {
18+
return "http";
19+
}
20+
return null;
21+
}
22+
23+
public static String getVersion(@Nullable String protocol) {
24+
if (protocol != null && protocol.startsWith("HTTP/")) {
25+
return normalizeHttpVersion(protocol.substring("HTTP/".length()));
26+
}
27+
return null;
28+
}
29+
30+
public static String normalizeHttpVersion(String version) {
31+
if ("2.0".equals(version)) {
32+
return "2";
33+
}
34+
35+
return version;
36+
}
37+
38+
private HttpProtocolUtil() {}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.armeria.v1_3;
7+
8+
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions;
9+
10+
class ArmeriaHttp2ServerTest extends ArmeriaHttpServerTest {
11+
12+
@Override
13+
protected void configure(HttpServerTestOptions options) {
14+
super.configure(options);
15+
options.useHttp2();
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.armeria.v1_3;
7+
8+
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions;
9+
10+
class ArmeriaHttp2ServerTest extends ArmeriaHttpServerTest {
11+
12+
@Override
13+
protected void configure(HttpServerTestOptions options) {
14+
super.configure(options);
15+
options.useHttp2();
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import static io.opentelemetry.javaagent.instrumentation.undertow.UndertowSingletons.helper;
99
import static net.bytebuddy.matcher.ElementMatchers.named;
10+
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
1011
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
1112

1213
import io.opentelemetry.context.Context;
@@ -18,16 +19,18 @@
1819
import net.bytebuddy.description.type.TypeDescription;
1920
import net.bytebuddy.matcher.ElementMatcher;
2021

21-
public class HttpTransferEncodingInstrumentation implements TypeInstrumentation {
22+
public class HttpServerConnectionInstrumentation implements TypeInstrumentation {
2223
@Override
2324
public ElementMatcher<TypeDescription> typeMatcher() {
24-
return named("io.undertow.server.protocol.http.HttpTransferEncoding");
25+
return namedOneOf(
26+
"io.undertow.server.protocol.http.HttpServerConnection",
27+
"io.undertow.server.protocol.http2.Http2ServerConnection");
2528
}
2629

2730
@Override
2831
public void transform(TypeTransformer transformer) {
2932
transformer.applyAdviceToMethod(
30-
named("createSinkConduit")
33+
named("getSinkConduit")
3134
.and(takesArgument(0, named("io.undertow.server.HttpServerExchange"))),
3235
this.getClass().getName() + "$ResponseAdvice");
3336
}

instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowHttpAttributesGetter.java

+3-10
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
package io.opentelemetry.javaagent.instrumentation.undertow;
77

8+
import io.opentelemetry.instrumentation.api.internal.HttpProtocolUtil;
89
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesGetter;
910
import io.undertow.server.HttpServerExchange;
1011
import io.undertow.util.HeaderValues;
@@ -65,22 +66,14 @@ public String getUrlQuery(HttpServerExchange exchange) {
6566
@Override
6667
public String getNetworkProtocolName(
6768
HttpServerExchange exchange, @Nullable HttpServerExchange unused) {
68-
String protocol = exchange.getProtocol().toString();
69-
if (protocol.startsWith("HTTP/")) {
70-
return "http";
71-
}
72-
return null;
69+
return HttpProtocolUtil.getProtocol(exchange.getProtocol().toString());
7370
}
7471

7572
@Nullable
7673
@Override
7774
public String getNetworkProtocolVersion(
7875
HttpServerExchange exchange, @Nullable HttpServerExchange unused) {
79-
String protocol = exchange.getProtocol().toString();
80-
if (protocol.startsWith("HTTP/")) {
81-
return protocol.substring("HTTP/".length());
82-
}
83-
return null;
76+
return HttpProtocolUtil.getVersion(exchange.getProtocol().toString());
8477
}
8578

8679
@Override

instrumentation/undertow-1.4/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/undertow/UndertowInstrumentationModule.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ public List<TypeInstrumentation> typeInstrumentations() {
3232
return asList(
3333
new HandlerInstrumentation(),
3434
new HttpServerExchangeInstrumentation(),
35-
new HttpTransferEncodingInstrumentation());
35+
new HttpServerConnectionInstrumentation());
3636
}
3737
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import io.undertow.Undertow
7+
import io.undertow.UndertowOptions
8+
9+
class UndertowHttp2ServerTest extends UndertowServerTest {
10+
11+
void configureUndertow(Undertow.Builder builder) {
12+
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true)
13+
}
14+
15+
boolean useHttp2() {
16+
true
17+
}
18+
}

instrumentation/undertow-1.4/javaagent/src/test/groovy/UndertowServerTest.groovy

+75-68
Original file line numberDiff line numberDiff line change
@@ -36,72 +36,74 @@ class UndertowServerTest extends HttpServerTest<Undertow> implements AgentTestTr
3636

3737
@Override
3838
Undertow startServer(int port) {
39-
Undertow server = Undertow.builder()
40-
.addHttpListener(port, "localhost")
41-
.setHandler(Handlers.path()
42-
.addExactPath(SUCCESS.rawPath()) { exchange ->
43-
controller(SUCCESS) {
44-
exchange.getResponseSender().send(SUCCESS.body)
45-
}
46-
}
47-
.addExactPath(QUERY_PARAM.rawPath()) { exchange ->
48-
controller(QUERY_PARAM) {
49-
exchange.getResponseSender().send(exchange.getQueryString())
50-
}
51-
}
52-
.addExactPath(REDIRECT.rawPath()) { exchange ->
53-
controller(REDIRECT) {
54-
exchange.setStatusCode(StatusCodes.FOUND)
55-
exchange.getResponseHeaders().put(Headers.LOCATION, REDIRECT.body)
56-
exchange.endExchange()
57-
}
58-
}
59-
.addExactPath(CAPTURE_HEADERS.rawPath()) { exchange ->
60-
controller(CAPTURE_HEADERS) {
61-
exchange.setStatusCode(StatusCodes.OK)
62-
exchange.getResponseHeaders().put(new HttpString("X-Test-Response"), exchange.getRequestHeaders().getFirst("X-Test-Request"))
63-
exchange.getResponseSender().send(CAPTURE_HEADERS.body)
64-
}
65-
}
66-
.addExactPath(ERROR.rawPath()) { exchange ->
67-
controller(ERROR) {
68-
exchange.setStatusCode(ERROR.status)
69-
exchange.getResponseSender().send(ERROR.body)
70-
}
71-
}
72-
.addExactPath(EXCEPTION.rawPath()) { exchange ->
73-
controller(EXCEPTION) {
74-
throw new Exception(EXCEPTION.body)
75-
}
76-
}
77-
.addExactPath(INDEXED_CHILD.rawPath()) { exchange ->
78-
controller(INDEXED_CHILD) {
79-
INDEXED_CHILD.collectSpanAttributes { name -> exchange.getQueryParameters().get(name).peekFirst() }
80-
exchange.getResponseSender().send(INDEXED_CHILD.body)
81-
}
82-
}
83-
.addExactPath("sendResponse") { exchange ->
84-
Span.current().addEvent("before-event")
85-
runWithSpan("sendResponse") {
86-
exchange.setStatusCode(StatusCodes.OK)
87-
exchange.getResponseSender().send("sendResponse")
88-
}
89-
// event is added only when server span has not been ended
90-
// we need to make sure that sending response does not end server span
91-
Span.current().addEvent("after-event")
92-
}
93-
.addExactPath("sendResponseWithException") { exchange ->
94-
Span.current().addEvent("before-event")
95-
runWithSpan("sendResponseWithException") {
96-
exchange.setStatusCode(StatusCodes.OK)
97-
exchange.getResponseSender().send("sendResponseWithException")
98-
}
99-
// event is added only when server span has not been ended
100-
// we need to make sure that sending response does not end server span
101-
Span.current().addEvent("after-event")
102-
throw new Exception("exception after sending response")
103-
}
104-
).build()
39+
Undertow.Builder builder = Undertow.builder()
40+
.addHttpListener(port, "localhost")
41+
.setHandler(Handlers.path()
42+
.addExactPath(SUCCESS.rawPath()) { exchange ->
43+
controller(SUCCESS) {
44+
exchange.getResponseSender().send(SUCCESS.body)
45+
}
46+
}
47+
.addExactPath(QUERY_PARAM.rawPath()) { exchange ->
48+
controller(QUERY_PARAM) {
49+
exchange.getResponseSender().send(exchange.getQueryString())
50+
}
51+
}
52+
.addExactPath(REDIRECT.rawPath()) { exchange ->
53+
controller(REDIRECT) {
54+
exchange.setStatusCode(StatusCodes.FOUND)
55+
exchange.getResponseHeaders().put(Headers.LOCATION, REDIRECT.body)
56+
exchange.endExchange()
57+
}
58+
}
59+
.addExactPath(CAPTURE_HEADERS.rawPath()) { exchange ->
60+
controller(CAPTURE_HEADERS) {
61+
exchange.setStatusCode(StatusCodes.OK)
62+
exchange.getResponseHeaders().put(new HttpString("X-Test-Response"), exchange.getRequestHeaders().getFirst("X-Test-Request"))
63+
exchange.getResponseSender().send(CAPTURE_HEADERS.body)
64+
}
65+
}
66+
.addExactPath(ERROR.rawPath()) { exchange ->
67+
controller(ERROR) {
68+
exchange.setStatusCode(ERROR.status)
69+
exchange.getResponseSender().send(ERROR.body)
70+
}
71+
}
72+
.addExactPath(EXCEPTION.rawPath()) { exchange ->
73+
controller(EXCEPTION) {
74+
throw new Exception(EXCEPTION.body)
75+
}
76+
}
77+
.addExactPath(INDEXED_CHILD.rawPath()) { exchange ->
78+
controller(INDEXED_CHILD) {
79+
INDEXED_CHILD.collectSpanAttributes { name -> exchange.getQueryParameters().get(name).peekFirst() }
80+
exchange.getResponseSender().send(INDEXED_CHILD.body)
81+
}
82+
}
83+
.addExactPath("sendResponse") { exchange ->
84+
Span.current().addEvent("before-event")
85+
runWithSpan("sendResponse") {
86+
exchange.setStatusCode(StatusCodes.OK)
87+
exchange.getResponseSender().send("sendResponse")
88+
}
89+
// event is added only when server span has not been ended
90+
// we need to make sure that sending response does not end server span
91+
Span.current().addEvent("after-event")
92+
}
93+
.addExactPath("sendResponseWithException") { exchange ->
94+
Span.current().addEvent("before-event")
95+
runWithSpan("sendResponseWithException") {
96+
exchange.setStatusCode(StatusCodes.OK)
97+
exchange.getResponseSender().send("sendResponseWithException")
98+
}
99+
// event is added only when server span has not been ended
100+
// we need to make sure that sending response does not end server span
101+
Span.current().addEvent("after-event")
102+
throw new Exception("exception after sending response")
103+
}
104+
)
105+
configureUndertow(builder)
106+
Undertow server = builder.build()
105107
server.start()
106108
return server
107109
}
@@ -111,6 +113,9 @@ class UndertowServerTest extends HttpServerTest<Undertow> implements AgentTestTr
111113
undertow.stop()
112114
}
113115

116+
void configureUndertow(Undertow.Builder builder) {
117+
}
118+
114119
@Override
115120
Set<AttributeKey<?>> httpAttributes(ServerEndpoint endpoint) {
116121
def attributes = super.httpAttributes(endpoint)
@@ -147,14 +152,15 @@ class UndertowServerTest extends HttpServerTest<Undertow> implements AgentTestTr
147152
eventName "after-event"
148153
}
149154

155+
def protocolVersion = useHttp2() ? "2" : "1.1"
150156
attributes {
151157
"$ClientAttributes.CLIENT_ADDRESS" TEST_CLIENT_IP
152158
"$UrlAttributes.URL_SCHEME" uri.getScheme()
153159
"$UrlAttributes.URL_PATH" uri.getPath()
154160
"$HttpAttributes.HTTP_REQUEST_METHOD" "GET"
155161
"$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200
156162
"$UserAgentAttributes.USER_AGENT_ORIGINAL" TEST_USER_AGENT
157-
"$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1"
163+
"$NetworkAttributes.NETWORK_PROTOCOL_VERSION" protocolVersion
158164
"$ServerAttributes.SERVER_ADDRESS" uri.host
159165
"$ServerAttributes.SERVER_PORT" uri.port
160166
"$NetworkAttributes.NETWORK_PEER_ADDRESS" "127.0.0.1"
@@ -196,14 +202,15 @@ class UndertowServerTest extends HttpServerTest<Undertow> implements AgentTestTr
196202
}
197203
errorEvent(Exception, "exception after sending response", 2)
198204

205+
def protocolVersion = useHttp2() ? "2" : "1.1"
199206
attributes {
200207
"$ClientAttributes.CLIENT_ADDRESS" TEST_CLIENT_IP
201208
"$UrlAttributes.URL_SCHEME" uri.getScheme()
202209
"$UrlAttributes.URL_PATH" uri.getPath()
203210
"$HttpAttributes.HTTP_REQUEST_METHOD" "GET"
204211
"$HttpAttributes.HTTP_RESPONSE_STATUS_CODE" 200
205212
"$UserAgentAttributes.USER_AGENT_ORIGINAL" TEST_USER_AGENT
206-
"$NetworkAttributes.NETWORK_PROTOCOL_VERSION" "1.1"
213+
"$NetworkAttributes.NETWORK_PROTOCOL_VERSION" protocolVersion
207214
"$ServerAttributes.SERVER_ADDRESS" uri.host
208215
"$ServerAttributes.SERVER_PORT" uri.port
209216
"$NetworkAttributes.NETWORK_PEER_ADDRESS" "127.0.0.1"

testing-common/src/main/groovy/io/opentelemetry/instrumentation/test/base/HttpServerTest.groovy

+10
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
3434
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM
3535
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM
3636
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS
37+
import static org.junit.jupiter.api.Assumptions.assumeFalse
3738
import static org.junit.jupiter.api.Assumptions.assumeTrue
3839

3940
@Unroll
@@ -80,6 +81,10 @@ abstract class HttpServerTest<SERVER> extends InstrumentationSpecification imple
8081
return ""
8182
}
8283

84+
boolean useHttp2() {
85+
false
86+
}
87+
8388
boolean hasHandlerSpan(ServerEndpoint endpoint) {
8489
false
8590
}
@@ -249,6 +254,7 @@ abstract class HttpServerTest<SERVER> extends InstrumentationSpecification imple
249254
if (!testNonStandardHttpMethod()) {
250255
options.disableTestNonStandardHttpMethod()
251256
}
257+
options.useHttp2 = useHttp2()
252258
}
253259

254260
// Override trace assertion method. We can call java assertions from groovy but not the other
@@ -352,12 +358,16 @@ abstract class HttpServerTest<SERVER> extends InstrumentationSpecification imple
352358

353359
def "http pipelining test"() {
354360
assumeTrue(testHttpPipelining())
361+
// test uses http 1.1
362+
assumeFalse(useHttp2())
355363
expect:
356364
junitTest.httpPipelining()
357365
}
358366

359367
def "non standard http method"() {
360368
assumeTrue(testNonStandardHttpMethod())
369+
// test uses http 1.1
370+
assumeFalse(useHttp2())
361371
expect:
362372
junitTest.requestWithNonStandardHttpMethod()
363373
}

0 commit comments

Comments
 (0)