/** * Lets InspectIT optionally instrument the given response by wrapping it. * * @param httpRequestObj * the request (an instance of javax.servlet.ServletRequest) * @param httpResponseObj * the response object (an instance of javax.servlet.ServletResponse) * @return the new response object to use, or the original one if it was not instrumented. */ public Object instrumentResponse(Object httpRequestObj, Object httpResponseObj) { try { if (configurationValid && WHttpServletResponse.isInstance(httpResponseObj)) { if (!linker.isProxyInstance(httpResponseObj, TagInjectionResponseWrapper.class)) { ClassLoader cl = httpResponseObj.getClass().getClassLoader(); TagInjectionResponseWrapper wrap = new TagInjectionResponseWrapper(httpRequestObj, httpResponseObj, tracer, scriptTags); Object proxy = linker.createProxy(TagInjectionResponseWrapper.class, wrap, cl); if (proxy == null) { return httpResponseObj; } else { return proxy; } } } } catch (Throwable e) { // NOPMD LOG.error("Error instrumenting response object.", e); } return httpResponseObj; // No instrumentation }
/** * {@inheritDoc} */ @Override public void setSpanStore(SpanStore spanStore) { // get original request listener Object originalListener = cache.invokeMethod(jettyHttpExchange.getClass(), "getEventListener", new Class<?>[] {}, jettyHttpExchange, new Object[] {}, null); // create proxy for this listener JettyEventListenerProxy listenerProxy = new JettyEventListenerProxy(originalListener, spanStore); Object proxyObject = runtimeLinker.createProxy(JettyEventListenerProxy.class, listenerProxy, jettyHttpExchange.getClass().getClassLoader()); // find the interface event listener interface, it's in the super-class of the proxy Class<?> eventListenerClass = proxyObject.getClass().getSuperclass().getInterfaces()[0]; // replace with our listener cache.invokeMethod(jettyHttpExchange.getClass(), "setEventListener", new Class<?>[] { eventListenerClass }, jettyHttpExchange, new Object[] { proxyObject }, null); }
/** * {@inheritDoc} */ @Override public Object beforeBody(long methodId, Object object, Object[] parameters, SpecialSensorConfig ssc) { if ((null != parameters) && (parameters.length == 4)) { // the HTTP context Object httpContext = parameters[2]; // the original callback given by the user Object originalCallback = parameters[3]; SpanStoreAdapter spanStoreAdapter = new ApacheHttpContextSpanStoreAdapter(httpContext); FutureCallbackProxy proxy = new FutureCallbackProxy(originalCallback, spanStoreAdapter); Object newProxy = runtimeLinker.createProxy(FutureCallbackProxy.class, proxy, object.getClass().getClassLoader()); parameters[3] = newProxy; } return null; }
@SuppressWarnings("unchecked") @BeforeMethod public void initLinker() { when(linker.isProxyInstance(any(TagInjectionResponseWrapper.class), any(Class.class))).thenAnswer(new Answer<Boolean>() { @Override public Boolean answer(InvocationOnMock invocation) throws Throwable { return invocation.getArguments()[0] instanceof FakeWrapper; } }); when(linker.createProxy(any(Class.class), any(TagInjectionResponseWrapper.class), any(ClassLoader.class))).then(new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { respWrapper = (TagInjectionResponseWrapper) invocation.getArguments()[1]; return new FakeWrapper(dummyResponse); } }); }
@Test public void successful() { SpanStore spanStore = new SpanStore(); Object[] parameters = new Object[] { new Object(), new Object(), httpContext, mock(FutureCallback.class) }; when(runtimeLinker.createProxy(eq(FutureCallbackProxy.class), Mockito.<FutureCallbackProxy> any(), Mockito.<ClassLoader> any())).thenReturn(futureCallback); when(httpContext.getAttribute(SpanStoreAdapter.Constants.ID)).thenReturn(spanStore); ArgumentCaptor<FutureCallbackProxy> proxyCaptor = ArgumentCaptor.forClass(FutureCallbackProxy.class); Object result = hook.beforeBody(METHOD_ID, object, parameters, ssc); verify(runtimeLinker).createProxy(eq(FutureCallbackProxy.class), proxyCaptor.capture(), Mockito.<ClassLoader> any()); verifyNoMoreInteractions(runtimeLinker, httpContext); verifyZeroInteractions(ssc, object, futureCallback, httpContext); assertThat(result, is(nullValue())); assertThat(parameters[3], is(theInstance((Object) futureCallback))); }
@SuppressWarnings("unchecked") @BeforeMethod public void init() { when(linker.isProxyInstance(any(Object.class), any(Class.class))).thenReturn(false); when(linker.createProxy(any(Class.class), any(TagInjectionOutputStream.class), any(ClassLoader.class))).then(new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { TagInjectionOutputStream injectedStream = (TagInjectionOutputStream) invocation.getArguments()[1]; injectedStream.proxyLinked(dummyStreamProxy, linker); return dummyStreamProxy; } }); streamResult.reset(); respWrapper = new TagInjectionResponseWrapper(dummyRequest, dummyResponse, tracer, tagPrinter); respWrapper.proxyLinked(dummyProxy, linker); }
@SuppressWarnings("unchecked") @Test public void testPlainTextNoInjection() throws IOException { ArgumentCaptor<TagInjectionOutputStream> streamCaptor = ArgumentCaptor.forClass(TagInjectionOutputStream.class); respWrapper.getOutputStream(); verify(linker, times(1)).createProxy(any(Class.class), streamCaptor.capture(), any(ClassLoader.class)); TagInjectionOutputStream stream = streamCaptor.getValue(); byte[] bytes = NON_HTML_TEST_CASE_A.getBytes(CHARACTER_ENCODING); int pos = 0; while (pos < bytes.length) { stream.write(bytes, pos, Math.min(3, bytes.length - pos)); pos += 3; // write 3 bytes at once } String result = new String(streamResult.toByteArray(), CHARACTER_ENCODING); assertThat(result, equalTo(NON_HTML_TEST_CASE_A)); }
/** * Proxy for {@link javax.servlet.ServletResponse#getOutputStream()}. * * @return the instrumented stream * @throws IOException * if an exception getting the original stream occurs. */ @ProxyMethod(returnType = "javax.servlet.ServletOutputStream") public OutputStream getOutputStream() throws IOException { commitHeaderData(); if (wrappedStream == null) { OutputStream originalStream = wrappedResponse.getOutputStream(); // avoid rewrapping or unnecessary wrapping if (isNonHtmlContentTypeSet() || linker.isProxyInstance(originalStream, TagInjectionOutputStream.class)) { wrappedStream = originalStream; } else { TagInjectionOutputStream resultStr = new TagInjectionOutputStream(originalStream, tagToInject.printTags()); resultStr.setEncoding(wrappedResponse.getCharacterEncoding()); ClassLoader cl = wrappedResponse.getWrappedElement().getClass().getClassLoader(); wrappedStream = (OutputStream) linker.createProxy(TagInjectionOutputStream.class, resultStr, cl); if (wrappedStream == null) { // fallback to the normal stream if it can not be linked wrappedStream = originalStream; } } } return wrappedStream; }
@SuppressWarnings("unchecked") @Test public void testHeadInjection() throws IOException { ArgumentCaptor<TagInjectionOutputStream> streamCaptor = ArgumentCaptor.forClass(TagInjectionOutputStream.class); respWrapper.getOutputStream(); verify(linker, times(1)).createProxy(any(Class.class), streamCaptor.capture(), any(ClassLoader.class)); TagInjectionOutputStream stream = streamCaptor.getValue(); byte[] bytes = HTML_TEST_CASE_A.getBytes(CHARACTER_ENCODING); int pos = 0; while (pos < bytes.length) { stream.write(bytes, pos, Math.min(3, bytes.length - pos)); pos += 3; // write 3 bytes at once } String result = new String(streamResult.toByteArray(), CHARACTER_ENCODING); assertThat(result, equalTo(HTML_TEST_CASE_A_REFERENCE)); }
@SuppressWarnings("unchecked") @Test public void testInvalidMarkupNoInjection() throws IOException { ArgumentCaptor<TagInjectionOutputStream> streamCaptor = ArgumentCaptor.forClass(TagInjectionOutputStream.class); respWrapper.getOutputStream(); verify(linker, times(1)).createProxy(any(Class.class), streamCaptor.capture(), any(ClassLoader.class)); TagInjectionOutputStream stream = streamCaptor.getValue(); byte[] bytes = NON_HTML_TEST_CASE_B.getBytes(CHARACTER_ENCODING); int pos = 0; while (pos < bytes.length) { stream.write(bytes, pos, Math.min(3, bytes.length - pos)); pos += 3; // write 3 bytes at once } String result = new String(streamResult.toByteArray(), CHARACTER_ENCODING); assertThat(result, equalTo(NON_HTML_TEST_CASE_B)); }
@SuppressWarnings("unchecked") @Test public void testBodyInjection() throws IOException { ArgumentCaptor<TagInjectionOutputStream> streamCaptor = ArgumentCaptor.forClass(TagInjectionOutputStream.class); respWrapper.getOutputStream(); verify(linker, times(1)).createProxy(any(Class.class), streamCaptor.capture(), any(ClassLoader.class)); TagInjectionOutputStream stream = streamCaptor.getValue(); byte[] bytes = HTML_TEST_CASE_B.getBytes(CHARACTER_ENCODING); int pos = 0; while (pos < bytes.length) { stream.write(bytes, pos, Math.min(3, bytes.length - pos)); pos += 3; // write 3 bytes at once } String result = new String(streamResult.toByteArray(), CHARACTER_ENCODING); assertThat(result, equalTo(HTML_TEST_CASE_B_REFERENCE)); }
@SuppressWarnings("unchecked") @Test public void testPreventDoubleInstrumentation() throws IOException { hook = new EUMInstrumentationHook(linker, tracer, dataHandler, config, agentBuilder); Object[] params = new Object[] { dummyRequest, new FakeWrapper(dummyResponse) }; boolean intercepted = null != hook.beforeBody(METHOD_ID, dummyServlet, params, ssc); assertThat(intercepted, equalTo(false)); verify(linker, never()).createProxy(any(Class.class), any(IProxySubject.class), any(ClassLoader.class)); }
@SuppressWarnings("unchecked") @Test public void testLinkerErrorHandling() throws IOException { when(linker.createProxy(any(Class.class), any(TagInjectionResponseWrapper.class), any(ClassLoader.class))).then(new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { return null; } }); hook = new EUMInstrumentationHook(linker, tracer, dataHandler, config, agentBuilder); Object[] params = new Object[] { dummyRequest, dummyResponse }; boolean intercepted = null != hook.beforeBody(METHOD_ID, dummyServlet, params, ssc); Object response = params[1]; assertThat(intercepted, equalTo(false)); verify(linker, times(1)).createProxy(any(Class.class), any(IProxySubject.class), any(ClassLoader.class)); assertThat(response, equalTo((Object) dummyResponse)); assertThat(intercepted, equalTo(false)); }
@Test public void spanStoreListenerNull() { Proxy proxyListener = new Proxy(); when(httpExchange.getEventListener()).thenReturn(null); when(runtimeLinker.createProxy(eq(JettyEventListenerProxy.class), Mockito.<JettyEventListenerProxy> any(), Mockito.<ClassLoader> any())).thenReturn(proxyListener); AsyncClientRequestAdapter<TextMap> adapter = sensor.getAsyncClientRequestAdapter(object, new Object[] { httpExchange }, rsc); adapter.getSpanStoreAdapter().setSpanStore(spanStore); verify(httpExchange).setEventListener(proxyListener); ArgumentCaptor<JettyEventListenerProxy> proxyCaptor = ArgumentCaptor.forClass(JettyEventListenerProxy.class); verify(runtimeLinker).createProxy(eq(JettyEventListenerProxy.class), proxyCaptor.capture(), eq(httpExchange.getClass().getClassLoader())); assertThat(proxyCaptor.getValue().getOriginalListener(), is(nullValue())); assertThat(proxyCaptor.getValue().getSpanStore(), is(spanStore)); verifyNoMoreInteractions(runtimeLinker); verifyZeroInteractions(object, rsc); }
@Test public void spanStore() { Object listener = new Object(); Proxy proxyListener = new Proxy(); when(httpExchange.getEventListener()).thenReturn(listener); when(runtimeLinker.createProxy(eq(JettyEventListenerProxy.class), Mockito.<JettyEventListenerProxy> any(), Mockito.<ClassLoader> any())).thenReturn(proxyListener); AsyncClientRequestAdapter<TextMap> adapter = sensor.getAsyncClientRequestAdapter(object, new Object[] { httpExchange }, rsc); adapter.getSpanStoreAdapter().setSpanStore(spanStore); verify(httpExchange).setEventListener(proxyListener); ArgumentCaptor<JettyEventListenerProxy> proxyCaptor = ArgumentCaptor.forClass(JettyEventListenerProxy.class); verify(runtimeLinker).createProxy(eq(JettyEventListenerProxy.class), proxyCaptor.capture(), eq(httpExchange.getClass().getClassLoader())); assertThat(proxyCaptor.getValue().getOriginalListener(), is(listener)); assertThat(proxyCaptor.getValue().getSpanStore(), is(spanStore)); verifyNoMoreInteractions(runtimeLinker); verifyZeroInteractions(object, rsc); }