Skip to content

Commit 686764c

Browse files
Extract Ratpack json body response schemas
1 parent 135e0f0 commit 686764c

File tree

11 files changed

+487
-148
lines changed

11 files changed

+487
-148
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package datadog.trace.instrumentation.ratpack;
2+
3+
import static datadog.trace.api.gateway.Events.EVENTS;
4+
5+
import datadog.appsec.api.blocking.BlockingException;
6+
import datadog.trace.advice.ActiveRequestContext;
7+
import datadog.trace.advice.RequiresRequestContext;
8+
import datadog.trace.api.gateway.BlockResponseFunction;
9+
import datadog.trace.api.gateway.CallbackProvider;
10+
import datadog.trace.api.gateway.Flow;
11+
import datadog.trace.api.gateway.RequestContext;
12+
import datadog.trace.api.gateway.RequestContextSlot;
13+
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
14+
import java.util.function.BiFunction;
15+
import net.bytebuddy.asm.Advice;
16+
import ratpack.jackson.JsonRender;
17+
18+
@RequiresRequestContext(RequestContextSlot.APPSEC)
19+
public class JsonRendererAdvice {
20+
21+
// for now ignore that the parser can be configured to mix in the query string
22+
@Advice.OnMethodEnter(suppress = Throwable.class)
23+
static void after(
24+
@Advice.Argument(1) final JsonRender render,
25+
@ActiveRequestContext final RequestContext reqCtx) {
26+
Object obj = render == null ? null : render.getObject();
27+
if (obj == null) {
28+
return;
29+
}
30+
31+
CallbackProvider cbp = AgentTracer.get().getCallbackProvider(RequestContextSlot.APPSEC);
32+
BiFunction<RequestContext, Object, Flow<Void>> callback =
33+
cbp.getCallback(EVENTS.responseBody());
34+
if (callback == null) {
35+
return;
36+
}
37+
Flow<Void> flow = callback.apply(reqCtx, obj);
38+
Flow.Action action = flow.getAction();
39+
if (action instanceof Flow.Action.RequestBlockingAction) {
40+
BlockResponseFunction brf = reqCtx.getBlockResponseFunction();
41+
if (brf != null) {
42+
Flow.Action.RequestBlockingAction rba = (Flow.Action.RequestBlockingAction) action;
43+
brf.tryCommitBlockingResponse(
44+
reqCtx.getTraceSegment(),
45+
rba.getStatusCode(),
46+
rba.getBlockingContentType(),
47+
rba.getExtraHeaders());
48+
49+
throw new BlockingException("Blocked request (for JsonRenderer/render)");
50+
}
51+
}
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package datadog.trace.instrumentation.ratpack;
2+
3+
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
4+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
5+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
6+
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
7+
8+
import com.google.auto.service.AutoService;
9+
import datadog.trace.agent.tooling.Instrumenter;
10+
import datadog.trace.agent.tooling.InstrumenterModule;
11+
import datadog.trace.agent.tooling.muzzle.Reference;
12+
13+
@AutoService(InstrumenterModule.class)
14+
public class JsonRendererInstrumentation extends InstrumenterModule.AppSec
15+
implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice {
16+
17+
// so it doesn't apply to ratpack < 1.5
18+
private static final Reference FILE_IO = new Reference.Builder("ratpack.file.FileIo").build();
19+
20+
public JsonRendererInstrumentation() {
21+
super("ratpack");
22+
}
23+
24+
@Override
25+
public String instrumentedType() {
26+
return "ratpack.jackson.internal.JsonRenderer";
27+
}
28+
29+
@Override
30+
public Reference[] additionalMuzzleReferences() {
31+
return new Reference[] {FILE_IO};
32+
}
33+
34+
@Override
35+
public String muzzleDirective() {
36+
return "ratpack-json";
37+
}
38+
39+
@Override
40+
public void methodAdvice(MethodTransformer transformer) {
41+
transformer.applyAdvice(
42+
isMethod()
43+
.and(named("render"))
44+
.and(takesArguments(2))
45+
.and(takesArgument(0, named("ratpack.handling.Context")))
46+
.and(takesArgument(1, named("ratpack.jackson.JsonRender"))),
47+
packageName + ".JsonRendererAdvice");
48+
}
49+
}

dd-java-agent/instrumentation/ratpack-1.5/src/test/groovy/server/RatpackAsyncHttpServerTest.groovy

Lines changed: 95 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import ratpack.exec.Promise
1010
import ratpack.form.Form
1111
import ratpack.groovy.test.embed.GroovyEmbeddedApp
1212
import ratpack.handling.HandlerDecorator
13+
import static ratpack.jackson.Jackson.json
1314

1415
import java.nio.charset.StandardCharsets
1516

@@ -33,34 +34,36 @@ class RatpackAsyncHttpServerTest extends RatpackHttpServerTest {
3334
@Override
3435
HttpServer server() {
3536
return new RatpackServer(GroovyEmbeddedApp.ratpack {
36-
serverConfig {
37-
port 0
38-
address InetAddress.getByName('localhost')
39-
}
40-
bindings {
41-
bind TestErrorHandler
42-
multiBindInstance(HandlerDecorator, HandlerDecorator.prepend(new ResponseHeaderDecorator()))
43-
}
44-
handlers {
45-
prefix(SUCCESS.relativeRawPath()) {
46-
all {
47-
Promise.sync {
48-
SUCCESS
49-
} then { HttpServerTest.ServerEndpoint endpoint ->
50-
controller(endpoint) {
51-
context.response.status(endpoint.status).send(endpoint.body)
37+
serverConfig {
38+
port 0
39+
address InetAddress.getByName('localhost')
40+
}
41+
bindings {
42+
bind TestErrorHandler
43+
multiBindInstance(HandlerDecorator, HandlerDecorator.prepend(new ResponseHeaderDecorator()))
44+
}
45+
handlers {
46+
prefix(SUCCESS.relativeRawPath()) {
47+
all {
48+
Promise.sync {
49+
SUCCESS
50+
} then {
51+
HttpServerTest.ServerEndpoint endpoint ->
52+
controller(endpoint) {
53+
context.response.status(endpoint.status).send(endpoint.body)
54+
}
5255
}
5356
}
5457
}
55-
}
56-
prefix(CREATED.relativeRawPath()) {
57-
all {
58-
Promise.sync {
59-
CREATED
60-
} then {endpoint ->
61-
controller(endpoint) {
62-
def outerDelegate = delegate
63-
request.bodyStream.subscribe(new Subscriber<ByteBuf>() {
58+
prefix(CREATED.relativeRawPath()) {
59+
all {
60+
Promise.sync {
61+
CREATED
62+
} then {
63+
endpoint ->
64+
controller(endpoint) {
65+
def outerDelegate = delegate
66+
request.bodyStream.subscribe(new Subscriber<ByteBuf>() {
6467
Subscription sub
6568
String res = ''
6669

@@ -72,9 +75,10 @@ class RatpackAsyncHttpServerTest extends RatpackHttpServerTest {
7275

7376
@Override
7477
void onNext(ByteBuf byteBuf) {
75-
Promise.async {downstream ->
78+
Promise.async {
79+
downstream ->
7680
CharSequence sequence =
77-
byteBuf.readCharSequence(byteBuf.readableBytes(), StandardCharsets.UTF_8)
81+
byteBuf.readCharSequence(byteBuf.readableBytes(), StandardCharsets.UTF_8)
7882
res += sequence
7983
downstream.success(sequence)
8084
} then {
@@ -91,58 +95,71 @@ class RatpackAsyncHttpServerTest extends RatpackHttpServerTest {
9195
@Override
9296
void onComplete() {
9397
outerDelegate.response.status(endpoint.status)
94-
.send('text/plain', "${endpoint.body}: $res")
98+
.send('text/plain', "${endpoint.body}: $res")
9599
}
96100
})
101+
}
97102
}
98103
}
99104
}
100-
}
101-
prefix(FORWARDED.relativeRawPath()) {
102-
all {
103-
Promise.sync {
104-
FORWARDED
105-
} then { HttpServerTest.ServerEndpoint endpoint ->
106-
controller(endpoint) {
107-
context.response.status(endpoint.status).send(request.headers.get("x-forwarded-for"))
105+
prefix(FORWARDED.relativeRawPath()) {
106+
all {
107+
Promise.sync {
108+
FORWARDED
109+
} then {
110+
HttpServerTest.ServerEndpoint endpoint ->
111+
controller(endpoint) {
112+
context.response.status(endpoint.status).send(request.headers.get("x-forwarded-for"))
113+
}
108114
}
109115
}
110116
}
111-
}
112-
get('path/:id/param') {
113-
Promise.sync {
114-
PATH_PARAM
115-
} then {endpoint ->
116-
controller(endpoint) {
117-
context.response.status(PATH_PARAM.status).send('text/plain', context.pathTokens['id'])
117+
get('path/:id/param') {
118+
Promise.sync {
119+
PATH_PARAM
120+
} then {
121+
endpoint ->
122+
controller(endpoint) {
123+
context.response.status(PATH_PARAM.status).send('text/plain', context.pathTokens['id'])
124+
}
118125
}
119126
}
120-
}
121-
[BODY_URLENCODED, BODY_MULTIPART].each { endpoint ->
122-
prefix(endpoint.relativeRawPath()) {
123-
all {
124-
Promise.sync {
125-
endpoint
126-
} then { ep ->
127-
controller(endpoint) {
128-
context.parse(Form).then { form ->
129-
def text = form.findAll { it.key != 'ignore' }
130-
.collectEntries { [it.key, it.value as List] } as String
131-
response.status(endpoint.status).send('text/plain', text)
127+
[BODY_URLENCODED, BODY_MULTIPART].each {
128+
endpoint ->
129+
prefix(endpoint.relativeRawPath()) {
130+
all {
131+
Promise.sync {
132+
endpoint
133+
} then {
134+
ep ->
135+
controller(endpoint) {
136+
context.parse(Form).then {
137+
form ->
138+
def text = form.findAll {
139+
it.key != 'ignore'
140+
}
141+
.collectEntries {
142+
[it.key, it.value as List]
143+
} as String
144+
response.status(endpoint.status).send('text/plain', text)
145+
}
132146
}
133147
}
134148
}
135149
}
136150
}
137-
}
138-
prefix(BODY_JSON.relativeRawPath()) {
139-
all {
140-
Promise.sync {
141-
BODY_JSON
142-
} then {endpoint ->
143-
controller(endpoint) {
144-
context.parse(Map).then { map ->
145-
response.status(BODY_JSON.status).send('text/plain', "{\"a\":\"${map['a']}\"}")
151+
prefix(BODY_JSON.relativeRawPath()) {
152+
all {
153+
Promise.sync {
154+
BODY_JSON
155+
} then {
156+
endpoint ->
157+
controller(endpoint) {
158+
context.parse(Map).then {
159+
map -> {
160+
response.status(BODY_JSON.status)
161+
context.render(json(map))
162+
}
146163
}
147164
}
148165
}
@@ -152,7 +169,8 @@ class RatpackAsyncHttpServerTest extends RatpackHttpServerTest {
152169
all {
153170
Promise.sync {
154171
QUERY_ENCODED_BOTH
155-
} then { HttpServerTest.ServerEndpoint endpoint ->
172+
} then {
173+
HttpServerTest.ServerEndpoint endpoint ->
156174
controller(endpoint) {
157175
context.response.status(endpoint.status).send(endpoint.bodyForQuery(request.query))
158176
}
@@ -163,7 +181,8 @@ class RatpackAsyncHttpServerTest extends RatpackHttpServerTest {
163181
all {
164182
Promise.sync {
165183
QUERY_ENCODED_QUERY
166-
} then { HttpServerTest.ServerEndpoint endpoint ->
184+
} then {
185+
HttpServerTest.ServerEndpoint endpoint ->
167186
controller(endpoint) {
168187
context.response.status(endpoint.status).send(endpoint.bodyForQuery(request.query))
169188
}
@@ -174,7 +193,8 @@ class RatpackAsyncHttpServerTest extends RatpackHttpServerTest {
174193
all {
175194
Promise.sync {
176195
QUERY_PARAM
177-
} then { HttpServerTest.ServerEndpoint endpoint ->
196+
} then {
197+
HttpServerTest.ServerEndpoint endpoint ->
178198
controller(endpoint) {
179199
context.response.status(endpoint.status).send(endpoint.bodyForQuery(request.query))
180200
}
@@ -185,7 +205,8 @@ class RatpackAsyncHttpServerTest extends RatpackHttpServerTest {
185205
all {
186206
Promise.sync {
187207
USER_BLOCK
188-
} then { HttpServerTest.ServerEndpoint endpoint ->
208+
} then {
209+
HttpServerTest.ServerEndpoint endpoint ->
189210
controller(endpoint) {
190211
Blocking.forUser('user-to-block').blockIfMatch()
191212
context.response.status(SUCCESS.status).send('should never be reached')
@@ -197,7 +218,8 @@ class RatpackAsyncHttpServerTest extends RatpackHttpServerTest {
197218
all {
198219
Promise.sync {
199220
REDIRECT
200-
} then { HttpServerTest.ServerEndpoint endpoint ->
221+
} then {
222+
HttpServerTest.ServerEndpoint endpoint ->
201223
controller(endpoint) {
202224
context.redirect(endpoint.body)
203225
}
@@ -208,7 +230,8 @@ class RatpackAsyncHttpServerTest extends RatpackHttpServerTest {
208230
all {
209231
Promise.sync {
210232
ERROR
211-
} then { HttpServerTest.ServerEndpoint endpoint ->
233+
} then {
234+
HttpServerTest.ServerEndpoint endpoint ->
212235
controller(endpoint) {
213236
context.response.status(endpoint.status).send(endpoint.body)
214237
}
@@ -219,7 +242,8 @@ class RatpackAsyncHttpServerTest extends RatpackHttpServerTest {
219242
all {
220243
Promise.sync {
221244
EXCEPTION
222-
} then { HttpServerTest.ServerEndpoint endpoint ->
245+
} then {
246+
HttpServerTest.ServerEndpoint endpoint ->
223247
controller(endpoint) {
224248
throw new Exception(endpoint.body)
225249
}

0 commit comments

Comments
 (0)