Over the past year, Webmasters have seen the hype about wireless technologies come and go. For a while, anything wireless was the hot new thing, but just as with B2C and B2B, the hype machine for WML and WAP has slowed down lately. However, even if wireless technologies aren’t making headlines, you can still harness them to create powerful applications. WAP and WML are great ways to provide information to consumers and to employees working outside the office.

In this article, we’ll look at how you can harness the power of WML and WAP with Perl. Because these technologies are based on XML and HTTP, you’ll find yourself quickly getting up to speed and ready to create dynamic content for the wireless Web. We’ll look at sample code for both creating Web applications in regular CGI and under the Mason component language. Let’s start by looking at how to build wireless applications.

The basics of WAP
Currently, the wireless technology that has gotten the widest deployment is WAP, or Wireless Application Protocol. WAP is a wide set of technologies that provides wireless service to handheld phones, terminals, and other devices. While WAP specifies all the technologies required to provide content to the wireless client, our concern here is about interacting with clients over the HTTP protocol. HTTP 1.1 is the underlying protocol for requesting content over a wireless network, so any HTTP 1.1 server is capable of providing wireless content.

However, just because you can use a standard Web server doesn’t mean that all of your existing content will work. WAP defines a new XML-based markup language called Wireless Markup Language (WML) for encoding your data. Just as HTML is a markup language that defines how your data renders and behaves on a Web browser, WML defines how your data renders and behaves on a wireless device.

Getting started
The first thing you should do is read up on WML. Because WML is based on XML, be prepared to deal with the strict XML rules on document structure. WML also differs greatly from HTML; check out Builder.com’s coverage for more information.

Once you’re up to speed on XML and WML, you’ll need a WAP device to test with. While a WAP-capable mobile phone is great for last-minute tests, you’ll probably want to do most of your work in a software simulator. Take a look at AllNetDevices‘ list of simulators or head to Openwave’s developer site to download one of the most robust simulators.

Now that you have the tools and the technology down, let’s look at some code.

Dynamic CGI scripts for WAP
Because WAP uses HTTP, you follow the same design strategies when creating dynamic content for wireless clients as you do for regular Web browsers. In fact, you could say that the processes are the same, except in specific details. First, while Web browsers expect the content type text/html, wireless clients require text/vnd.wap.wml. Second, because the result is an XML file rather than an HTML file, you need to print out the XML declarations. Once you’ve done that, you’re ready to start serving content. Here is a simple CGI script that serves up a WML deck.

print “Content-type: text/vnd.wap.wml\n\n”;
print “<?xml version=\”1.0\” encoding=\”iso-8859-1\”?>\n”;
print “<!DOCTYPE wml PUBLIC \”-//WAPFORUM//DTD WML 1.1//EN\” \”http://www.wapforum.org/DTD/wml_1.1.xml\”>\n”;
print “<wml>\n”;
print “   <card id=’card1′>\n”;
print “     <p>Welcome to my wireless cgi</p>\n”;
print “   </card>\n”;
print “</wml>\n”;

This minimal example illustrates how to format the HTTP and XML headers. Using XML, you’ll need to be exact with the output of your CGI script. Whereas Web browsers are very forgiving about content types and header declarations, WAP clients are not.

The output from the above script, while generated on the fly, isn’t very dynamic–it sends the same output regardless. Instead, we can use the CGI library to read input from the client and build new decks based on that information. WML tries to be as self-sufficient as possible, enabling each deck to move from card to card without requiring a new HTTP request. But this means that you need to track your WML inputs carefully and ensure that the last card sends all of the variables to the server.

This relatively simple sample script reads input from the client over three different cards and sends it to the server to build a new deck. It records an input in each card and sends each to the server as an HTTP get request. Notice that in the card finish, we precede every WML variable, such as $first_name, with a slash, in order to prevent Perl from trying to use it as a Perl variable. Once the variables are sent to the server, the CGI script can access them like it can any other CGI variable.

Control caching on the client
Because WAP devices run over low-bandwidth connections, they are aggressive about caching. Just sending CGI parameters to a CGI script is not enough to ensure that the results will not get cached. The wireless device will store them, keyed by the request URL, and return the cached version for subsequent requests to that URL without checking with the server to see if a newer version is available.

We can see this in the following example. This script returns the time and date to the WAP device as a WML deck, with an OK button to request the date again from the server. It also asks for the user’s name, in order to customize the response.
use CGI;
use Date::Format;

print “Content-type: text/vnd.wap.wml\n\n”;
print “<?xml version=\”1.0\” encoding=\”iso-8859-1\”?>\n”;
print “<!DOCTYPE wml PUBLIC \”-//WAPFORUM//DTD WML 1.1//EN\” \”http://www.wapforum.org/DTD/wml_1.1.xml\”>\n”;

my $input = new CGI;
my $time = time2str(“%a %b %e %T %Y\n”, time);

print “<wml>\n”;
print ”   <card id=\”time_and_date\”>\n”;
print ”    <do type=\”accept\” label=\”Ok\”>\n”;
print ”         <go method=\”get\” href=\”?\”>\n”;
print ”            <postfield name=\”name\” value=\”\$name\”/>\n”;
print ”         </go>\n”;
print ”      </do>\n”;
if ($input->param(“name”)) {
   print ”   <p>”.$input->param(“name”).”, </p>\n”;
print ”      <p>Time is: “.$time.”</p>\n”;
print ”      <p>Please enter your name:\n”;
print ”      <input name=\”name\” emptyok=\”false\”/></p>\n”;
print ”   </card>\n”;
print “</wml>\n”;

When we run the card with a simulator, we see the following entries in the simulator’s request log:
cache miss: http://wap.server.com/wml4.cgi
cache miss: http://wap.server.com/wml4.cgi?name=James
cache hit: <http://wap.server.com/wml4.cgi?name=James>
cache hit: <http://wap.server.com/wml4.cgi?name=James>

The first request, without any CGI parameters, initiated a network request and returned the current time. The same was true for the next request, with a CGI parameter. However, the device made no network requests for the two subsequent requests, and the user received invalid data.

There are two ways to force a device to make a network request for a deck. The first is to manually add HTTP headers that say the deck is already expired and must be revalidated. In our sample script above, we extend the Content-type header with more headers, forcing a network request:
print “Content-type: text/vnd.wap.wml\n”;
print “Last-Modified: ” . time2str(“%a, %e %b %Y %T”, time) . ” GMT\n”;
print “Expires: ” . time2str(“%a, %e %b %Y %T”, time) . ” GMT\n”;
print “Cache-Control: no-cache, must-revalidate\n”;
print “Pragma: no-cache\n\n”;

The second way is to enforce revalidation from within the deck’s WML by adding <meta> commands in the <head> portion:
<meta http-equiv=”Cache-Control” content=”max-age=60″ forua=true />
<meta http-equiv=”Cache-Control” content=”must-revalidate” forua= true/>
<meta http-equiv=”Cache-Control” content=”no-cache” forua= true/>

These tags produce the same results as the modified HTTP headers.

WAP request headers
Now that you know the essentials of creating wireless content, you’ll have to customize your content for different phones. One of the biggest difficulties you’ll face is that each WAP browser has a slightly different set of capabilities. Luckily, the headers sent from the wireless device to the server will indicate what the device is capable of. Let’s look at a dynamic deck that lets you know all of the information that the browser sends to the server.
use CGI;

print “Content-type: text/vnd.wap.wml\n\n”;
print “<?xml version=\”1.0\” encoding=\”iso-8859-1\”?>\n”;

print “<!DOCTYPE wml PUBLIC \”-//WAPFORUM//DTD WML 1.1//EN\” \”http://www.wapforum.org/DTD/wml_1.1.xml\”>\n”;

my $input = new CGI;

print “<wml>\n”;
print ” <card id=\”time_and_date\”>\n”;
foreach $header (%ENV) {
   if ($header =~ /^HTTP/) {
      print ” <p>$header: $ENV{$header}</p>\n”;
print ” </card>\n”;
print “</wml>\n”;

The headers from each client are pretty uniform, and you can safely ignore some of them. However, a few are particularly useful. For example, like Web browsers, WAP browsers send a User-Agent string identifying the software that the phone is operating. Perl surfaces the header as HTTP_USER_AGENT in the format BROWSER/VERSION.

This is particularly useful because not all browsers accept WAP content. You may find browsers that accept only HDML instead of WML. You can create an index of browsers to exclude, or you can look in the HTTP_ACCEPT header for the string text/vnd.wap.wml, without which, the browser is not WML compatible.

Also, the header HTTP_X_UP_SUBNO is a globally unique identification number for the phone. You can safely use its value to track the phone without setting a cookie. Unlike Web browsers, which have no unique identifier, this identification number is the same every time the phone reconnects to your server.

Serving WAP with HTML::Mason
If you’ve already built your site upon HTML::Mason, never fear–you can still serve WML content to WAP browsers. Most of what you have read still applies, but you do have to write the Mason components slightly differently.

Most importantly, you must remember to make the XML declaration the very first line of Mason code that gets interpreted. If any other characters come between the HTTP header and the XML declaration, WAP devices won’t read your code. The only other major change is in the <%init> section, where the HTTP content type must match what the WAP device requires.

To see how a WAP script might be written in Mason, compare our original dynamic CGI example with this Mason component version.


By now, you should be ready to start building wireless Web applications. As more and more providers increase wireless Web access, it becomes a more useful tool. WAP certainly can’t deliver content to match what you can deliver over the Web, but it does grant a flexible environment for writing applications. Hopefully, you’ll be able to put Perl-powered wireless applications to use within your business.