Skip to content

Commit 2848d37

Browse files
committed
Add server.request.body.filenames support for Akka HTTP
1 parent 081af53 commit 2848d37

7 files changed

Lines changed: 130 additions & 8 deletions

File tree

dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/baseTest/groovy/AkkaHttpServerInstrumentationTest.groovy

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ abstract class AkkaHttpServerInstrumentationTest extends HttpServerTest<AkkaHttp
7474
true
7575
}
7676

77+
@Override
78+
boolean testBodyFilenames() {
79+
true
80+
}
81+
7782
@Override
7883
boolean testBodyJson() {
7984
true
@@ -223,6 +228,11 @@ abstract class AkkaHttpServerInstrumentationSyncTest extends AkkaHttpServerInstr
223228
false
224229
}
225230

231+
@Override
232+
boolean testBodyFilenames() {
233+
false
234+
}
235+
226236
@Override
227237
boolean testBodyJson() {
228238
false
@@ -283,6 +293,11 @@ class AkkaHttpServerInstrumentationBindAndHandleTest extends AkkaHttpServerInstr
283293
akkaHttpVersion != '10.0.10'
284294
}
285295

296+
@Override
297+
boolean testBodyFilenames() {
298+
akkaHttpVersion != '10.0.10'
299+
}
300+
286301
@Override
287302
boolean testBodyUrlencoded() {
288303
akkaHttpVersion != '10.0.10'
@@ -309,6 +324,11 @@ class AkkaHttpServerInstrumentationBindAndHandleAsyncWithRouteAsyncHandlerTest e
309324
akkaHttpVersion != '10.0.10'
310325
}
311326

327+
@Override
328+
boolean testBodyFilenames() {
329+
akkaHttpVersion != '10.0.10'
330+
}
331+
312332
@Override
313333
boolean testBodyUrlencoded() {
314334
akkaHttpVersion != '10.0.10'

dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/latestDepTest/groovy/AkkaHttp102ServerInstrumentationTests.groovy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ class AkkaHttp102ServerInstrumentationBindSyncTest extends AkkaHttpServerInstrum
3636
false
3737
}
3838

39+
@Override
40+
boolean testBodyFilenames() {
41+
false
42+
}
43+
3944
@Override
4045
boolean testBodyJson() {
4146
false

dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/main/java/datadog/trace/instrumentation/akkahttp/appsec/UnmarshallerHelpers.java

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.HashMap;
2727
import java.util.List;
2828
import java.util.Map;
29+
import java.util.Optional;
2930
import java.util.WeakHashMap;
3031
import java.util.function.BiFunction;
3132
import org.slf4j.Logger;
@@ -199,17 +200,24 @@ private static void handleMultipartStrictFormData(
199200
}
200201

201202
CallbackProvider cbp = AgentTracer.get().getCallbackProvider(RequestContextSlot.APPSEC);
202-
BiFunction<RequestContext, Object, Flow<Void>> callback =
203+
BiFunction<RequestContext, Object, Flow<Void>> bodyCallback =
203204
cbp.getCallback(EVENTS.requestBodyProcessed());
204-
if (callback == null) {
205+
BiFunction<RequestContext, List<String>, Flow<Void>> filenamesCallback =
206+
cbp.getCallback(EVENTS.requestFilesFilenames());
207+
if (bodyCallback == null && filenamesCallback == null) {
205208
return;
206209
}
207210

208-
// conversion to map string -> list of string
209211
java.lang.Iterable<akka.http.javadsl.model.Multipart.FormData.BodyPart.Strict> strictParts =
210212
st.getStrictParts();
211213
Map<String, List<String>> conv = new HashMap<>();
214+
List<String> filenames = new ArrayList<>();
212215
for (akka.http.javadsl.model.Multipart.FormData.BodyPart.Strict part : strictParts) {
216+
Optional<String> filenameOpt = part.getFilename();
217+
if (filenameOpt.isPresent() && !filenameOpt.get().isEmpty()) {
218+
filenames.add(filenameOpt.get());
219+
}
220+
213221
akka.http.javadsl.model.HttpEntity.Strict entity = part.getEntity();
214222
if (!(entity instanceof HttpEntity.Strict)) {
215223
continue;
@@ -232,8 +240,27 @@ private static void handleMultipartStrictFormData(
232240
curStrings.add(s);
233241
}
234242

235-
// callback execution
236-
executeCallback(reqCtx, callback, conv, "multipartFormDataUnmarshaller");
243+
if (bodyCallback != null) {
244+
executeCallback(reqCtx, bodyCallback, conv, "multipartFormDataUnmarshaller");
245+
}
246+
247+
if (filenamesCallback != null && !filenames.isEmpty()) {
248+
Flow<Void> filenamesFlow = filenamesCallback.apply(reqCtx, filenames);
249+
Flow.Action filenamesAction = filenamesFlow.getAction();
250+
if (filenamesAction instanceof Flow.Action.RequestBlockingAction) {
251+
Flow.Action.RequestBlockingAction rba = (Flow.Action.RequestBlockingAction) filenamesAction;
252+
BlockResponseFunction brf = reqCtx.getBlockResponseFunction();
253+
if (brf != null) {
254+
boolean success = brf.tryCommitBlockingResponse(reqCtx.getTraceSegment(), rba);
255+
if (success) {
256+
if (brf instanceof AkkaBlockResponseFunction) {
257+
((AkkaBlockResponseFunction) brf).setUnmarshallBlock(true);
258+
}
259+
throw new BlockingException("Blocked request (multipart file upload)");
260+
}
261+
}
262+
}
263+
}
237264
}
238265

239266
public static Unmarshaller<HttpEntity, String> transformStringUnmarshaller(
@@ -389,6 +416,7 @@ public static Unmarshaller<HttpEntity, StrictForm> transformStrictFormUnmarshall
389416
private static void handleStrictFormData(StrictForm sf) {
390417
Iterator<Tuple2<String, StrictForm.Field>> iterator = sf.fields().iterator();
391418
Map<String, List<String>> conv = new HashMap<>();
419+
List<String> filenames = new ArrayList<>();
392420
while (iterator.hasNext()) {
393421
Tuple2<String, StrictForm.Field> next = iterator.next();
394422
String fieldName = next._1();
@@ -413,9 +441,13 @@ private static void handleStrictFormData(StrictForm sf) {
413441
strings.add((String) strictFieldValue);
414442
} else if (strictFieldValue
415443
instanceof akka.http.scaladsl.model.Multipart$FormData$BodyPart$Strict) {
416-
HttpEntity.Strict sentity =
417-
((akka.http.scaladsl.model.Multipart$FormData$BodyPart$Strict) strictFieldValue)
418-
.entity();
444+
akka.http.scaladsl.model.Multipart$FormData$BodyPart$Strict bodyPart =
445+
(akka.http.scaladsl.model.Multipart$FormData$BodyPart$Strict) strictFieldValue;
446+
Optional<String> filenameOpt = bodyPart.getFilename();
447+
if (filenameOpt.isPresent() && !filenameOpt.get().isEmpty()) {
448+
filenames.add(filenameOpt.get());
449+
}
450+
HttpEntity.Strict sentity = bodyPart.entity();
419451
String s =
420452
sentity
421453
.getData()
@@ -426,6 +458,36 @@ private static void handleStrictFormData(StrictForm sf) {
426458
}
427459

428460
handleArbitraryPostData(conv, "HttpEntity -> StrictForm unmarshaller");
461+
462+
if (!filenames.isEmpty()) {
463+
AgentSpan span = activeSpan();
464+
RequestContext reqCtx;
465+
if (span != null
466+
&& (reqCtx = span.getRequestContext()) != null
467+
&& reqCtx.getData(RequestContextSlot.APPSEC) != null) {
468+
CallbackProvider cbp = AgentTracer.get().getCallbackProvider(RequestContextSlot.APPSEC);
469+
BiFunction<RequestContext, List<String>, Flow<Void>> filenamesCb =
470+
cbp.getCallback(EVENTS.requestFilesFilenames());
471+
if (filenamesCb != null) {
472+
Flow<Void> filenamesFlow = filenamesCb.apply(reqCtx, filenames);
473+
Flow.Action filenamesAction = filenamesFlow.getAction();
474+
if (filenamesAction instanceof Flow.Action.RequestBlockingAction) {
475+
Flow.Action.RequestBlockingAction rba =
476+
(Flow.Action.RequestBlockingAction) filenamesAction;
477+
BlockResponseFunction brf = reqCtx.getBlockResponseFunction();
478+
if (brf != null) {
479+
boolean success = brf.tryCommitBlockingResponse(reqCtx.getTraceSegment(), rba);
480+
if (success) {
481+
if (brf instanceof AkkaBlockResponseFunction) {
482+
((AkkaBlockResponseFunction) brf).setUnmarshallBlock(true);
483+
}
484+
throw new BlockingException("Blocked request (multipart file upload)");
485+
}
486+
}
487+
}
488+
}
489+
}
490+
}
429491
}
430492

431493
private static Object tryConvertingScalaContainers(Object obj, int depth) {

dd-java-agent/instrumentation/akka/akka-http/akka-http-10.6/src/test/groovy/AkkaHttp102ServerInstrumentationTests.groovy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ class AkkaHttp102ServerInstrumentationBindSyncTest extends AkkaHttpServerInstrum
3636
false
3737
}
3838

39+
@Override
40+
boolean testBodyFilenames() {
41+
false
42+
}
43+
3944
@Override
4045
boolean testBodyJson() {
4146
false

dd-java-agent/instrumentation/akka/akka-http/akka-http-10.6/src/test/groovy/AkkaHttpServerInstrumentationTest.groovy

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ abstract class AkkaHttpServerInstrumentationTest extends HttpServerTest<AkkaHttp
7575
true
7676
}
7777

78+
@Override
79+
boolean testBodyFilenames() {
80+
true
81+
}
82+
7883
@Override
7984
boolean testBodyJson() {
8085
true
@@ -223,6 +228,11 @@ abstract class AkkaHttpServerInstrumentationSyncTest extends AkkaHttpServerInstr
223228
false
224229
}
225230

231+
@Override
232+
boolean testBodyFilenames() {
233+
false
234+
}
235+
226236
@Override
227237
boolean testBodyJson() {
228238
false
@@ -279,6 +289,11 @@ class AkkaHttpServerInstrumentationBindAndHandleTest extends AkkaHttpServerInstr
279289
true
280290
}
281291

292+
@Override
293+
boolean testBodyFilenames() {
294+
true
295+
}
296+
282297
@Override
283298
boolean testBodyUrlencoded() {
284299
true
@@ -305,6 +320,11 @@ class AkkaHttpServerInstrumentationBindAndHandleAsyncWithRouteAsyncHandlerTest e
305320
true
306321
}
307322

323+
@Override
324+
boolean testBodyFilenames() {
325+
true
326+
}
327+
308328
@Override
309329
boolean testBodyUrlencoded() {
310330
true

dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/latestDepTest/groovy/test/boot/SpringBootBasedTest.groovy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ class SpringBootBasedTest extends HttpServerTest<ConfigurableApplicationContext>
109109
true
110110
}
111111

112+
@Override
113+
boolean testBodyFilenames() {
114+
true
115+
}
116+
112117
@Override
113118
boolean testBodyJson() {
114119
true

dd-java-agent/instrumentation/spring/spring-webmvc/spring-webmvc-3.1/src/test/groovy/test/boot/SpringBootBasedTest.groovy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ class SpringBootBasedTest extends HttpServerTest<ConfigurableApplicationContext>
119119
true
120120
}
121121

122+
@Override
123+
boolean testBodyFilenames() {
124+
true
125+
}
126+
122127
@Override
123128
boolean testBodyJson() {
124129
true

0 commit comments

Comments
 (0)