From 0c79f14c79fdabff31c617573cb6dbbe6cd1b472 Mon Sep 17 00:00:00 2001 From: oliver zhang Date: Wed, 9 Aug 2023 22:58:33 +0800 Subject: [PATCH] snippet inject support like (#8736) Co-authored-by: Mateusz Rzeszutek Co-authored-by: Lauri Tulmin --- .../v3_0/snippet/SnippetPrintWriterTest.java | 16 ++++++++++++++++ .../SnippetServletOutputStreamTest.java | 19 +++++++++++++++++++ ...terSnippetInjectionWithOtherHeadStyle.html | 11 +++++++++++ ...oreSnippetInjectionWithOtherHeadStyle.html | 10 ++++++++++ ...rvlet3SnippetInjectingResponseWrapper.java | 3 ++- .../v5_0/snippet/SnippetPrintWriterTest.java | 16 ++++++++++++++++ .../SnippetServletOutputStreamTest.java | 19 +++++++++++++++++++ ...terSnippetInjectionWithOtherHeadStyle.html | 11 +++++++++++ ...oreSnippetInjectionWithOtherHeadStyle.html | 10 ++++++++++ ...rvlet5SnippetInjectingResponseWrapper.java | 3 ++- .../bootstrap/servlet/InjectionState.java | 9 +++++---- 11 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 instrumentation/servlet/servlet-3.0/javaagent-unit-tests/src/test/resources/afterSnippetInjectionWithOtherHeadStyle.html create mode 100644 instrumentation/servlet/servlet-3.0/javaagent-unit-tests/src/test/resources/beforeSnippetInjectionWithOtherHeadStyle.html create mode 100644 instrumentation/servlet/servlet-5.0/javaagent-unit-tests/src/test/resources/afterSnippetInjectionWithOtherHeadStyle.html create mode 100644 instrumentation/servlet/servlet-5.0/javaagent-unit-tests/src/test/resources/beforeSnippetInjectionWithOtherHeadStyle.html diff --git a/instrumentation/servlet/servlet-3.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/snippet/SnippetPrintWriterTest.java b/instrumentation/servlet/servlet-3.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/snippet/SnippetPrintWriterTest.java index 796665225deb..65e47c168c29 100644 --- a/instrumentation/servlet/servlet-3.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/snippet/SnippetPrintWriterTest.java +++ b/instrumentation/servlet/servlet-3.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/snippet/SnippetPrintWriterTest.java @@ -124,6 +124,22 @@ void testWriteWithOffset() throws IOException { assertThat(response.getStringContent()).isEqualTo(expectedHtml); } + @Test + void testInjectToTextHtmlWithOtherHeadStyle() throws IOException { + String snippet = "\n "; + String html = readFileAsString("beforeSnippetInjectionWithOtherHeadStyle.html"); + + InMemoryHttpServletResponse response = createInMemoryHttpServletResponse("text/html"); + Servlet3SnippetInjectingResponseWrapper responseWrapper = + new Servlet3SnippetInjectingResponseWrapper(response, snippet); + + responseWrapper.getWriter().write(html); + responseWrapper.getWriter().flush(); + + String expectedHtml = readFileAsString("afterSnippetInjectionWithOtherHeadStyle.html"); + assertThat(response.getStringContent()).isEqualTo(expectedHtml); + } + private static InMemoryHttpServletResponse createInMemoryHttpServletResponse(String contentType) { HttpServletResponse response = mock(HttpServletResponse.class); when(response.getContentType()).thenReturn(contentType); diff --git a/instrumentation/servlet/servlet-3.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/snippet/SnippetServletOutputStreamTest.java b/instrumentation/servlet/servlet-3.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/snippet/SnippetServletOutputStreamTest.java index 10ee6f14997f..d16c4c521f6c 100644 --- a/instrumentation/servlet/servlet-3.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/snippet/SnippetServletOutputStreamTest.java +++ b/instrumentation/servlet/servlet-3.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/snippet/SnippetServletOutputStreamTest.java @@ -125,6 +125,25 @@ void testHeadTagSplitAcrossTwoWrites() throws IOException { assertThat(out.getBytes()).isEqualTo(expectedSecondPart.getBytes(UTF_8)); } + @Test + void testInjectionWithOtherHeadStyle() throws IOException { + String snippet = "\n "; + byte[] html = readFileAsBytes("beforeSnippetInjectionWithOtherHeadStyle.html"); + + InjectionState obj = createInjectionStateForTesting(snippet, UTF_8); + InMemoryServletOutputStream out = new InMemoryServletOutputStream(); + + Supplier 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); + + byte[] expectedHtml = readFileAsBytes("afterSnippetInjectionWithOtherHeadStyle.html"); + assertThat(out.getBytes()).isEqualTo(expectedHtml); + } + private static InjectionState createInjectionStateForTesting(String snippet, Charset charset) { HttpServletResponse response = mock(HttpServletResponse.class); when(response.isCommitted()).thenReturn(false); diff --git a/instrumentation/servlet/servlet-3.0/javaagent-unit-tests/src/test/resources/afterSnippetInjectionWithOtherHeadStyle.html b/instrumentation/servlet/servlet-3.0/javaagent-unit-tests/src/test/resources/afterSnippetInjectionWithOtherHeadStyle.html new file mode 100644 index 000000000000..4ef9cb954d5a --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/javaagent-unit-tests/src/test/resources/afterSnippetInjectionWithOtherHeadStyle.html @@ -0,0 +1,11 @@ + + + + + + Title + + + + + diff --git a/instrumentation/servlet/servlet-3.0/javaagent-unit-tests/src/test/resources/beforeSnippetInjectionWithOtherHeadStyle.html b/instrumentation/servlet/servlet-3.0/javaagent-unit-tests/src/test/resources/beforeSnippetInjectionWithOtherHeadStyle.html new file mode 100644 index 000000000000..65f97cac2408 --- /dev/null +++ b/instrumentation/servlet/servlet-3.0/javaagent-unit-tests/src/test/resources/beforeSnippetInjectionWithOtherHeadStyle.html @@ -0,0 +1,10 @@ + + + + + Title + + + + + diff --git a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/snippet/Servlet3SnippetInjectingResponseWrapper.java b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/snippet/Servlet3SnippetInjectingResponseWrapper.java index 57dbb09401e7..be732a35b2ec 100644 --- a/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/snippet/Servlet3SnippetInjectingResponseWrapper.java +++ b/instrumentation/servlet/servlet-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/snippet/Servlet3SnippetInjectingResponseWrapper.java @@ -150,7 +150,8 @@ public boolean isContentTypeTextHtml() { if (contentType == null) { contentType = super.getHeader("content-type"); } - return contentType != null && contentType.startsWith("text/html"); + return contentType != null + && (contentType.startsWith("text/html") || contentType.startsWith("application/xhtml+xml")); } @Override diff --git a/instrumentation/servlet/servlet-5.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/snippet/SnippetPrintWriterTest.java b/instrumentation/servlet/servlet-5.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/snippet/SnippetPrintWriterTest.java index ebe5adba05d6..efcd6a7a3285 100644 --- a/instrumentation/servlet/servlet-5.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/snippet/SnippetPrintWriterTest.java +++ b/instrumentation/servlet/servlet-5.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/snippet/SnippetPrintWriterTest.java @@ -123,6 +123,22 @@ void testWriteWithOffset() throws IOException { assertThat(response.getStringContent()).isEqualTo(expectedHtml); } + @Test + void testInjectToTextHtmlWithOtherHeadStyle() throws IOException { + String snippet = "\n "; + String html = TestUtil.readFileAsString("beforeSnippetInjectionWithOtherHeadStyle.html"); + + InMemoryHttpServletResponse response = createInMemoryHttpServletResponse("text/html"); + Servlet5SnippetInjectingResponseWrapper responseWrapper = + new Servlet5SnippetInjectingResponseWrapper(response, snippet); + + responseWrapper.getWriter().write(html); + responseWrapper.getWriter().flush(); + + String expectedHtml = TestUtil.readFileAsString("afterSnippetInjectionWithOtherHeadStyle.html"); + assertThat(response.getStringContent()).isEqualTo(expectedHtml); + } + private static InMemoryHttpServletResponse createInMemoryHttpServletResponse(String contentType) { HttpServletResponse response = mock(HttpServletResponse.class); when(response.getContentType()).thenReturn(contentType); diff --git a/instrumentation/servlet/servlet-5.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/snippet/SnippetServletOutputStreamTest.java b/instrumentation/servlet/servlet-5.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/snippet/SnippetServletOutputStreamTest.java index 29a25b646a3e..aed84df63cf0 100644 --- a/instrumentation/servlet/servlet-5.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/snippet/SnippetServletOutputStreamTest.java +++ b/instrumentation/servlet/servlet-5.0/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/snippet/SnippetServletOutputStreamTest.java @@ -127,6 +127,25 @@ void testHeadTagSplitAcrossTwoWrites() throws IOException { assertThat(out.getBytes()).isEqualTo(expectedSecondPart.getBytes(UTF_8)); } + @Test + void testInjectionWithOtherHeadStyle() throws IOException { + String snippet = "\n "; + byte[] html = readFileAsBytes("beforeSnippetInjectionWithOtherHeadStyle.html"); + + InjectionState obj = createInjectionStateForTesting(snippet, UTF_8); + InMemoryServletOutputStream out = new InMemoryServletOutputStream(); + + Supplier 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); + + byte[] expectedHtml = readFileAsBytes("afterSnippetInjectionWithOtherHeadStyle.html"); + assertThat(out.getBytes()).isEqualTo(expectedHtml); + } + private static InjectionState createInjectionStateForTesting(String snippet, Charset charset) { HttpServletResponse response = mock(HttpServletResponse.class); when(response.isCommitted()).thenReturn(false); diff --git a/instrumentation/servlet/servlet-5.0/javaagent-unit-tests/src/test/resources/afterSnippetInjectionWithOtherHeadStyle.html b/instrumentation/servlet/servlet-5.0/javaagent-unit-tests/src/test/resources/afterSnippetInjectionWithOtherHeadStyle.html new file mode 100644 index 000000000000..4ef9cb954d5a --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/javaagent-unit-tests/src/test/resources/afterSnippetInjectionWithOtherHeadStyle.html @@ -0,0 +1,11 @@ + + + + + + Title + + + + + diff --git a/instrumentation/servlet/servlet-5.0/javaagent-unit-tests/src/test/resources/beforeSnippetInjectionWithOtherHeadStyle.html b/instrumentation/servlet/servlet-5.0/javaagent-unit-tests/src/test/resources/beforeSnippetInjectionWithOtherHeadStyle.html new file mode 100644 index 000000000000..65f97cac2408 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/javaagent-unit-tests/src/test/resources/beforeSnippetInjectionWithOtherHeadStyle.html @@ -0,0 +1,10 @@ + + + + + Title + + + + + diff --git a/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/snippet/Servlet5SnippetInjectingResponseWrapper.java b/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/snippet/Servlet5SnippetInjectingResponseWrapper.java index 5a9f02acc748..06d8b38e162e 100644 --- a/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/snippet/Servlet5SnippetInjectingResponseWrapper.java +++ b/instrumentation/servlet/servlet-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/snippet/Servlet5SnippetInjectingResponseWrapper.java @@ -142,7 +142,8 @@ public boolean isContentTypeTextHtml() { if (contentType == null) { contentType = super.getHeader("content-type"); } - return contentType != null && contentType.startsWith("text/html"); + return contentType != null + && (contentType.startsWith("text/html") || contentType.startsWith("application/xhtml+xml")); } @Override diff --git a/instrumentation/servlet/servlet-common/bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/servlet/InjectionState.java b/instrumentation/servlet/servlet-common/bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/servlet/InjectionState.java index 7986357696d9..bc34f72d0fd7 100644 --- a/instrumentation/servlet/servlet-common/bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/servlet/InjectionState.java +++ b/instrumentation/servlet/servlet-common/bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/servlet/InjectionState.java @@ -8,7 +8,7 @@ // this is shared by both ServletOutputStream and PrintWriter injection public class InjectionState { private static final int HEAD_TAG_WRITTEN_FAKE_VALUE = -1; - private static final int HEAD_TAG_LENGTH = "".length(); + private static final int HEAD_TAG_PREFIX_LENGTH = " HEAD_TAG_PREFIX_LENGTH && b == '>') { setHeadTagWritten(); return true; } else { @@ -64,10 +64,11 @@ private boolean inHeadTag(int b) { return true; } else if (headTagBytesSeen == 4 && b == 'd') { return true; - } else if (headTagBytesSeen == 5 && b == '>') { + } else if (headTagBytesSeen == 5 && (b == '>' || Character.isWhitespace(b))) { return true; + } else { + return headTagBytesSeen > 5; } - return false; } public SnippetInjectingResponseWrapper getWrapper() {