Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate browser footer injection APIs;header injection now adds both scripts #1679

Merged
merged 4 commits into from
Jan 11, 2024
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 @@ -107,13 +107,20 @@ public interface PublicApi extends ErrorApi {
/**
* Get the RUM JavaScript footer for the current web transaction.
*
* @deprecated The full browser script is now included when calling {@link PublicApi#getBrowserTimingHeader}
* or {@link PublicApi#getBrowserTimingHeader(String)}
*
* @return RUM JavaScript footer for the current web transaction.
*/
String getBrowserTimingFooter();

/**
* Get the RUM JavaScript footer for the current web transaction.
*
* @param nonce a random per-request nonce for sites using Content Security Policy (CSP)
* @deprecated The full browser script is not included when calling {@link PublicApi#getBrowserTimingHeader}
* or {@link PublicApi#getBrowserTimingHeader(String)}
*
* @return RUM JavaScript footer for the current web transaction.
*/
String getBrowserTimingFooter(String nonce);
Expand Down
141 changes: 21 additions & 120 deletions functional_test/src/test/java/test/newrelic/test/agent/api/ApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/* (non-javadoc)
* Note: the "beacon" was a predecessor technology for correlated transaction traces with the browser.
Expand Down Expand Up @@ -386,7 +388,7 @@ public void testSetTransactionNameNoStartingForwardSlash() {
}

@Test
public void testSetTransactionNameAfterGetBrowserInstrumentationFooter() throws Exception {
public void testSetTransactionNameAfterGetBrowserInstrumentationScript() throws Exception {

ApiTestHelper.mockOutServiceManager();

Expand All @@ -397,7 +399,6 @@ public void testSetTransactionNameAfterGetBrowserInstrumentationFooter() throws
NewRelic.setTransactionName("Test", "/foo1");

NewRelic.getBrowserTimingHeader();
NewRelic.getBrowserTimingFooter(); // lock the name

NewRelic.setTransactionName("Test", "/foo2");

Expand Down Expand Up @@ -1329,15 +1330,21 @@ public void testGetBrowserTimingHeader() throws Exception {
tx.getTransactionActivity().tracerStarted(tracer);

String browserTimingHeader = NewRelic.getBrowserTimingHeader();
Assert.assertEquals("Incorrect header", ApiTestHelper.HEADER, browserTimingHeader);

// The only thing that can vary in the returned script is the "applicationTime" property, so we remove it
Pattern p = Pattern.compile("\"applicationTime\":([0-9]*)");
browserTimingHeader = browserTimingHeader.replaceFirst("\"applicationTime\":([0-9]*)", "");


Assert.assertEquals("Incorrect header", ApiTestHelper.JAVASCRIPT_AGENT_SCRIPT, browserTimingHeader);
tx.getTransactionActivity().tracerFinished(tracer, 0);
}

@Test
public void testGetBrowserTimingHeaderWhenRUMEnabledNotSpecified() throws Exception {

Map<String, Object> connectionResponse = new HashMap<>();
connectionResponse.put(ApiTestHelper.BROWSER_KEY, "3969ca217b");
connectionResponse.put(ApiTestHelper.BROWSER_KEY, "abcd");
connectionResponse.put(ApiTestHelper.BROWSER_LOADER_VERSION, "248");
connectionResponse.put(ApiTestHelper.JS_AGENT_LOADER, ApiTestHelper.LOADER);
connectionResponse.put(ApiTestHelper.JS_AGENT_FILE, "js-agent.newrelic.com\nr-248.min.js");
Expand All @@ -1355,7 +1362,12 @@ public void testGetBrowserTimingHeaderWhenRUMEnabledNotSpecified() throws Except
tx.getTransactionActivity().tracerStarted(tracer);

String browserTimingHeader = NewRelic.getBrowserTimingHeader();
Assert.assertEquals("Incorrect header", ApiTestHelper.HEADER, browserTimingHeader);

// The only thing that can vary in the returned script is the "applicationTime" property, so we remove it
Pattern p = Pattern.compile("\"applicationTime\":([0-9]*)");
browserTimingHeader = browserTimingHeader.replaceFirst("\"applicationTime\":([0-9]*)", "");

Assert.assertEquals("Incorrect header", ApiTestHelper.JAVASCRIPT_AGENT_SCRIPT, browserTimingHeader);
tx.getTransactionActivity().tracerFinished(tracer, 0);

}
Expand Down Expand Up @@ -1425,33 +1437,7 @@ public void testGetBrowserTimingHeaderNoTransaction() {
String browserTimingHeader = NewRelic.getBrowserTimingHeader();
Assert.assertEquals("Outside of a transaction, the timing header should be empty", "", browserTimingHeader);
}

@Test
public void testGetBrowserTimingFooterNoTransaction() {

String browserTimingFooter = NewRelic.getBrowserTimingFooter();
Assert.assertEquals("Outside of a transaction, the timing footer should be empty", "", browserTimingFooter);
}

@Test
public void testGetBrowserTimingFooter() throws Exception {

ApiTestHelper.mockOutServiceManager();
Transaction tx = Transaction.getTransaction();
TransactionNamePriority expectedPriority = TransactionNamePriority.FILTER_NAME;
PriorityTransactionName ptn = PriorityTransactionName.create("name", null, expectedPriority);
tx.setPriorityTransactionName(ptn);
BasicRequestRootTracer tracer = createDispatcherTracer();
tx.getTransactionActivity().tracerStarted(tracer);

NewRelic.getBrowserTimingHeader();

String browserTimingFooter = NewRelic.getBrowserTimingFooter();
Assert.assertTrue("Incorrect footer. Was " + browserTimingFooter + ", but expected it to start with "
+ getTimingFooterStart(), browserTimingFooter.startsWith(getTimingFooterStart()));
tx.getTransactionActivity().tracerFinished(tracer, 0);
}


@Test
public void testRUMWithNoBeacon() throws Exception {

Expand Down Expand Up @@ -1527,49 +1513,6 @@ public void testGetBrowserTimingFooterNoHeader() throws Exception {

}

@Test
public void testGetBrowserTimingFooterWhenComponentBasedTransactionNamingDisabled() throws Exception {

Map<String, Object> connectionResponse = new HashMap<>();
connectionResponse.put(ApiTestHelper.BROWSER_KEY, "3969ca217b");
connectionResponse.put(ApiTestHelper.BROWSER_LOADER_VERSION, "248");
connectionResponse.put(ApiTestHelper.JS_AGENT_LOADER, ApiTestHelper.LOADER);
connectionResponse.put(ApiTestHelper.JS_AGENT_FILE, "js-agent.newrelic.com/r-248.min.js");
connectionResponse.put(ApiTestHelper.BEACON, "staging-beacon-2.newrelic.com");
connectionResponse.put(ApiTestHelper.ERROR_BEACON, "staging-jserror.newrelic.com");
connectionResponse.put(ApiTestHelper.APPLICATION_ID, 100L);

ApiTestHelper.mockOutServiceManager(connectionResponse);

Transaction tx = Transaction.getTransaction();

BasicRequestRootTracer tracer = createDispatcherTracer();
tx.getTransactionActivity().tracerStarted(tracer);

NewRelic.getBrowserTimingHeader();
String browserTimingFooter = NewRelic.getBrowserTimingFooter();
System.out.println(browserTimingFooter);
List<String> matched = new ArrayList<>(15);
BrowserConfigTest.checkFooter(browserTimingFooter, matched);

String expectedObfuscatedName = Obfuscator.obfuscateNameUsingKey(
tx.getPriorityTransactionName().getName(),
NewRelic.getAgent().getConfig().getValue("license_key", "").substring(0, 13));

List<String> expectedFooterProperties = Arrays.asList(
"\"beacon\":\"staging-beacon-2.newrelic.com\"",
"\"queueTime\":[0-9]+",
"\"licenseKey\":\"3969ca217b\"",
"\"transactionName\":\"" + expectedObfuscatedName + "\"",
"\"errorBeacon\":\"staging-jserror.newrelic.com\"",
"\"agent\":\"js-agent.newrelic.com\\\\/r-248.min.js\"",
"\"applicationTime\":[0-9]+",
"\"applicationID\":\"100\"");
BrowserConfigTest.checkStrings(browserTimingFooter, expectedFooterProperties, matched);
tx.getTransactionActivity().tracerFinished(tracer, 0);

}

@Test
public void testGetBrowserTimingFooterWhenRUMDisabled() throws Exception {
Map<String, Object> connectionResponse = new HashMap<>();
Expand All @@ -1587,30 +1530,6 @@ public void testGetBrowserTimingFooterWhenRUMDisabled() throws Exception {
tx.getTransactionActivity().tracerFinished(tracer, 0);
}

@Test
public void testGetBrowserTimingFooterWhenRUMEnabledNotSpecified() throws Exception {
Map<String, Object> connectionResponse = new HashMap<>();
connectionResponse.put(ApiTestHelper.BROWSER_KEY, "3969ca217b");
connectionResponse.put(ApiTestHelper.BROWSER_LOADER_VERSION, "248");
connectionResponse.put(ApiTestHelper.JS_AGENT_LOADER, ApiTestHelper.LOADER);
connectionResponse.put(ApiTestHelper.JS_AGENT_FILE, "js-agent.newrelic.com\nr-248.min.js");
connectionResponse.put(ApiTestHelper.BEACON, "staging-beacon-2.newrelic.com");
connectionResponse.put(ApiTestHelper.ERROR_BEACON, "staging-jserror.newrelic.com");
connectionResponse.put(ApiTestHelper.APPLICATION_ID, 100L);
ApiTestHelper.mockOutServiceManager(connectionResponse);

Transaction tx = Transaction.getTransaction();

BasicRequestRootTracer tracer = createDispatcherTracer();
tx.getTransactionActivity().tracerStarted(tracer);

NewRelic.getBrowserTimingHeader();
String browserTimingFooter = NewRelic.getBrowserTimingFooter();
Assert.assertTrue("Incorrect footer. Was " + browserTimingFooter + " expected to start with "
+ getTimingFooterStart(), browserTimingFooter.startsWith(getTimingFooterStart()));
tx.getTransactionActivity().tracerFinished(tracer, 0);
}

@Test
public void testGetBrowserTimingFooterForIgnoredTransaction() throws Exception {
ApiTestHelper.mockOutServiceManager();
Expand Down Expand Up @@ -1659,27 +1578,9 @@ public void testGetBrowserTimingShortFooterForIgnoredTransaction() throws Except

@Test
public void testGetBrowserTimingFooterRUM4() throws Exception {
Map<String, Object> connectionResponse = new HashMap<>();
connectionResponse.put(ApiTestHelper.BROWSER_KEY, "3969ca217b");
connectionResponse.put(ApiTestHelper.BROWSER_LOADER_VERSION, "248");
connectionResponse.put(ApiTestHelper.JS_AGENT_LOADER, ApiTestHelper.LOADER);
connectionResponse.put(ApiTestHelper.JS_AGENT_FILE, "js-agent.newrelic.com\nr-248.min.js");
connectionResponse.put(ApiTestHelper.BEACON, "staging-beacon-2.newrelic.com");
connectionResponse.put(ApiTestHelper.ERROR_BEACON, "staging-jserror.newrelic.com");
connectionResponse.put(ApiTestHelper.APPLICATION_ID, 100L);

ApiTestHelper.mockOutServiceManager(connectionResponse);

Transaction tx = Transaction.getTransaction();
BasicRequestRootTracer tracer = createDispatcherTracer();
tx.getTransactionActivity().tracerStarted(tracer);

NewRelic.getBrowserTimingHeader();
String browserTimingFooter = NewRelic.getBrowserTimingFooter();
Assert.assertTrue("Incorrect footer. Was " + browserTimingFooter + " expected to start with "
+ getTimingFooterStart(), browserTimingFooter.startsWith(getTimingFooterStart()));
tx.getTransactionActivity().tracerFinished(tracer, 0);

// The getBrowserTimingFooter API is deprecated and now only returns an empty String
Assert.assertEquals("", NewRelic.getBrowserTimingFooter());
Assert.assertEquals("", NewRelic.getBrowserTimingFooter("123"));
}

private String getTimingFooterStart() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ public class ApiTestHelper {
+ "var f=a(\"handle\"),g=window,h=g.document,i=\"readystatechange\",j=\"addEventListener\",k=\"attachEvent\",l=(\"\"+location).split(\"?\")[0],"
+ "m=b.exports={offset:e(),origin:l};h[j]?(h[j](i,d,!1),g[j](\"load\",c,!1)):(h[k](\"on\"+i,d),g[k](\"onload\",c)),"
+ "f(\"mark\",[\"firstbyte\",e()])},{handle:\"4O2Y62\"}],loader:[function(a,b){b.exports=a(\"YLUGVp\")},{}]},{},[\"YLUGVp\"]);";
static final String HEADER = "\n<script type=\"text/javascript\">" + LOADER + "</script>";
static final String JAVASCRIPT_AGENT_SCRIPT = "\n<script type=\"text/javascript\">window.NREUM||(NREUM={});NREUM.info={\"errorBeacon\":" +
"\"staging-jserror.newrelic.com\",\"licenseKey\":\"abcd\",\"agent\":\"js-agent.newrelic.com\\nr-248.min.js\"," +
"\"beacon\":\"staging-beacon-2.newrelic.com\",,\"applicationID\":\"100\"," +
"\"transactionName\":\"MwADMBAECxUCAhIMDQpKNBYLSjAICA8JEgw=\",\"queueTime\":0};\n" +
LOADER + "</script>";
// From BeaconConfiguration
public static final String BROWSER_KEY = "browser_key";
public static final String BROWSER_LOADER_VERSION = "browser_monitoring.loader_version";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,38 +35,56 @@ public class BrowserConfig extends BaseConfig {
public static final String ERROR_BEACON = "error_beacon";
public static final String APPLICATION_ID = "application_id";

private static final String HEADER_BEGIN = "\n<script type=\"text/javascript\">";
private static final String HEADER_END = "</script>";

private final BrowserFooter footer;
private static final String SCRIPT_BEGIN = "\n<script type=\"text/javascript\"";
private static final String SCRIPT_END = "</script>";
static final String JS_AGENT_CONFIG_STR_PREFIX = "window.NREUM||(NREUM={});NREUM.info=";
private final String jsAgentLoader;
private final String header;
private final JavaScriptAgentConfig javaScriptAgentConfig;

private BrowserConfig(String appName, Map<String, Object> props) throws Exception {
super(props);
// when rum is turned off on the server, none of the required properties come down
// meaning this will throw an exception
footer = initBrowserFooter(appName);
javaScriptAgentConfig = initJavaScriptAgentConfig(appName);
jsAgentLoader = getRequiredProperty(JS_AGENT_LOADER);
header = HEADER_BEGIN + jsAgentLoader + HEADER_END;
logVersion(appName);
}

private void logVersion(String appName) {
String version = getProperty(BROWSER_LOADER_VERSION);
if (version != null) {
Agent.LOG.log(Level.INFO, MessageFormat.format("Using RUM version {0} for application \"{1}\"", version,
appName));
public static BrowserConfig createBrowserConfig(String appName, Map<String, Object> settings) throws Exception {
if (settings == null) {
settings = Collections.emptyMap();
}
return new BrowserConfig(appName, settings);
}

private BrowserFooter initBrowserFooter(String appName) throws Exception {
public String getBrowserAgentHeaderScript(BrowserTransactionState state) {
return getBrowserAgentHeaderScript(state, null);
}

public String getBrowserAgentHeaderScript(BrowserTransactionState state, String nonce) {
return SCRIPT_BEGIN + (nonce != null ? " nonce=\"" + nonce + "\">" : ">")
+ JS_AGENT_CONFIG_STR_PREFIX
+ javaScriptAgentConfig.getConfigString(state)
+ ";\n"
+ jsAgentLoader
+ SCRIPT_END;
}

private JavaScriptAgentConfig initJavaScriptAgentConfig(String appName) throws Exception {
String beacon = getRequiredProperty(BEACON);
String browserKey = getRequiredProperty(BROWSER_KEY);
String errorBeacon = getRequiredProperty(ERROR_BEACON);
String payloadScript = getRequiredProperty(JS_AGENT_FILE);
String appId = getRequiredProperty(APPLICATION_ID);
return new BrowserFooter(appName, beacon, browserKey, errorBeacon, payloadScript, appId);
return new JavaScriptAgentConfig(appName, beacon, browserKey, errorBeacon, payloadScript, appId);
}

private void logVersion(String appName) {
String version = getProperty(BROWSER_LOADER_VERSION);
if (version != null) {
Agent.LOG.log(Level.INFO, MessageFormat.format("Using RUM version {0} for application \"{1}\"", version,
appName));
}
}

private String getRequiredProperty(String key) throws Exception {
Expand All @@ -77,33 +95,4 @@ private String getRequiredProperty(String key) throws Exception {
}
return val.toString();
}

public String getBrowserTimingHeader() {
return header;
}

public String getBrowserTimingHeader(String nonce) {
// nonce should change per request so we cannot pre-build this string
return "\n<script type=\"text/javascript\" nonce=\""
+ nonce
+ "\">"
+ jsAgentLoader
+ "</script>";
}

public String getBrowserTimingFooter(BrowserTransactionState state) {
return footer.getFooter(state);
}

public String getBrowserTimingFooter(BrowserTransactionState state, String nonce) {
return footer.getFooter(state, nonce);
}

public static BrowserConfig createBrowserConfig(String appName, Map<String, Object> settings) throws Exception {
if (settings == null) {
settings = Collections.emptyMap();
}
return new BrowserConfig(appName, settings);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ public interface BrowserTransactionState {

String getBrowserTimingHeaderForJsp();

String getBrowserTimingFooter();

String getBrowserTimingFooter(String nonce);

String getTransactionName();

Map<String, Object> getUserAttributes();
Expand Down
Loading
Loading