TAG | Scripting
14
Eliminating concurrent access to sensitive data
No comments · Posted by Patrick Lightbody in Load Testing Tips
We recently had a customer from a large clothing retailer ask us if there was any way to ensure that data, such as a username/password combination, could be restricted such that it was “checked out” and available only for a specific concurrent user. This is very common with logins, where systems often will prevent concurrent logins from multiple IP addresses.
While BrowserMob does not have a concept in which data rows can be “checked out”, some simple scripting can achieve the same results. The key is in creative use of the browserMob.getUserNum() and browserMob.getTxCount() APIs. You can learn more about them by reading up on the BrowserMob APIs.
The getUserNum function returns 0, 1, 2, etc based on the concurrent user in your load test. So if you have a 100 user test, getUserNum will return between 0 and 99. It’s important to understand that it will return the same value for the same user throughout the test.
The getTxCount function returns 1, 2, 3, etc based on the number of cycles for that specific user. This number will effectively be a counter of the unique number of transactions that that particular user has executed. So user 1 and user 100 will both have a getTxCount of 1 returned, but by the time user 100 sees it, user 1 might already be on transaction 50.
Now suppose you want to run a 1000 user test in which you never concurrently log in with the same user. All you need to do is pre-create 1000 user accounts and then write your script like so:
var userId = browserMob.getUserNum(); var username = "test-" + userId; var password = "password"; selenium.type("username", username); selenium.type("password", password);
This works great, but what if you want to use more than 1000 logins? Suppose you want to use up to 10,000 logins among the 1000 user test? This is where the getTxCount function comes in to:
var loginsPerUser = 10; var userNum = browserMob.getUserNum(); var txCount = browserMob.getTxCount(); var userId = userNum * loginsPerUser + txCount % loginsPerUser; var username = "test-" + userId; var password = "password"; selenium.type("username", username); selenium.type("password", password);
What this does is allocate 10 logins per concurrent user. So user 1 will get usernames test-0, test-1, …, test-9 while user 8 will get usernames test-80, test-81, …, test-89, etc. Because of the mod call (%) the ten usernames will simply wrap around once they’ve been used.
13
Advanced handling of page timeouts in Selenium
1 Comment · Posted by Patrick Lightbody in Uncategorized
Because both our load testing and website monitoring services are based on Selenium, we have a unique ability to measure the performance of things like page load times, AJAX timings, and other in-browser interactions.
Selenium has both a setTimeout command and a waitForPageToLoad command. Both can be given a timeout value, which will control how long Selenium waits for a given page to load or element to appear. When it comes to using our services, most people stick with the default time of 30 seconds. If the timeout is reached, an error is thrown, the script aborts, and the transaction is recorded.
However, sometimes people want to know when pages take more than X seconds to load, but don’t want to necessarily interrupt the flow of the script. In fact, just last week we got this request from a customer:
Ability to trigger an alert based on a set threshold (by the user) – not using the timeout. This basically came from the performance issues we are experiencing. Here’s the scenario:
- We have a specific page that takes 2+ minutes to load.
- The page load timeout in the Selenium script was set to 60s.
- BMob properly reported the “timeout” error but when this error happens, BMob quits the script.
- This is not ideal for me since I want to still be able to see how long the page takes to load.
- Increasing the page load timeout for the page in question works, but now I don’t have a way (that I know of) to still trigger an alert after 60s.
I should be able to set a threshold for any page I choose that would then send a notification alert.
In other words, this customer wanted for a way to still report a transaction as a failure and receive an alert, but also still allow the script to continue. Fortunately, our support for JavaScript as a scripting language provides the answer:
var timeout = 90000; ... // start of script ... var start = new Date().getTime(); selenium.waitForPageToLoad(timeout); var end = new Date().getTime(); ... // rest of script ... if ((end - start) > 45000) { throw "An important page took longer than 45 seconds to load"; }
What this script does is sets the timeout to a very long amount (1.5 minutes) but will still report an error if a specific page takes longer than 45 seconds. This allows the remainder of the script to execute even when the page takes more than a specified 45-second threshold.
The only problem with this script is that if the page takes longer than 90 seconds, then the rest of the script will still not run because waitForPageToLoad will throw an exception. You can solve that too with a little code:
var start = new Date().getTime(); try { selenium.waitForPageToLoad(timeout); } catch (e) { // this will happen after 90 seconds // todo: recover and send the browser to the the next URL } var end = new Date().getTime();
The only thing that is important to remember with this use of try/catch is that you’ll need to properly recover from the error. Simply catching the error and trying to continue may not work. For example, if the next Selenium command requires clicking on a button that should have loaded from the last page, there may be no way to recover. However, if the next step is simply visiting a new URL, you could possibly get away with a simple open() command in the catch block.
When setting up monitoring jobs, there are often predictable time periods in which you want to change the behavior of a script or prevent it from running at all, without having to manually stop/start the monitoring job each time. For instance, you might want to prevent errors and alert emails during routine maintenance windows, or perhaps you’re only interested in site performance on weekdays during regular business hours. We’ve come up with solutions to a few common situations that will help jump start your scripts.
Overview
At the core of all these examples is a small configuration object that sits at the top of the script for quick changes, and a single function that returns true or false depending on whether or not the current time is within a blackout period. The following is some pseudo-code outlining one way in which you might choose to implement these examples in your own scripts. Don’t forget to add in the appropriate maintenance object and isInMaintenance() function from the examples below to make this work.
// The configuration object for the maintenance window var maintenance = {...} // The maintenance function (plug in the appropriate function) var isInMaintenance = function() { ... } var selenium = browserMob.openBrowser(); if(!browserMob.isValidation() && isInMaintenance()) { // Active maintenance window browserMob.log("Is in maintenance!"); } else { // Run normal script browserMob.beginTransaction(); browserMob.beginStep("Step 1"); selenium.open("http://google.com/search?q=website+monitoring"); ... browserMob.endStep(); browserMob.endTransaction(); }
Web Page Check
The most straight forward way to check for a maintenance window is to have Selenium load a web page and look for an element that’s only present during scheduled down time. You’ll want to be careful not to choose something that is also present when the site is really in trouble or you will never get your alerts!
var maintenance = { check: { url: "http://example.com/home", element: "//img[@title=\"maintenance\"]" } } var isInMaintenance = function() { if(maintenance.check && maintenance.check.url != null && maintenance.check.element != null) { selenium.open(maintenance.url); return selenium.isElementPresent(maintenance.element); } return false; }
Multipe Date Ranges
One of the most common requests we get is for a way to handle scheduled maintenance windows. These windows tend to be one-off date ranges decided well in advance of the actual event. Given a set of start/end dates, you could prevent monitoring errors and email alerts during the blackout periods. When setting the dates, don’t forget to include the time zone abbreviation in the argument string or the script will use the local time of the machine it’s running on.
var maintenance = { dates: [ {start: new Date("05/12/2010 23:00 PDT"), end: new Date("05/13/2010 02:30 PDT")}, {start: new Date("05/24/2010 23:00 PDT"), end: new Date("05/25/2010 02:30 PDT")} ] } var isInMaintenance = function() { var now = new Date(), nowTime = now.getTime(), maintenanceWindows = []; // Check for maintenance date ranges if(maintenance.dates && maintenance.dates.length) { maintenanceWindows = maintenance.dates; } for(var i in maintenanceWindows) { var m = maintenanceWindows[i]; if(nowTime >= m.start.getTime() && nowTime < m.end.getTime()) { return true; } } return false; }
Days of the Week
Here’s an example for blacking out specific days of the week. This will allow you to create a script that, for example, monitors on Mondays, Wednesdays, and Fridays, or only on business days. Simply provide a time zone offset, which is the number of minutes your time zone deviates from coordinated universal time (a.k.a. UTC, GMT, Zulu time), and the days of the week you don’t want to run the script (from 0-6, where 0 is Sunday, 1 is Monday, etc.).
var maintenance = { timezoneOffset: 240, // UTC offset in minutes (eg: NYC (GMT-4) is 4 * 60 days: [0, 6] // Sunday = 0, Saturday = 6 } var isInMaintenance = function(){ var now = new Date(), nowTime = now.getTime(), timezoneOffset = maintenance.timezoneOffset || 0, localTimezoneOffset = now.getTimezoneOffset(), timezoneDiff = localTimezoneOffset - timezoneOffset; // Check for full blackout days (such as weekends) if(maintenance.days != null && maintenance.days.length) { var targetDate = new Date(nowTime + (timezoneDiff * 60 * 1000)), targetDay = targetDate.getDay(); for(var i in maintenance.days) { if(maintenance.days[i] == targetDay) { return true; } } } return false; }
Time of Day
This example shows how you can have a daily blackout range. This code will parse the start/end strings into a daily time range adjusted to the target time zone. Use this if you only want to monitor during banker’s hours, or if you want to prevent a script from running while your database is backing up.
var maintenance = { timezoneOffset: 240, // UTC offset in minutes (eg: NYC (GMT-4) is 4 * 60 hours: {start: "23:30", end: "07:30"} } var isInMaintenance = function(startTime, endTime, offset) { var offset = maintenance.timezoneOffset || 0; var now = new Date(); var nowTime = now.getTime(); var maintenanceWindows = []; var toUtc = function(localTime) { var chunks = localTime.toString().split(":"); var h = parseInt(chunks[0], 10); var m = parseInt(chunks[1], 10) || 0; var d = new Date(); d.setHours(h); d.setMinutes(m + offset); return d.getHours() + ":" + d.getMinutes(); } var sH, eH, sM, eM; startTime = toUtc(maintenance.hours.start); var chunks = startTime.toString().split(":"); sH = parseInt(chunks[0], 10); sM = parseInt(chunks[1], 10) || 0; endTime = toUtc(maintenance.hours.end); chunks = endTime.toString().split(":"); eH = parseInt(chunks[0], 10); eM = parseInt(chunks[1], 10) || 0; var startDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), sH, sM, 0, 0); var endDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), eH, eM, 0, 0); if(startDate.getTime() <= endDate.getTime()) { // Single range mid-day maintenanceWindows.push({start: startDate, end: endDate}); } else { // Date wraps, which means two blackout ranges in a single day var morning = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0), midnight = new Date(morning.getTime() + (24 * 60 * 60 * 1000)); maintenanceWindows.push({ start: morning, end: endDate }); maintenanceWindows.push({ start: startDate, end: midnight }); } for(var i in maintenanceWindows) { var m = maintenanceWindows[i]; if(nowTime >= m.start.getTime() && nowTime < m.end.getTime()) { return true; } } return false; }
Now, a few of you might have noticed that the above examples are compatible with one another; you can mix-and-match them as you see fit. Just combine the various maintenance object fields into one config object, combine the contents of all the maintenance methods you want to use (just watch not to overwrite the variable declarations), and you can create something that fits just about any situation.
downtime · javascript · maintenance · maintenance windows · Monitoring · RBU · Scripting · scripts
8
Selenium, BASIC authentication, and how to get it to work in BrowserMob
No comments · Posted by Patrick Lightbody in Uncategorized
BASIC authentication is used to provide minimal, low-security protection from anonymous visitors hitting your website. It is frequently used by companies to ensure that their staging or development environments are not accessibly by the general public prior to pushing the changes to production. Typical authentication dialog prompts look like so:

The challenge here is that this dialog box is the kind of dialog that Selenium cannot automate. You cannot issue “type” or “click” commands on it. In fact, if this box comes up, your script is guaranteed to time out because Selenium will continue to wait for the page to load, not realizing a login is required and unable to populate it.
Traditionally Selenium users have worked around this problem by using a URL pattern in which the username and password was encoded:
http://username:password@example.com
Recently, however, this is not working across all browsers. IE no longer supports it and recent versions of Firefox add a confirmation box that breaks the automation with a slightly different popup. And while we’re working on as solution for Selenium 2.0, there aren’t a lot of options for Selenium today.

But when it comes to BrowserMob, we do have a solution available today, which will be soon back-porting to the Selenium 2 codebase. Simply edit your script to look like the following:
var selenium = browserMob.openBrowser(); var c = browserMob.getActiveHttpClient(); c.autoBasicAuthorization("example.com", "username", "password"); browserMob.beginTransaction(); browserMob.beginStep("Step 1"); selenium.open("http://example.com"); // rest of script...
This code tells BrowserMob to automatically put in the required Authorization headers for any HTTP request issued to example.com. And soon this capability will be added to Selenium 2 (after we get the first alpha out in time for the holidays).
