YUI DataSource with dynamic oRequest

I preface this by asserting that I consider myself expert neither in YUI nor in JavaScript. There may be a better way to do this.

YUI (which I love) has an excellent abstract DataSource component that normalizes requests for data to something compatible with all their widgets. I find it generally useful as well, even when I'm not using, for example, a YUI DataTable to display data.

Last weekend I wrote a UI notification subsystem for Zenoss, based on YUI and Yowl. I used a DataSource to handle communication with the server. DataSources can be told to poll the server periodically, using the setInterval method:

myDataSource.setInterval(1000*60, "?var=123", callback)

This is all well and good, but in my case, I needed that second argument (oRequest) to be different each time, based on previous server responses; once setInterval is called, however, the same request parameters are used each time.

I got around this by writing an object extending XHRDataSource that would accept a callable for oRequest, which would be called each time it made an external connection. Turned out to be very simple (although I ran into "too much recursion" bug, described here. I used the fix at the end of the thread to get around it):

var Y = YAHOO.util; // Internal shortcut to save typing and lookups
CallableReqDS = function(oLiveData, oConfigs) {
// Workaround for bug #2176072 in YUI 2.6.0
this.constructor = Y.XHRDataSource;
// Chain constructors
CallableReqDS.superclass.constructor.call(this, oLiveData, oConfigs);
// Set constructor back (also part of fix)
this.constructor = CallableReqDS;
}
YAHOO.lang.extend(CallableReqDS, Y.XHRDataSource);

CallableReqDS.prototype.makeConnection = function(oRequest, oCallback, oCaller) {
if (typeof(oRequest)=='function') oRequest = oRequest();
CallableReqDS.superclass.makeConnection.call(this, oRequest, oCallback, oCaller)
}

Trivial, really, but it got the job done.


UPDATE: Here's an example. Let's say you want to defeat IE's draconian caching. Standard way to do this is to tack on a unique query string for each request, which you can't do with the default XHRDataSource, because the query string can't change from request to request when using setInterval. So you use this instead:

function getCacheBustingString(){
return "?ms=" + new Date().getTime()
}
var myDS = new CallableReqDS("/path/to/myurl");

myDS.setInterval(60, getCacheBustingString, callback)


That way the cache buster will be unique for each request.

Read More...

DNS with dd-wrt

I'd been running bind on a local server for some time, and was using that to forward DNS requests through my assorted VPN tunnels; but it never worked properly, and I had to set search domains in /etc/resolv.conf manually on all my boxes. I felt this was stupid. I felt that strongly.

When my router died a few weeks ago, I got another, and of course immediately flashed it with dd-wrt; this time around, however, I noticed the DNSMasq options. dd-wrt can use DNSMasq as an alternative to dhcpd. I looked into it. Suffice it to say, I got my local DNS working perfectly for the first time (after some Googling; the docs weren't terribly helpful to a networking novice like me). Multiple search domains, DNS forwarding, everything. Here's what I did.

I have eight boxes locally—a server, two MacBooks, an Apple TV, and four VMs serving as my Zenoss VPN gateway, torrent server (also a VPN gateway, to another endpoint), CentOS dev box, and Zenoss server. Local domain is "mcb.loc", but I also interact extensively with the "zenoss.loc" domain, resolution requests for which I need forwarded through the Zenoss VPN connection to another DNS server.


First, I set up all my static DHCP leases—the last time I changed my subnet I cursed myself for having my boxes set their own IPs. That's done under the "Services" tab in dd-wrt. Next, I checked "Use DNSMasq for DHCP" and "Unse DNSMasq for DNS" and added my router's IP as "Local DNS", all under the "Setup" tab.
Routing was set up so that appropriate traffic would go through the VPN gateways (see how to do that here and here).

Then it was time for the DNSMasq setup. As I said before, docs were somewhat minimal and geared towards people not me. Several directives needed to be added to the "Additional DNSMasq Options" box under the "Services" tab. First, the easy ones:
local=/mcb.loc/
expand-hosts

These set up my local domain and tell it to treat requests to, for example, "iansmbp" as "iansmbp.mcb.loc". This got me halfway there. DNSMasq adds hostnames defined under the static leases section to DNS automatically, so local DNS was working perfectly. I still needed to add "zenoss.loc" as a search domain and have it forward requests to the remote DNS server. This was pretty difficult to find via Google, at least under the search terms I thought were appropriate at the time, but eventually I got it:
server=/zenoss.loc/10.175.211.10

That forwards all "zenoss.loc" lookups to the IP specified, which is the Zenoss DNS over the VPN.
dhcp-option=15,"zenoss.loc mcb.loc"


That was the part that was hard to find, because it isn't a default DNSMasq option. That sets the search domains for the resolv.conf of DHCP clients.

At this point, I renewed leases on my assorted boxes and was good to go! Way less effort than setting up bind, which is overkill for my needs anyway. I have yet to have an issue with this setup.

Read More...