Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
484cf9b
Fix v2 inferred proxy span tests for Java
jandro996 Feb 10, 2026
1391c1b
Enable optional tags validation for Java in v2 tests
jandro996 Feb 10, 2026
5530fab
wip - working
jandro996 Feb 20, 2026
5c38d02
upgrade tracer version
jandro996 Mar 5, 2026
c76b6f5
Assert http.status_code and error on inferred proxy spans for Java
jandro996 Mar 6, 2026
270f7e7
Merge branch 'main' into alejandro.gonzalez/rfc-1081-proxy
jandro996 Mar 6, 2026
77e7b4c
Merge branch 'main' into alejandro.gonzalez/rfc-1081-proxy
jandro996 Mar 6, 2026
6bb03c7
Remove Java-specific branch for service name on inferred proxy servic…
jandro996 Mar 6, 2026
b9ea74d
wip
jandro996 Mar 9, 2026
323e7db
Merge branch 'main' into alejandro.gonzalez/rfc-1081-proxy
jandro996 Mar 9, 2026
afab00f
wip
jandro996 Mar 9, 2026
6e1f25a
wip
jandro996 Mar 9, 2026
bcbabe8
remove full_trace=True from interfaces.library.validate_one_span as j…
jandro996 Mar 9, 2026
2409f2c
wip
jandro996 Mar 9, 2026
4b29aff
wip
jandro996 Mar 9, 2026
237d391
Add endpoints and enable for all weblogs
jandro996 Mar 10, 2026
b17e75e
Fix test for v1
jandro996 Mar 10, 2026
e74ee0f
Merge branch 'main' into alejandro.gonzalez/rfc-1081-proxy
jandro996 Mar 10, 2026
ae0d169
2- Fix test for v1
jandro996 Mar 10, 2026
2c2db8d
Merge branch 'main' into alejandro.gonzalez/rfc-1081-proxy
jandro996 Mar 11, 2026
51f8d10
Merge branch 'main' into alejandro.gonzalez/rfc-1081-proxy
jandro996 Mar 11, 2026
b659d8a
Merge branch 'main' into alejandro.gonzalez/rfc-1081-proxy
jandro996 Mar 11, 2026
dbcd68b
Merge branch 'main' into alejandro.gonzalez/rfc-1081-proxy
jandro996 Mar 12, 2026
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
16 changes: 11 additions & 5 deletions manifests/java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2227,7 +2227,10 @@ manifest:
- weblog_declaration:
"*": v1.48.0
spring-boot-3-native: irrelevant (GraalVM. Tracing support only)
tests/appsec/test_inferred_spans.py::Test_Proxy_Inferred_Span_Tags: missing_feature
tests/appsec/test_inferred_spans.py::Test_Proxy_Inferred_Span_Tags:
- weblog_declaration:
"*": v1.61.0-SNAPSHOT
spring-boot-3-native: irrelevant (GraalVM. Tracing support only)
tests/appsec/test_ip_blocking_full_denylist.py::Test_AppSecIPBlockingFullDenylist:
- weblog_declaration:
"*": v0.111.0
Expand Down Expand Up @@ -3427,16 +3430,19 @@ manifest:
tests/integrations/test_inferred_proxy.py::Test_AWS_API_Gateway_Inferred_Span_Creation:
- weblog_declaration:
"*": irrelevant
spring-boot: v1.56.0-SNAPSHOT
spring-boot: ">=1.56.0 <1.61.0-SNAPSHOT"
tests/integrations/test_inferred_proxy.py::Test_AWS_API_Gateway_Inferred_Span_Creation_With_Distributed_Context:
- weblog_declaration:
"*": irrelevant
spring-boot: v1.56.0-SNAPSHOT
spring-boot: ">=1.56.0 <1.61.0-SNAPSHOT"
tests/integrations/test_inferred_proxy.py::Test_AWS_API_Gateway_Inferred_Span_Creation_With_Error:
- weblog_declaration:
"*": irrelevant
spring-boot: v1.56.0-SNAPSHOT
tests/integrations/test_inferred_proxy.py::Test_AWS_API_Gateway_Inferred_Span_Creation_v2: missing_feature
spring-boot: ">=1.56.0 <1.61.0-SNAPSHOT"
tests/integrations/test_inferred_proxy.py::Test_AWS_API_Gateway_Inferred_Span_Creation_v2:
- weblog_declaration:
"*": v1.61.0-SNAPSHOT
spring-boot-3-native: irrelevant (GraalVM. Tracing support only)
tests/integrations/test_mongo.py::Test_Mongo: bug (APMAPI-729)
tests/integrations/test_otel_drop_in.py::Test_Otel_Drop_In:
- weblog_declaration:
Expand Down
3 changes: 2 additions & 1 deletion tests/appsec/test_inferred_spans.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ def setup_proxy_inferred_span(self) -> None:

def test_proxy_inferred_span(self) -> None:
service_entry_span_appsec_data = None
for _, _, span, appsec_data in interfaces.library.get_appsec_events(self.r):
for _, _, span, appsec_data in interfaces.library.get_appsec_events(self.r, full_trace=True):
if span.get("service") == "weblog":
service_entry_span_appsec_data = appsec_data
break

assert service_entry_span_appsec_data, "Expected non empty appsec data on the weblog entry span"

Expand Down
15 changes: 8 additions & 7 deletions tests/integrations/test_inferred_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,17 @@ def assert_api_gateway_span(
assert "http.method" in span["meta"], "Inferred AWS API Gateway span meta should contain 'http.method'"
assert span["meta"]["http.method"] == "GET", "Inferred AWS API Gateway span meta expected HTTP method to be 'GET'"

# Skip http.url and http.status_code assertions for Java (language='jvm') - these fields are not properly set
# http.url expected value in this test lacks the https:// scheme that Java correctly sets;
# the v2 tests (mandatory_tags_validator_factory) validate the full URL for all languages.
is_java = span["meta"].get("language") == "jvm" or span["meta"].get("language") == "java"
if not is_java:
assert "http.url" in span["meta"], "Inferred AWS API Gateway span eta should contain 'http.url'"
assert "http.url" in span["meta"], "Inferred AWS API Gateway span meta should contain 'http.url'"
assert span["meta"]["http.url"] == "system-tests-api-gateway.com" + path, (
f"Inferred AWS API Gateway span meta expected HTTP URL to be 'system-tests-api-gateway.com{path}'"
)
assert "http.status_code" in span["meta"], "Inferred AWS API Gateway span eta should contain 'http.status_code'"
# http.status_code is only guaranteed in newer tracer versions; skip if absent to stay compatible
# with older releases. The v2 tests (mandatory_tags_validator_factory) strictly validate this tag.
if "http.status_code" in span["meta"]:
assert span["meta"]["http.status_code"] == status_code, (
f"Inferred AWS API Gateway span meta expected HTTP Status Code of '{status_code}'"
)
Expand All @@ -198,7 +201,7 @@ def assert_api_gateway_span(
assert span["parent_id"] == DISTRIBUTED_PARENT_ID
assert span["metrics"]["_sampling_priority_v1"] == DISTRIBUTED_SAMPLING_PRIORITY

if is_error and not is_java:
if is_error and "http.status_code" in span["meta"]:
assert span["error"] == 1
assert span["meta"]["http.status_code"] == "500"

Expand Down Expand Up @@ -320,9 +323,7 @@ def validate_api_gateway_span(span: DataDogLibrarySpan) -> bool:
if region != "eu-west-3":
raise ValueError(f"Expected 'region' tag to be 'eu-west-3', found '{region}'")

user = meta.get("aws_user")
if user != "aws-user":
raise ValueError(f"Expected 'aws_user' tag to be 'aws-user', found '{user}'")
# Note: aws_user is NOT validated - RFC states it should not be implemented without explicit approval (PII concerns)

dd_resource_key = meta.get("dd_resource_key")
if dd_resource_key != expected_arn:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.datadoghq.akka_http

import akka.http.scaladsl.model.{ContentTypes, HttpEntity, HttpResponse, StatusCodes}
import akka.http.scaladsl.model.{ContentTypes, HttpEntity, HttpResponse, StatusCode, StatusCodes}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route

Expand All @@ -26,6 +26,24 @@ object MainRoutes {
)
)
}
} ~
pathPrefix("inferred-proxy") {
path("span-creation") {
get {
parameters("status_code".?) { statusCodeParam =>
extractRequest { req =>
println("Received an API Gateway request:")
req.headers.foreach(h => println(s"${h.name}: ${h.value}"))
val code = statusCodeParam match {
case Some(s) => try { s.toInt } catch { case _: NumberFormatException => 400 }
case None => 200
}
val status: StatusCode = StatusCodes.getForKey(code).getOrElse(StatusCodes.custom(code, "Custom", "Custom"))
complete(HttpResponse(status = status, entity = HttpEntity(ContentTypes.`text/plain(UTF-8)`, "ok")))
}
}
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,23 @@ public String toString() {
}
}

@GET
@Path("/inferred-proxy/span-creation")
public Response inferredProxySpanCreation(@QueryParam("status_code") String statusCodeParam, @Context HttpHeaders headers) {
int statusCode = 200;
if (statusCodeParam != null && !statusCodeParam.isEmpty()) {
try {
statusCode = Integer.parseInt(statusCodeParam);
} catch (NumberFormatException e) {
statusCode = 400;
}
}
System.out.println("Received an API Gateway request:");
headers.getRequestHeaders().forEach((name, values) ->
values.forEach(value -> System.out.println(name + ": " + value)));
return Response.status(statusCode).entity("ok").build();
}

@GET
@Path("/make_distant_call")
public DistantCallResponse make_distant_call(@QueryParam("url") String url) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ class AppSecController @Inject()(cc: MessagesControllerComponents, ws: WSClient,
}


def inferredProxySpanCreation(status_code: Option[Int]) = Action { request =>
println("Received an API Gateway request:")
request.headers.headers.foreach { case (name, value) => println(s"$name: $value") }
Results.Status(status_code.getOrElse(200))("ok")
}

def tagValue(value: String, code: Int) = Action { request =>
handleTagValue(value, code, request.queryString, None)
}
Expand Down
1 change: 1 addition & 0 deletions utils/build/docker/java/play/conf/routes
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ GET /user_login_failure_event controllers.AppSecController.loginFailure(event_u
GET /custom_event controllers.AppSecController.customEvent(event_name: Option[String])
POST /user_login_success_event_v2 controllers.AppSecController.loginSuccessV2
POST /user_login_failure_event_v2 controllers.AppSecController.loginFailureV2
GET /inferred-proxy/span-creation controllers.AppSecController.inferredProxySpanCreation(status_code: Option[Int])
GET /rasp/sqli controllers.RaspController.sqli
POST /rasp/sqli controllers.RaspController.sqli
GET /rasp/lfi controllers.RaspController.lfi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,21 @@ public static void main(String[] args) throws Exception {
setRootSpanTag("service", serviceName);
ctx.getResponse().send("ok");
})
.get("inferred-proxy/span-creation", ctx -> {
String statusCodeParam = ctx.getRequest().getQueryParams().get("status_code");
int statusCode = 200;
if (statusCodeParam != null && !statusCodeParam.isEmpty()) {
try {
statusCode = Integer.parseInt(statusCodeParam);
} catch (NumberFormatException e) {
statusCode = 400;
}
}
System.out.println("Received an API Gateway request:");
Headers headers = ctx.getRequest().getHeaders();
headers.getNames().forEach(name -> System.out.println(name + ": " + headers.get(name)));
ctx.getResponse().status(statusCode).send("text/plain", "ok");
})
.get("set_cookie", ctx -> {
final String name = ctx.getRequest().getQueryParams().get("name");
final String value = ctx.getRequest().getQueryParams().get("value");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,23 @@ public String toString() {
}
}

@GET
@Path("/inferred-proxy/span-creation")
public Response inferredProxySpanCreation(@QueryParam("status_code") String statusCodeParam, @Context HttpHeaders headers) {
int statusCode = 200;
if (statusCodeParam != null && !statusCodeParam.isEmpty()) {
try {
statusCode = Integer.parseInt(statusCodeParam);
} catch (NumberFormatException e) {
statusCode = 400;
}
}
System.out.println("Received an API Gateway request:");
headers.getRequestHeaders().forEach((name, values) ->
values.forEach(value -> System.out.println(name + ": " + value)));
return Response.status(statusCode).entity("ok").build();
}

@GET
@Path("/make_distant_call")
public DistantCallResponse make_distant_call(@QueryParam("url") String url) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,22 @@ public void onResponse(Call call, Response response) throws IOException {
ctx.request().headers().forEach(header -> headersJson.put(header.getKey(), header.getValue()));
ctx.response().end(headersJson.encode());
});
router.get("/inferred-proxy/span-creation")
.handler(ctx -> {
String statusCodeParam = ctx.request().getParam("status_code");
int statusCode = 200;
if (statusCodeParam != null && !statusCodeParam.isEmpty()) {
try {
statusCode = Integer.parseInt(statusCodeParam);
} catch (NumberFormatException e) {
statusCode = 400;
}
}
System.out.println("Received an API Gateway request:");
ctx.request().headers().forEach(header ->
System.out.println(header.getKey() + ": " + header.getValue()));
ctx.response().setStatusCode(statusCode).end("ok");
});
router.get("/set_cookie")
.handler(ctx -> {
String name = ctx.request().getParam("name");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,22 @@ public void onResponse(Call call, Response response) throws IOException {
ctx.request().headers().forEach(header -> headersJson.put(header.getKey(), header.getValue()));
ctx.response().end(headersJson.encode());
});
router.get("/inferred-proxy/span-creation")
.handler(ctx -> {
String statusCodeParam = ctx.request().getParam("status_code");
int statusCode = 200;
if (statusCodeParam != null && !statusCodeParam.isEmpty()) {
try {
statusCode = Integer.parseInt(statusCodeParam);
} catch (NumberFormatException e) {
statusCode = 400;
}
}
System.out.println("Received an API Gateway request:");
ctx.request().headers().forEach(header ->
System.out.println(header.getKey() + ": " + header.getValue()));
ctx.response().setStatusCode(statusCode).end("ok");
});
router.get("/set_cookie")
.handler(ctx -> {
String name = ctx.request().getParam("name");
Expand Down
Loading