Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ void testInjectToTextHtml() throws IOException {
String html = readFileAsString("beforeSnippetInjection.html");

InMemoryHttpServletResponse response = createInMemoryHttpServletResponse("text/html");
SnippetInjectingResponseWrapper responseWrapper =
new SnippetInjectingResponseWrapper(response, snippet);
Servlet3SnippetInjectingResponseWrapper responseWrapper =
new Servlet3SnippetInjectingResponseWrapper(response, snippet);

responseWrapper.getWriter().write(html);
responseWrapper.getWriter().flush();
Expand All @@ -42,8 +42,8 @@ void testInjectToChineseTextHtml() throws IOException {
String html = readFileAsString("beforeSnippetInjectionChinese.html");

InMemoryHttpServletResponse response = createInMemoryHttpServletResponse("text/html");
SnippetInjectingResponseWrapper responseWrapper =
new SnippetInjectingResponseWrapper(response, snippet);
Servlet3SnippetInjectingResponseWrapper responseWrapper =
new Servlet3SnippetInjectingResponseWrapper(response, snippet);

responseWrapper.getWriter().write(html);
responseWrapper.getWriter().flush();
Expand All @@ -59,8 +59,8 @@ void shouldNotInjectToTextHtml() throws IOException {

InMemoryHttpServletResponse response = createInMemoryHttpServletResponse("not/text");

SnippetInjectingResponseWrapper responseWrapper =
new SnippetInjectingResponseWrapper(response, snippet);
Servlet3SnippetInjectingResponseWrapper responseWrapper =
new Servlet3SnippetInjectingResponseWrapper(response, snippet);

responseWrapper.getWriter().write(html);
responseWrapper.getWriter().flush();
Expand All @@ -74,8 +74,8 @@ void testWriteInt() throws IOException {
String html = readFileAsString("beforeSnippetInjection.html");

InMemoryHttpServletResponse response = createInMemoryHttpServletResponse("text/html");
SnippetInjectingResponseWrapper responseWrapper =
new SnippetInjectingResponseWrapper(response, snippet);
Servlet3SnippetInjectingResponseWrapper responseWrapper =
new Servlet3SnippetInjectingResponseWrapper(response, snippet);

byte[] originalBytes = html.getBytes(Charset.defaultCharset());
for (byte originalByte : originalBytes) {
Expand All @@ -93,8 +93,8 @@ void testWriteCharArray() throws IOException {
String html = readFileAsString("beforeSnippetInjectionChinese.html");

InMemoryHttpServletResponse response = createInMemoryHttpServletResponse("text/html");
SnippetInjectingResponseWrapper responseWrapper =
new SnippetInjectingResponseWrapper(response, snippet);
Servlet3SnippetInjectingResponseWrapper responseWrapper =
new Servlet3SnippetInjectingResponseWrapper(response, snippet);

char[] originalChars = html.toCharArray();
responseWrapper.getWriter().write(originalChars, 0, originalChars.length);
Expand All @@ -112,8 +112,8 @@ void testWriteWithOffset() throws IOException {
html = extraBuffer + html;

InMemoryHttpServletResponse response = createInMemoryHttpServletResponse("text/html");
SnippetInjectingResponseWrapper responseWrapper =
new SnippetInjectingResponseWrapper(response, snippet);
Servlet3SnippetInjectingResponseWrapper responseWrapper =
new Servlet3SnippetInjectingResponseWrapper(response, snippet);

responseWrapper
.getWriter()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import io.opentelemetry.javaagent.instrumentation.servlet.snippet.InjectionState;
import io.opentelemetry.javaagent.instrumentation.servlet.snippet.OutputStreamSnippetInjectionHelper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.function.Supplier;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
Expand All @@ -28,7 +31,9 @@ void testInjectionForStringContainHeadTag() throws IOException {
InjectionState obj = createInjectionStateForTesting(snippet, UTF_8);
InMemoryServletOutputStream out = new InMemoryServletOutputStream();

OutputStreamSnippetInjectionHelper helper = new OutputStreamSnippetInjectionHelper(snippet);
Supplier<String> stringSupplier = snippet::toString;
OutputStreamSnippetInjectionHelper helper =
new OutputStreamSnippetInjectionHelper(stringSupplier);
boolean injected = helper.handleWrite(obj, out, html, 0, html.length);
assertThat(obj.getHeadTagBytesSeen()).isEqualTo(-1);
assertThat(injected).isEqualTo(true);
Expand All @@ -45,7 +50,9 @@ void testInjectionForChinese() throws IOException {
InjectionState obj = createInjectionStateForTesting(snippet, UTF_8);
InMemoryServletOutputStream out = new InMemoryServletOutputStream();

OutputStreamSnippetInjectionHelper helper = new OutputStreamSnippetInjectionHelper(snippet);
Supplier<String> stringSupplier = snippet::toString;
OutputStreamSnippetInjectionHelper helper =
new OutputStreamSnippetInjectionHelper(stringSupplier);
boolean injected = helper.handleWrite(obj, out, html, 0, html.length);

byte[] expectedHtml = readFileAsBytes("afterSnippetInjectionChinese.html");
Expand All @@ -61,8 +68,9 @@ void testInjectionForStringWithoutHeadTag() throws IOException {

InjectionState obj = createInjectionStateForTesting(snippet, UTF_8);
InMemoryServletOutputStream out = new InMemoryServletOutputStream();

OutputStreamSnippetInjectionHelper helper = new OutputStreamSnippetInjectionHelper(snippet);
Supplier<String> stringSupplier = snippet::toString;
OutputStreamSnippetInjectionHelper helper =
new OutputStreamSnippetInjectionHelper(stringSupplier);
boolean injected = helper.handleWrite(obj, out, html, 0, html.length);

assertThat(injected).isFalse();
Expand All @@ -79,7 +87,9 @@ void testHeadTagSplitAcrossTwoWrites() throws IOException {
InjectionState obj = createInjectionStateForTesting(snippet, UTF_8);
InMemoryServletOutputStream out = new InMemoryServletOutputStream();

OutputStreamSnippetInjectionHelper helper = new OutputStreamSnippetInjectionHelper(snippet);
Supplier<String> stringSupplier = snippet::toString;
OutputStreamSnippetInjectionHelper helper =
new OutputStreamSnippetInjectionHelper(stringSupplier);
boolean injected =
helper.handleWrite(obj, out, htmlFirstPartBytes, 0, htmlFirstPartBytes.length);

Expand Down Expand Up @@ -119,8 +129,7 @@ private static InjectionState createInjectionStateForTesting(String snippet, Cha
HttpServletResponse response = mock(HttpServletResponse.class);
when(response.isCommitted()).thenReturn(false);
when(response.getCharacterEncoding()).thenReturn(charset.name());

return new InjectionState(new SnippetInjectingResponseWrapper(response, snippet));
return new InjectionState(new Servlet3SnippetInjectingResponseWrapper(response, snippet));
}

private static class InMemoryServletOutputStream extends ServletOutputStream {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import io.opentelemetry.javaagent.bootstrap.servlet.AppServerBridge;
import io.opentelemetry.javaagent.bootstrap.servlet.MappingResolver;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext;
import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.snippet.SnippetInjectingResponseWrapper;
import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.snippet.Servlet3SnippetInjectingResponseWrapper;
import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
Expand Down Expand Up @@ -46,8 +46,9 @@ public static void onEnter(
String snippet = getSnippetInjectionHelper().getSnippet();
if (!snippet.isEmpty()
&& !((HttpServletResponse) response)
.containsHeader(SnippetInjectingResponseWrapper.FAKE_SNIPPET_HEADER)) {
response = new SnippetInjectingResponseWrapper((HttpServletResponse) response, snippet);
.containsHeader(Servlet3SnippetInjectingResponseWrapper.FAKE_SNIPPET_HEADER)) {
response =
new Servlet3SnippetInjectingResponseWrapper((HttpServletResponse) response, snippet);
}
callDepth = CallDepth.forClass(AppServerBridge.getCallDepthKey());
callDepth.getAndIncrement();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.getSnippetInjectionHelper;

import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.snippet.InjectionState;
import io.opentelemetry.javaagent.instrumentation.servlet.snippet.InjectionState;
import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.snippet.ServletOutputStreamInjectionState;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.getSnippetInjectionHelper;

import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.snippet.InjectionState;
import io.opentelemetry.javaagent.instrumentation.servlet.snippet.InjectionState;
import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.snippet.ServletOutputStreamInjectionState;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.Servlet3Singletons.getSnippetInjectionHelper;

import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.snippet.InjectionState;
import io.opentelemetry.javaagent.instrumentation.servlet.snippet.InjectionState;
import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.snippet.ServletOutputStreamInjectionState;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import io.opentelemetry.javaagent.instrumentation.servlet.ServletRequestContext;
import io.opentelemetry.javaagent.instrumentation.servlet.ServletResponseContext;
import io.opentelemetry.javaagent.instrumentation.servlet.common.response.ResponseInstrumenterFactory;
import io.opentelemetry.javaagent.instrumentation.servlet.v3_0.snippet.OutputStreamSnippetInjectionHelper;
import io.opentelemetry.javaagent.instrumentation.servlet.snippet.OutputStreamSnippetInjectionHelper;
import javax.servlet.Filter;
import javax.servlet.Servlet;
import javax.servlet.http.HttpServletRequest;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import static io.opentelemetry.javaagent.instrumentation.servlet.v3_0.snippet.ServletOutputStreamInjectionState.initializeInjectionStateIfNeeded;
import static java.util.logging.Level.FINE;

import io.opentelemetry.javaagent.instrumentation.servlet.snippet.SnippetInjectingPrintWriter;
import io.opentelemetry.javaagent.instrumentation.servlet.snippet.SnippetInjectingResponseWrapper;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.invoke.MethodHandle;
Expand All @@ -29,9 +31,11 @@
* not been submitted, in which case it is likely using the real length of content that has been
* written, including the snippet length.
*/
public class SnippetInjectingResponseWrapper extends HttpServletResponseWrapper {
public class Servlet3SnippetInjectingResponseWrapper extends HttpServletResponseWrapper
implements SnippetInjectingResponseWrapper {

private static final Logger logger = Logger.getLogger(HttpServletResponseWrapper.class.getName());
private static final Logger logger =
Logger.getLogger(Servlet3SnippetInjectingResponseWrapper.class.getName());

public static final String FAKE_SNIPPET_HEADER = "FAKE_SNIPPET_HEADER";

Expand All @@ -48,7 +52,7 @@ public class SnippetInjectingResponseWrapper extends HttpServletResponseWrapper

private SnippetInjectingPrintWriter snippetInjectingPrintWriter = null;

public SnippetInjectingResponseWrapper(HttpServletResponse response, String snippet) {
public Servlet3SnippetInjectingResponseWrapper(HttpServletResponse response, String snippet) {
super(response);
this.snippet = snippet;
snippetLength = snippet.length();
Expand Down Expand Up @@ -89,7 +93,7 @@ public void addHeader(String name, String value) {
try {
contentLength = Long.parseLong(value);
} catch (NumberFormatException ex) {
logger.log(FINE, "NumberFormatException", ex);
logger.log(FINE, "Failed to parse the Content-Length header", ex);
}
}
super.addHeader(name, value);
Expand Down Expand Up @@ -143,7 +147,8 @@ public void setContentLengthLong(long length) throws Throwable {
}
}

boolean isContentTypeTextHtml() {
@Override
public boolean isContentTypeTextHtml() {
String contentType = super.getContentType();
if (contentType == null) {
contentType = super.getHeader("content-type");
Expand All @@ -170,13 +175,15 @@ public PrintWriter getWriter() throws IOException {
return snippetInjectingPrintWriter;
}

void updateContentLengthIfPreviouslySet() {
@Override
public void updateContentLengthIfPreviouslySet() {
if (contentLength != UNSET) {
setContentLength((int) contentLength + snippetLength);
}
}

boolean isNotSafeToInject() {
@Override
public boolean isNotSafeToInject() {
// if content-length was set and response was already committed (headers sent to the client),
// then it is not safe to inject because the content-length header cannot be updated to account
// for the snippet length
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@
package io.opentelemetry.javaagent.instrumentation.servlet.v3_0.snippet;

import io.opentelemetry.instrumentation.api.util.VirtualField;
import io.opentelemetry.javaagent.instrumentation.servlet.snippet.InjectionState;
import javax.annotation.Nullable;
import javax.servlet.ServletOutputStream;

public class ServletOutputStreamInjectionState {

private static final VirtualField<ServletOutputStream, InjectionState> virtualField =
VirtualField.find(ServletOutputStream.class, InjectionState.class);

public static void initializeInjectionStateIfNeeded(
ServletOutputStream servletOutputStream, SnippetInjectingResponseWrapper wrapper) {
ServletOutputStream servletOutputStream, Servlet3SnippetInjectingResponseWrapper wrapper) {
InjectionState state = virtualField.get(servletOutputStream);
if (!wrapper.isContentTypeTextHtml()) {
virtualField.set(servletOutputStream, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* SPDX-License-Identifier: Apache-2.0
*/

import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
Expand Down Expand Up @@ -39,10 +38,10 @@ protected void service(HttpServletRequest req, HttpServletResponse resp)
ServletContext context = getServletContext();
RequestDispatcher dispatcher = context.getRequestDispatcher(target);
// for HTML test case, set the content type before calling include because
// setContentType will be rejected if called inside of include
// check https://statics.teams.cdn.office.net/evergreen-assets/safelinks/1/atp-safelinks.html
if (ServerEndpoint.forPath(target) == ServerEndpoint.forPath("/htmlPrintWriter")
|| ServerEndpoint.forPath(target) == ServerEndpoint.forPath("/htmlServletOutputStream")) {
// setContentType will be rejected if called inside include
// check
// https://docs.oracle.com/javaee/7/api/javax/servlet/RequestDispatcher.html#include-javax.servlet.ServletRequest-javax.servlet.ServletResponse-
if ("/htmlPrintWriter".equals(target) || "/htmlServletOutputStream".equals(target)) {
resp.setContentType("text/html");
}
dispatcher.include(req, resp);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
plugins {
id("otel.java-conventions")
}

dependencies {
testImplementation("jakarta.servlet:jakarta.servlet-api:5.0.0")
testImplementation(project(":instrumentation:servlet:servlet-5.0:javaagent"))
}
Loading