Skip to content

Commit 8daa014

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

7 files changed

Lines changed: 134 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: 74 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,28 @@ 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+
221+
if (bodyCallback == null) {
222+
continue;
223+
}
224+
213225
akka.http.javadsl.model.HttpEntity.Strict entity = part.getEntity();
214226
if (!(entity instanceof HttpEntity.Strict)) {
215227
continue;
@@ -232,8 +244,27 @@ private static void handleMultipartStrictFormData(
232244
curStrings.add(s);
233245
}
234246

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

239270
public static Unmarshaller<HttpEntity, String> transformStringUnmarshaller(
@@ -389,6 +420,7 @@ public static Unmarshaller<HttpEntity, StrictForm> transformStrictFormUnmarshall
389420
private static void handleStrictFormData(StrictForm sf) {
390421
Iterator<Tuple2<String, StrictForm.Field>> iterator = sf.fields().iterator();
391422
Map<String, List<String>> conv = new HashMap<>();
423+
List<String> filenames = new ArrayList<>();
392424
while (iterator.hasNext()) {
393425
Tuple2<String, StrictForm.Field> next = iterator.next();
394426
String fieldName = next._1();
@@ -413,9 +445,13 @@ private static void handleStrictFormData(StrictForm sf) {
413445
strings.add((String) strictFieldValue);
414446
} else if (strictFieldValue
415447
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();
448+
akka.http.scaladsl.model.Multipart$FormData$BodyPart$Strict bodyPart =
449+
(akka.http.scaladsl.model.Multipart$FormData$BodyPart$Strict) strictFieldValue;
450+
Optional<String> filenameOpt = bodyPart.getFilename();
451+
if (filenameOpt.isPresent() && !filenameOpt.get().isEmpty()) {
452+
filenames.add(filenameOpt.get());
453+
}
454+
HttpEntity.Strict sentity = bodyPart.entity();
419455
String s =
420456
sentity
421457
.getData()
@@ -426,6 +462,36 @@ private static void handleStrictFormData(StrictForm sf) {
426462
}
427463

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

431497
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)