Skip to content

Commit 29d165a

Browse files
authored
Reuse servlet31 advice in sparkweb (#84)
* Reuse servlet31 advice in sparkweb Signed-off-by: Pavol Loffay <[email protected]> * remove author Signed-off-by: Pavol Loffay <[email protected]>
1 parent 690a8fa commit 29d165a

File tree

3 files changed

+145
-210
lines changed

3 files changed

+145
-210
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* Copyright The Hypertrace Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.opentelemetry.instrumentation.hypertrace.servlet.v3_1;
18+
19+
import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3HttpServerTracer.TRACER;
20+
21+
import io.opentelemetry.instrumentation.hypertrace.servlet.common.ServletSpanDecorator;
22+
import io.opentelemetry.trace.Span;
23+
import java.util.Enumeration;
24+
import java.util.HashMap;
25+
import java.util.Map;
26+
import java.util.concurrent.atomic.AtomicBoolean;
27+
import javax.servlet.ServletRequest;
28+
import javax.servlet.ServletResponse;
29+
import javax.servlet.http.HttpServletRequest;
30+
import javax.servlet.http.HttpServletResponse;
31+
import net.bytebuddy.asm.Advice;
32+
import org.hypertrace.agent.blocking.BlockingProvider;
33+
import org.hypertrace.agent.blocking.BlockingResult;
34+
import org.hypertrace.agent.core.DynamicConfig;
35+
import org.hypertrace.agent.core.HypertraceSemanticAttributes;
36+
37+
public class Servlet31Advice {
38+
39+
// request attribute key injected at first filerChain.doFilter
40+
private static final String ALREADY_LOADED = "__org.hypertrace.agent.on_start_executed";
41+
42+
@Advice.OnMethodEnter(suppress = Throwable.class, skipOn = BlockingResult.class)
43+
public static Object start(
44+
@Advice.Argument(value = 0, readOnly = false) ServletRequest request,
45+
@Advice.Argument(value = 1, readOnly = false) ServletResponse response,
46+
@Advice.Local("rootStart") Boolean rootStart) {
47+
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
48+
return null;
49+
}
50+
51+
if (!DynamicConfig.isEnabled(InstrumentationName.INSTRUMENTATION_NAME)) {
52+
return null;
53+
}
54+
55+
// TODO run on every doFilter and check if user removed wrapper
56+
// TODO what if user unwraps request and reads the body?
57+
58+
// run the instrumentation only for the root FilterChain.doFilter()
59+
if (request.getAttribute(ALREADY_LOADED) != null) {
60+
return null;
61+
}
62+
request.setAttribute(ALREADY_LOADED, true);
63+
64+
HttpServletRequest httpRequest = (HttpServletRequest) request;
65+
HttpServletResponse httpResponse = (HttpServletResponse) response;
66+
Span currentSpan = TRACER.getCurrentSpan();
67+
68+
rootStart = true;
69+
response = new BufferingHttpServletResponse(httpResponse);
70+
request = new BufferingHttpServletRequest(httpRequest, (HttpServletResponse) response);
71+
72+
ServletSpanDecorator.addSessionId(currentSpan, httpRequest);
73+
74+
// set request headers
75+
Enumeration<String> headerNames = httpRequest.getHeaderNames();
76+
Map<String, String> headers = new HashMap<>();
77+
while (headerNames.hasMoreElements()) {
78+
String headerName = headerNames.nextElement();
79+
String headerValue = httpRequest.getHeader(headerName);
80+
currentSpan.setAttribute(
81+
HypertraceSemanticAttributes.httpRequestHeader(headerName), headerValue);
82+
headers.put(headerName, headerValue);
83+
}
84+
BlockingResult blockingResult = BlockingProvider.getBlockingEvaluator().evaluate(headers);
85+
currentSpan.setAttribute(
86+
HypertraceSemanticAttributes.OPA_RESULT, blockingResult.blockExecution());
87+
if (blockingResult.blockExecution()) {
88+
httpResponse.setStatus(403);
89+
currentSpan.setAttribute(HypertraceSemanticAttributes.OPA_REASON, blockingResult.getReason());
90+
return blockingResult;
91+
}
92+
return null;
93+
}
94+
95+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
96+
public static void stopSpan(
97+
@Advice.Argument(0) ServletRequest request,
98+
@Advice.Argument(1) ServletResponse response,
99+
@Advice.Local("rootStart") Boolean rootStart) {
100+
if (rootStart != null) {
101+
if (!(request instanceof BufferingHttpServletRequest)
102+
|| !(response instanceof BufferingHttpServletResponse)) {
103+
return;
104+
}
105+
106+
request.removeAttribute(ALREADY_LOADED);
107+
Span currentSpan = TRACER.getCurrentSpan();
108+
109+
AtomicBoolean responseHandled = new AtomicBoolean(false);
110+
if (request.isAsyncStarted()) {
111+
try {
112+
request
113+
.getAsyncContext()
114+
.addListener(new BodyCaptureAsyncListener(responseHandled, currentSpan));
115+
} catch (IllegalStateException e) {
116+
// org.eclipse.jetty.server.Request may throw an exception here if request became
117+
// finished after check above. We just ignore that exception and move on.
118+
}
119+
}
120+
121+
if (!request.isAsyncStarted() && responseHandled.compareAndSet(false, true)) {
122+
BufferingHttpServletResponse bufferingResponse = (BufferingHttpServletResponse) response;
123+
BufferingHttpServletRequest bufferingRequest = (BufferingHttpServletRequest) request;
124+
125+
// set response headers
126+
for (String headerName : bufferingResponse.getHeaderNames()) {
127+
String headerValue = bufferingResponse.getHeader(headerName);
128+
currentSpan.setAttribute(
129+
HypertraceSemanticAttributes.httpResponseHeader(headerName), headerValue);
130+
}
131+
// Bodies are captured at the end after all user processing.
132+
currentSpan.setAttribute(
133+
HypertraceSemanticAttributes.HTTP_REQUEST_BODY,
134+
bufferingRequest.getBufferedBodyAsString());
135+
currentSpan.setAttribute(
136+
HypertraceSemanticAttributes.HTTP_RESPONSE_BODY, bufferingResponse.getBufferAsString());
137+
}
138+
}
139+
}
140+
}

instrumentation/servlet/servlet-3.1/src/main/java/io/opentelemetry/instrumentation/hypertrace/servlet/v3_1/Servlet31BodyInstrumentation.java

Lines changed: 2 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package io.opentelemetry.instrumentation.hypertrace.servlet.v3_1;
1818

19-
import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3HttpServerTracer.TRACER;
2019
import static io.opentelemetry.javaagent.tooling.ClassLoaderMatcher.hasClassesNamed;
2120
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.safeHasSuperType;
2221
import static io.opentelemetry.javaagent.tooling.matcher.NameMatchers.namedOneOf;
@@ -26,25 +25,11 @@
2625
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
2726

2827
import com.google.auto.service.AutoService;
29-
import io.opentelemetry.instrumentation.hypertrace.servlet.common.ServletSpanDecorator;
3028
import io.opentelemetry.javaagent.tooling.Instrumenter;
31-
import io.opentelemetry.trace.Span;
32-
import java.util.Enumeration;
33-
import java.util.HashMap;
3429
import java.util.Map;
35-
import java.util.concurrent.atomic.AtomicBoolean;
36-
import javax.servlet.ServletRequest;
37-
import javax.servlet.ServletResponse;
38-
import javax.servlet.http.HttpServletRequest;
39-
import javax.servlet.http.HttpServletResponse;
40-
import net.bytebuddy.asm.Advice;
4130
import net.bytebuddy.description.method.MethodDescription;
4231
import net.bytebuddy.description.type.TypeDescription;
4332
import net.bytebuddy.matcher.ElementMatcher;
44-
import org.hypertrace.agent.blocking.BlockingProvider;
45-
import org.hypertrace.agent.blocking.BlockingResult;
46-
import org.hypertrace.agent.core.DynamicConfig;
47-
import org.hypertrace.agent.core.HypertraceSemanticAttributes;
4833

4934
/**
5035
* TODO https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/1395 is resolved
@@ -105,6 +90,7 @@ public String[] helperClassNames() {
10590
packageName + ".BufferingHttpServletRequest",
10691
packageName + ".BufferingHttpServletRequest$ServletInputStreamWrapper",
10792
packageName + ".BodyCaptureAsyncListener",
93+
packageName + ".Servlet31Advice",
10894
};
10995
}
11096

@@ -115,112 +101,6 @@ public Map<? extends ElementMatcher<? super MethodDescription>, String> transfor
115101
.and(takesArgument(0, named("javax.servlet.ServletRequest")))
116102
.and(takesArgument(1, named("javax.servlet.ServletResponse")))
117103
.and(isPublic()),
118-
FilterAdvice.class.getName());
119-
}
120-
121-
public static class FilterAdvice {
122-
// request attribute key injected at first filerChain.doFilter
123-
private static final String ALREADY_LOADED = "__org.hypertrace.agent.on_start_executed";
124-
125-
@Advice.OnMethodEnter(suppress = Throwable.class, skipOn = BlockingResult.class)
126-
public static Object start(
127-
@Advice.Argument(value = 0, readOnly = false) ServletRequest request,
128-
@Advice.Argument(value = 1, readOnly = false) ServletResponse response,
129-
@Advice.Local("rootStart") Boolean rootStart) {
130-
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
131-
return null;
132-
}
133-
134-
if (!DynamicConfig.isEnabled(InstrumentationName.INSTRUMENTATION_NAME)) {
135-
return null;
136-
}
137-
138-
// TODO run on every doFilter and check if user removed wrapper
139-
// TODO what if user unwraps request and reads the body?
140-
141-
// run the instrumentation only for the root FilterChain.doFilter()
142-
if (request.getAttribute(ALREADY_LOADED) != null) {
143-
return null;
144-
}
145-
request.setAttribute(ALREADY_LOADED, true);
146-
147-
HttpServletRequest httpRequest = (HttpServletRequest) request;
148-
HttpServletResponse httpResponse = (HttpServletResponse) response;
149-
Span currentSpan = TRACER.getCurrentSpan();
150-
151-
rootStart = true;
152-
response = new BufferingHttpServletResponse(httpResponse);
153-
request = new BufferingHttpServletRequest(httpRequest, (HttpServletResponse) response);
154-
155-
ServletSpanDecorator.addSessionId(currentSpan, httpRequest);
156-
157-
// set request headers
158-
Enumeration<String> headerNames = httpRequest.getHeaderNames();
159-
Map<String, String> headers = new HashMap<>();
160-
while (headerNames.hasMoreElements()) {
161-
String headerName = headerNames.nextElement();
162-
String headerValue = httpRequest.getHeader(headerName);
163-
currentSpan.setAttribute(
164-
HypertraceSemanticAttributes.httpRequestHeader(headerName), headerValue);
165-
headers.put(headerName, headerValue);
166-
}
167-
BlockingResult blockingResult = BlockingProvider.getBlockingEvaluator().evaluate(headers);
168-
currentSpan.setAttribute(
169-
HypertraceSemanticAttributes.OPA_RESULT, blockingResult.blockExecution());
170-
if (blockingResult.blockExecution()) {
171-
httpResponse.setStatus(403);
172-
currentSpan.setAttribute(
173-
HypertraceSemanticAttributes.OPA_REASON, blockingResult.getReason());
174-
return blockingResult;
175-
}
176-
return null;
177-
}
178-
179-
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
180-
public static void stopSpan(
181-
@Advice.Argument(0) ServletRequest request,
182-
@Advice.Argument(1) ServletResponse response,
183-
@Advice.Local("rootStart") Boolean rootStart) {
184-
if (rootStart != null) {
185-
if (!(request instanceof BufferingHttpServletRequest)
186-
|| !(response instanceof BufferingHttpServletResponse)) {
187-
return;
188-
}
189-
190-
request.removeAttribute(ALREADY_LOADED);
191-
Span currentSpan = TRACER.getCurrentSpan();
192-
193-
AtomicBoolean responseHandled = new AtomicBoolean(false);
194-
if (request.isAsyncStarted()) {
195-
try {
196-
request
197-
.getAsyncContext()
198-
.addListener(new BodyCaptureAsyncListener(responseHandled, currentSpan));
199-
} catch (IllegalStateException e) {
200-
// org.eclipse.jetty.server.Request may throw an exception here if request became
201-
// finished after check above. We just ignore that exception and move on.
202-
}
203-
}
204-
205-
if (!request.isAsyncStarted() && responseHandled.compareAndSet(false, true)) {
206-
BufferingHttpServletResponse bufferingResponse = (BufferingHttpServletResponse) response;
207-
BufferingHttpServletRequest bufferingRequest = (BufferingHttpServletRequest) request;
208-
209-
// set response headers
210-
for (String headerName : bufferingResponse.getHeaderNames()) {
211-
String headerValue = bufferingResponse.getHeader(headerName);
212-
currentSpan.setAttribute(
213-
HypertraceSemanticAttributes.httpResponseHeader(headerName), headerValue);
214-
}
215-
// Bodies are captured at the end after all user processing.
216-
currentSpan.setAttribute(
217-
HypertraceSemanticAttributes.HTTP_REQUEST_BODY,
218-
bufferingRequest.getBufferedBodyAsString());
219-
currentSpan.setAttribute(
220-
HypertraceSemanticAttributes.HTTP_RESPONSE_BODY,
221-
bufferingResponse.getBufferAsString());
222-
}
223-
}
224-
}
104+
Servlet31Advice.class.getName());
225105
}
226106
}

0 commit comments

Comments
 (0)