CoffeeScript/Javascript URI parser using the DOM

There are plenty of ways to parse a url in Javascript and I feel like I’ve written the method too many times to keep track. And when I say written, I either mean actually written, or pulled someone’s regex from the net and jammed it into some other wrappers to get the bits that I cared about. The key here is that each time, it was a new endeavor.

The last time I had to solve this problem, I found this gist:

Wow. Super straight forward, and the return value is an object (which I find the biggest failure of the regexp methods).

I took that and wrapped it up into a little tiny module that will easily let me modify the query string params and give me back the URL when I need it. You may notice a couple of if/then kind of checks. I found that Firefox and Chrome don’t quite give back the same results (as noted in the comment stream on the previously mentioned gist). I tried to account for these cases. I’ve tested this on IE8 and it seemed to work just fine.

Note: this is coffee script and depends on underscore. If you try to use this, you either need to handle the coffee script -> js and include underscore in your project or use js2coffee.org and sub out the underscore iterator methods.

To be clear, this method is not fast (check out performance results vs regex at jsperf.com) so if you need to parse piles of urls, this is not for you. This counts on being in a browser context (because it uses document.createElement). If you are working outside of the DOM (like in node.js or server side js), this is not for you. If you only use it a handful of times in an app or on a page, and you want some clean and easy code to read, this may be for you.

Here’s a sample of how you might use it. Take an page url like this

http://myblog.com/blogroll?page=1&style=cool&other_param=whatever

This code:

$('.next_page_button').bind('click', function() {
  var uri_parser = new QueryStringParser(location.href);
  uri_parser.query_params.page = uri_parser.query_params.page + 1;
  window.location.href = uri_parser.toString();
});

will make the .next_page_button take you to page 2 (http://myblog.com/blogroll?page=2&style=cool&other_param=whatever)

We could make a the front page button (where the page attribute is not there) like this.

$('.front_page_button').bind('click', function() {
  var uri_parser = new QueryStringParser(location.href);
  uri_parser.query_params.page = null;
  window.location.href = uri_parser.toString();
});

Until I need this to do some real heavy lifting (read: more performance), I think I may have written my last URL parser in Javascript. Phew.

Testing Events with Jasmine and Prototype

I realize that Prototype may be a bit out of fashion since jQuery has seemingly become the dominant front-end Javascript extension library. I’ve got a project that has a pile of code already written with Prototype, and the idea of converting all that to jQuery just seems like a waste of time. So I’m persisting.

I recently moved that project from Blue-Ridge (now an unsupported basically defunct Javascript testing framework) to Jasmine of which I’m already a huge fan. I was happy to see that from the onset, Jasmine had no real issues with testing Prototype code. After adding the gem to the project, and running `jasmine init`, I had a setup that was including my source code and a place to put my specs. I added a masylum/jasmine-prototype which provides a couple Prototype matchers and functionality to load fixtures.

The first little hurdle I ran into was trying to fire an click to test it’s response. Prototype has a `fire()` method on element. But it only allows firing of custom events. To get around this, I added a couple little Jasmine helpers that allowed me to inspect events that have been bound on an element and fire them.

To use these, either copy the code into your SpecHelper.js file or add a new file and add that helper file to your jasmine.yml and you can start calling it in your tests with something like this:

  describe('close button', function() {
    it("removes the dialog from the DOM", function() {
      jasmine.triggerEvent('#fixture .the_open_window .close_btn', 'click');
      expect($$('#fixture .the_open_window')).toEqual([]);
    });
  });

PS. You’ll notice the _.each() methods here. If you are following along exactly, you’ll also need to include underscore.js.

Bandwidth ain’t everything

I maintain an Ubuntu server for my work. The machine serves up a git repo (for my private projects), a wiki, several development versions of apps in progress, and a CI system. I noticed recently that connection times seemed much slower than they should based on the cable speed of the connection. I decided to investigate.

First, I checked out my connection speed directly via speedtest.net. The recorded speed looked good (~20Mbps in both directions) though watching it in the browser made the numbers seem wrong. The page rendering was slow and clunky.

Next I checked a traceroute to anywhere. Each hop was quick (10s of ms) though the command itself took many seconds:

% time traceroute www.ucsd.edu
traceroute to www.ucsd.edu (132.239.180.101), 30 hops max, 60 byte packets
 1  wp.comcast.net (10.1.10.1)  0.518 ms  1.619 ms  1.950 ms
 2  73.69.138.1 (73.69.138.1)  13.784 ms  13.773 ms  31.279 ms
 3  te-4-3-ur01.sffolsom.ca.sfba.comcast.net (68.85.100.9)  12.753 ms  16.303 ms  16.777 ms
 4  te-1-2-0-0-ar01.sfsutro.ca.sfba.comcast.net (68.86.143.50)  20.858 ms  20.849 ms  20.838 ms
 ...
17  ucsd.edu (132.239.180.101)  38.968 ms  39.270 ms  39.260 ms

real	1m30.760s
user	0m0.000s
sys	0m0.004s

How could 17hops each at ~30ms or less take 1.5 minutes to execute? I started to do a little reading and came across this article. The basic message is that your ISP DNS servers may not be the best way to go. This reminded me that we recently upgraded to a static IP and at that time, we updated the nameserver for that machine to use Comcast’s recommended DNS.

I looked into checking out that connection with dig.

% time dig +trace www.ucsd.edu

; <<>> DiG 9.7.3 <<>> +trace www.ucsd.edu
;; global options: +cmd
.			43734	IN	NS	k.root-servers.net.
.			43734	IN	NS	l.root-servers.net.
.			43734	IN	NS	m.root-servers.net.
.			43734	IN	NS	a.root-servers.net.
.			43734	IN	NS	b.root-servers.net.
.			43734	IN	NS	c.root-servers.net.
.			43734	IN	NS	d.root-servers.net.
.			43734	IN	NS	e.root-servers.net.
.			43734	IN	NS	f.root-servers.net.
.			43734	IN	NS	g.root-servers.net.
.			43734	IN	NS	h.root-servers.net.
.			43734	IN	NS	i.root-servers.net.
.			43734	IN	NS	j.root-servers.net.
;; Received 512 bytes from 75.75.75.75#53(75.75.75.75) in 13 ms

edu.			172800	IN	NS	f.edu-servers.net.
edu.			172800	IN	NS	d.edu-servers.net.
edu.			172800	IN	NS	g.edu-servers.net.
edu.			172800	IN	NS	c.edu-servers.net.
edu.			172800	IN	NS	a.edu-servers.net.
edu.			172800	IN	NS	l.edu-servers.net.
;; Received 265 bytes from 192.112.36.4#53(g.root-servers.net) in 10030 ms

ucsd.edu.		172800	IN	NS	ns1.ucsd.edu.
ucsd.edu.		172800	IN	NS	ns2.ucsd.edu.
ucsd.edu.		172800	IN	NS	ns0.ucsd.edu.
;; Received 160 bytes from 192.5.6.30#53(a.edu-servers.net) in 10035 ms

www.ucsd.edu.		43200	IN	CNAME	www.dr-link.ucsd.edu.
dr-link.ucsd.edu.	43200	IN	NS	lucifer.ucsd.edu.
dr-link.ucsd.edu.	43200	IN	NS	ns-ucop-alt.ucsd.edu.
dr-link.ucsd.edu.	43200	IN	NS	act-wcs2-old2.ucsd.edu.
dr-link.ucsd.edu.	43200	IN	NS	devilbunny.ucsd.edu.
dr-link.ucsd.edu.	43200	IN	NS	game.ucsd.edu.
;; Received 256 bytes from 128.54.16.2#53(ns1.ucsd.edu) in 10055 ms

real	0m46.218s
user	0m0.008s
sys	0m0.000s

Wow. Check out the highlighted lines and notice that in 3 cases it took 10+ *seconds* to get back an answer from the nameservers. This is unacceptable. I immediately switched my nameservers to my OpenDNS nameservers. What a huge improvement!

% time dig +trace www.ucsd.edu

; <<>> DiG 9.7.3 <<>> +trace www.ucsd.edu
;; global options: +cmd
... SNIPPPED FOR BREVITY ... 
edu.			172800	IN	NS	c.edu-servers.net.
edu.			172800	IN	NS	d.edu-servers.net.
edu.			172800	IN	NS	g.edu-servers.net.
edu.			172800	IN	NS	l.edu-servers.net.
edu.			172800	IN	NS	f.edu-servers.net.
edu.			172800	IN	NS	a.edu-servers.net.
;; Received 265 bytes from 192.112.36.4#53(g.root-servers.net) in 68 ms

ucsd.edu.		172800	IN	NS	ns1.ucsd.edu.
ucsd.edu.		172800	IN	NS	ns2.ucsd.edu.
ucsd.edu.		172800	IN	NS	ns0.ucsd.edu.
;; Received 160 bytes from 192.41.162.30#53(l.edu-servers.net) in 85 ms

www.ucsd.edu.		43200	IN	CNAME	www.dr-link.ucsd.edu.
dr-link.ucsd.edu.	43200	IN	NS	game.ucsd.edu.
dr-link.ucsd.edu.	43200	IN	NS	ns-ucop-alt.ucsd.edu.
dr-link.ucsd.edu.	43200	IN	NS	devilbunny.ucsd.edu.
dr-link.ucsd.edu.	43200	IN	NS	act-wcs2-old2.ucsd.edu.
dr-link.ucsd.edu.	43200	IN	NS	lucifer.ucsd.edu.
;; Received 256 bytes from 128.54.16.2#53(ns1.ucsd.edu) in 33 ms


real	0m0.270s
user	0m0.004s
sys	0m0.000s

With this very simple change, the server connection now feels like it should. If you find you’re having issues with network connectivity that doesn’t seem to agree with your expected connection speed, look into your DNS servers. You may be able to improve your overall network performance with this very simple change.

Note: Though I haven’t had any issues with OpenDNS going down, it’s a good idea to keep your ISP’s nameserver on record since you are paying for it and it will work as a backup. If you want to try out a switch, you can check out Google’s public DNS. I found that it gave comparable results to OpenDNS.