Developing Feather-Weight Webservices with JavaScript

Remotely called JavaScript with local configuration

There are two major parts involved for any web service. There is the service program itself and there is the client, or pseudo-client, code. For a JavaScript featherweight web service you need some JavaScript on your host, the service, and you need an API for the client, a script in a user’s HTML, to pass information to the service and get a response.

Using a remotely called piece of JavaScript with local configuration usually requires two steps.

  1. The client writes a bit of JavaScript which defines the configuration variables; the request.
  2. Import the remote script, the services proper, with <script src="http://remote-source/...js" which will use the configuration information/variables from the first script to do something.

The service code is usually going to be on a different web server than the client code but there’s no reason it has to be. You could even design in-house only services which use Apache, or another server, directives to deny use of the service to outside clients.

You probably saw this next bit coming.

Part 1, client’s local configuration/request

<script type="text/javascript">
<!-- 
  to_whom = 'world';
//-->
</script>

Part 2, call remote service/script

<script type="text/javascript"
  src="http://elektrum.org/js/hello.js">
</script>

This means, of course, that we have to have a remote script to call.

Part 2.5, the remote service/script itself

// fail gracefully, ie: do nothing if used incorrectly
var to = window.to_whom;
if ( to ) {
  // prevent cross-site scripting attacks
  to = unescape( to.toString() );
  to = to.replace(/</g, '&lt;');
  to = to.replace(/>/g, '&gt;');
  // send our output along
  document.write( '<b><i>Hello ' + to + '!</i></b>');
}

That bit of code lives on the remote server in the web path /js/hello.js. The client will typically not see it, though it’s not possible to hide it if they want to view the source. Just know that most clients won’t care what it looks like, just what they need to feed it and what its URI is.

The reason we get the to out of the to_whom attribute of the window object instead of plain old to_whom, which does work, is that if to_whom is never defined in the configuration script, we’ll get a run time error. It’s also a good habit to scope all your variables with var even if you don’t (and can’t really) require it for the client’s code.

Important!

Never, never, never echo user or client input back to the document without sanitizing or filtering it. Above we unescape the string and then replace out any tag markers with html entities. The order matters. There may be URI encoded HTML left if you save the unescape for last.

If you write user input to the screen without filtering it, it lets someone inject <script> tags into your script. This is called cross-site script attacking.

If you want to write services that others will use without earning yourself a reputation as a dangerous idiot, you must be careful about this.

An aside on try and catch

While try and catch are valuable parts of many languages they are too often used in place of good and thorough coding. It is difficult to envision all misuse cases and everything that can go wrong. It doesn’t mean it shouldn’t be the goal. If you can account for it up front and you’re not doing on the fly code building and evaling you may never need to use try.

Do, or do not. There is no try.
–Yoda, Empire Strikes Back

We could wrap our variable use in a try block instead of getting it through the window. We’d rather set it up so it won’t ever break instead of setting it up expecting it to break.

All together now

<script type="text/javascript">
<!-- 
  to_whom = 'world';
//-->
</script>

<script type="text/javascript"
  src="http://elektrum.org/js/hello.js">
</script>

The hotly anticipated output

A few words about why this works

It’s important to note why this works because it has programming implications. JavaScript has no trouble trouble sharing variables across the boundaries of different scripts, if they aren’t lexically declared inside classes, or in blocks (functions, loops, etc) with var. This is because JavaScript lets you declare variables globally; meaning they are visible, usable, and mutable from every scrap of JavaScript present or later introduced by you or others.

Even if we’d done something like this instead.

<script type="text/javascript">
<!-- 
  var to_whom = 'world';
//-->
</script>

It would still work. The var to_whom declaration should, in our opinion, make to_whom invisible to other scripts, including the script imported, but doesn’t because it’s at the global level. Everything formally declared or created out in the open is at the top of the pan-DOM context.

The implications of this include global pollution. You might, for example, want your user to be able to use a simple variable name like Size, index, Color, or even i. It’s not reasonable with this technique. It’s possible, it’s just unsafe. If you have other scripts in the page, scripts calling scripts in the background, or a code blocks 10 levels deep where you forgot the name was in use already, you’ll end up with name collisions. Which will mean mysterious failures and bugs that can be a migraine to track down and fix.

A wish unfilled

It would be wonderful if we could import the remote script and use it in one step alla:

<script type="text/javascript"
 src="http://elektrum.org/fakeService.js">
 var newThing = fakeImportedFunction();
</script>

If you haven’t guessed, we’ll save you the trouble of trying. It doesn’t work. If there is a src then the text content of the script is ignored.

Google’s Adsense implementation

Google uses the dual script technique Adsense. Here is the code for an ad we use on this site.

<script type="text/javascript"><!--
google_ad_client = "pub-3741595817388494";
google_ad_width = 234;
google_ad_height = 60;
google_ad_format = "234x60_as";
google_ad_channel ="9807480512";
google_color_border = "578A24";
google_color_bg = "CCFF99";
google_color_link = "00008B";
google_color_url = "00008B";
google_color_text = "000000";
//--></script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>

Here is the result when it’s used in a page.

We don’t know, and don’t need to or particularly care, what the code inside http://pagead2.googlesyndication.com/pagead/show_ads.js looks like or how it works, though it’s easy enough to take a peek.

We’re respecting the programming by contract of its API and not messing with the code Google gives us to use. Since we don’t mess with it, we’re not going to break the interface and really don’t need any knowledge of the application beneath.

Looking at the local configuration code you can see how Google is dealing with global namespace issues. They are prefixing all variable names with google_. This is a pseudo-namespace solution that makes the global variable names reasonably safe from namespace collisions. The chances of another script using a google_color_text are slim.

When you see other services delivered via JS, you will invariably, see them delivered this way. Set up script + remote script. And for a little foreshadowing: after this manual makes the rounds, the 1, 2 script combo won’t be the only way you see it done anymore.

Next up, our first service: Pangrams for typographers.

« Featherweight web services · Pangrams for typographers »
Google
 
Web Developing Featherweight Web Services with JavaScript
This is version 0.57b of this manual. It is a beta version with some gaps. We are grateful for feedback.

The code is the manual has not yet been fully tested against Internet Explorer. Bug reports are welcome.
An Elektrum Press Online