Using MacPerl for CGI Programming

By: David Steffen

Hello. No, I'm not Vicki Brown. For personal reasons, Vicki is taking a sabbatical from her responsibilities here at PerlMonth. Vicki is well-nigh irreplaceable. Thus, a small cadre of "MacPerl Scribes" has been formed.

I'm Brian McNett (see my article in issue #6). I've agreed to act as 'point-man' for this community-based effort. As such, I'm not so much authoring the column as arranging the rotation of scribes, and making sure articles are submitted to the editor on time. Most of the articles which will appear here in the coming months, are guest columns which Vicki had already scheduled.

This month's guest columnist is David Steffen. David's aritcle on CGI scripts in MacPerl is actually divided into two parts, of which, this is only the first. As he needs plenty of time to prepare the second installment, the two parts won't appear consecutively:

David Steffen is founder and current President of Biomedical Computing, Inc., a provider of custom software to the biomedical research community. He has over 20 years experience in biomedical research, in which software design played a role, and over 5 years experience in full time software design. He likes Macs and most forms of *nix, Perl, Python, and C, and will put up with almost anything that runs on a computer.

That said, I give you David Steffen, Ph.D...

--Brian

p.s. If you're a MacPerl user with knowledge to share, we're still looking for other guest columnists for future issues. Just send a note to macperl@perlmonth.com


Although Perl is used for many kinds of tasks, and although Perl predates the World Wide Web, CGI scripts are sometimes seen as the "killer app" for Perl. To a large extent, this is due to the fact that Perl is particularly well integrated into the Unix family of operating systems (Unix, Linux, BSD, etc, referred to hereafter as *nix), because the first Web (HTTP) servers ran under a *nix operating system, and because *nix operating systems remain the predominant hosts for Web servers today. Perl can also be used to write CGI scripts for Web servers running under Mac OS, however, and this article tell you how.

In most cases, MacPerl CGI scripts are identical to their *nix counterparts. For that reason, and because there is so much information available on writing CGI scripts in Perl (e.g. [1]), I will not discuss the writing of a CGI script in Perl at all. In this column, I discuss what MacPerl CGI scripts are and are not good for, the few places where MacPerl CGI scripts differ from *nix Perl CGI scripts, and some of the limitations and pitfalls of using MacPerl to write CGI scripts. In a later column, I will discuss the theory behind synchronous and asynchronous CGI scripts (.cgi and .acgi), server push, and taint checking.

Why write a CGI script in MacPerl?

MacPerl (as opposed to Perl) has some significant limitations as a CGI scripting language. Nonetheless, there are a number of reasons why you might want to run MacPerl CGI scripts on your Mac:

  1. You want to use MacPerl to prototype CGI scripts before uploading them to a production server. I have extensive experience doing this and it works very well. This may be the most useful thing you can do with MacPerl CGI scripts.
  2. You want to use a local, private web server on a Mac to run a slide show, and you want to add the flexibility and power of a Perl CGI script. Although I frequently give HTML-based slide shows, I have not used MacPerl CGI scripts in this context, but I believe that with a little care, this can be made to work well.
  3. You want to add an easy user interface to a MacPerl program running on a private network or even a single Mac. I have extensive experience with this application of MacPerl CGI scripts, and despite the apparent problems, I find this is a very useful application of MacPerl CGI scripts.
  4. You want to use a local, private web server on a Mac as a kiosk, and you want to add the flexibility and power of a Perl CGI script. I have never run a kiosk. In thinking about this application, I suspect that it would have many of the same difficulties and require the same precautionary measures as a public website, but could be made to work.
  5. Having carefully considered the difficulties, you want to include a MacPerl CGI script on a public Mac-based Web server. This is probably the most problematic and controversial use of MacPerl CGI scripts. I have some experience using MacPerl CGI scripts on a public website and believe that this can be made to work adequately under certain circumstances, if suitable precautions are taken.

Mechanics of Running MacPerl CGI scripts

A CGI script for MacPerl is usually identical to a *nix CGI script; most *nix CGI scripts will run without modification under MacPerl and the ever popular CGI.pm module is just as useful on the Mac as it is on *nix.

To run a Perl script as a CGI on a Mac, you will need to have both MacPerl and a Web server installed on your Mac, and have TCP/IP properly set up, all of which are beyond the scope of this article. I have personally used the MacHTTP/WebSTAR, Quid Pro Quo, and Personal Web Sharing web servers and they all work fine. I suspect that most Mac HTTP servers will work as well.

There are some differences in interaction between different web servers and MacPerl, as well as different versions of Mac OS, so your experience may vary a little from what's written here. I will try to point out places where I suspect such variability to lurk, but be aware that my current experience is with Quid Pro Quo running under Mac OS 8.1. Since Quid Pro Quo is free, currently being supported, available via the web [2], and seems to garner mostly favorable reviews, you might want to give it serious consideration.

The MacPerl interpreter comes in two forms; one which can be run with MPW and/or toolserver and one which is a stand-alone application. The stand-alone application is typically used for writing MacPerl CGI scripts and will be the form of MacPerl discussed here.

If MacPerl is installed properly on your Mac, you will have four options when you save a Perl script from the MacPerl application:

  1. Plain text
  2. CGI Script
  3. Droplet
  4. Runtime version

You must save your script as CGI script to run it as a CGI on a Mac. This is a major difference between MacPerl and *nix Perl, and is perhaps the most common difficulty people have when they first start trying to use MacPerl for CGI scripts. On the other hand, if you are prototyping a CGI script on a Mac which is eventually going to run on a *nix server, you must save a copy as Plain Text before transferring it to the *nix system; the CGI Script form will be useless to a *nix-based perl.

Your Web server may have to be configured to use CGI scripts, may have a particular location on the disk where it wants the CGI scripts, and may have conventions as to how it wants the CGI scripts named. Using a default installation of the latest version of Quid Pro Quo (2.1), you select a Folder on your Macintosh to contain the entire website and create a folder therein named "cgi-bin" in which all CGI programs and scripts must reside. They must also be named with a .cgi extension (foobar.cgi, perlscript.cgi, etc.) or a .acgi extension. For now, you should name all your MacPerl CGIs with the .cgi extension; the difference between .cgi and .acgi will be discussed in a later column.

Given the above assumptions, you create your Perl script using any text editor or the MacPerl application, or you can download a CGI script from your *nix server. You then open it with the MacPerl application and save it as a CGI Script into the cgi-bin folder of the root folder of your Web server. You should now be able to access it with the URL:

http://example.org/cgi-bin/your-script-name.cgi

You do not need to have either your CGI Script or MacPerl running; the Web server will start them automatically.

What's Happening Here?

Normally MacPerl executes a plain text file just like *nix perl does. However, Because Mac OS IPC is completely different than that of *nix, Mac OS and *nix CGI interface standards are completely different as well. The *nix CGI standard is based upon the pipes and environment variables.

Chuck Shotton wrote MacHTTP, one of the first Macintosh HTTP servers and the predecessor to WebSTAR. In doing so, he defined a CGI standard (based on Apple Events) for how HTTP servers should communicate with CGI programs on the Mac. As best I can tell, all Macintosh HTTP servers adhere to this standard. However, we don't use this native Mac OS model when writing CGI scripts with MacPerl. Instead, the author of MacPerl, Matthias Neeracher, provided us with a mechanism which mimics the *nix model and makes porting CGI scripts between *nix and MacPerl nearly seamless.

When you save a MacPerl script as a CGI Script, MacPerl creates a small, stand-alone application. The Perl script is included in this application as a resource. This application can do nothing useful in the absence of the MacPerl application. It accepts Apple Events from a Macintosh Web server, reformats them to make MacPerl work like *nix Perl, launches MacPerl if necessary and passes the Perl script and the reformatted data from the Web server to the MacPerl interpreter. Finally, the CGI application redirects the MacPerl output so that it can return it to the Web server. This CGI application appears in the application list at the right side of the Macintosh menu bar just like any other application, and when you select it, displays its single window with a single function; a "Quit Now" button. MacPerl can reopen these CGI applications, allowing you to edit their scripts.

A feature of the MacPerl CGI mini-application is that it remains running for 5 minutes after its MacPerl script finishes running. This is to allow a second request to that CGI application to be serviced more quickly by avoiding the overhead of starting the CGI application. The 5 minute interval is actually an editable resource in the application; it can be varied between 1 minute and 127 minutes, or set such that the CGI application continues running indefinitely. (Instructions for changing this value are given in MPPE, [3].)

Similarly, a feature of MacPerl is that it continues running indefinitely after a script finishes running. (This is different from *nix Perl.) This behavior can also be changed, but the command must come from within the Perl script. For example, to make MacPerl always quit when the script finishes running, add the following line to the script:

MacPerl::Quit(2);

Complete documentation for this command can be found in MPPE [4] and in the MacPerl documentation [5].

Differences between MacPerl and Unix Perl

In my opinion, the two most vexing differences between MacPerl and *nix Perl both result from differences between Mac OS and *nix; the different interprocess communication (IPC) models used by Mac OS and *nix, and the fact that *nix can run many copies of the same program at the same time, but Mac OS can only run one copy of a given program. Because of work done by Matthias Neeracher (described above), the difference in Mac OS and *nix IPC models has little impact on MacPerl CGI scripts. Unfortunately, the inability of Mac OS to run multiple copies of MacPerl has a major adverse impact on MacPerl CGI scripts.

Differences between Mac and *nix IPC

The Mac interprocess communication model is based on Apple Events, whereas that of the *nix operating system is, as noted above, based on pipes and environment variables. In general, interprocess communication is more difficult to use and much less generally available on a Mac. Thus, any *nix Perl program which makes use of interprocess communication is going to be more difficult to port to MacPerl. As noted above, this problem has been largely solved with regards to communication between a Web server and a MacPerl CGI script. The place where the difference in IPC models remains a problem is when a *nix-based CGI script uses pipes, the system() function, or `backquotes` to run other *nix programs via the *nix IPC. There is no general solution to this problem; in almost all cases, such commands will not work with MacPerl. In my opinion, most of these are a bad idea for CGI scripts anyway, and thus should be avoided.

The inability of Mac OS to run multiple copies of MacPerl

Unlike *nix, the Mac OS can only run one copy of any given program. Thus, under *nix, you can have 5 copies of the same CGI script or 5 different CGI scripts running at the same time. Under Mac OS, you can only have one MacPerl CGI script running at a time. This point can be a bit confusing and has been the topic of some discussion on the MacPerl mailing lists. The CGI mini-applications that are generated when you save a Perl script as CGI Script from MacPerl are independent applications and you can have as many of these as you like idling, waiting for hits from your Web server. While idling, they are not running their embedded Perl script. If two of them attempt to run their embedded Perl scripts at the same time, however, that will fail.

What makes the inability to run simultaneous MacPerl scripts such a problem is that the current version of MacPerl CGI "glue" behaves badly if you try. The exact consequence of this bad behavior seems to vary depending on your version of Mac OS and/or your Web server and/or something else; the bottom line is: don't let it happen.

How can you prevent overlapping requests to MacPerl? The first way is manually; if you are testing CGI scripts on a Mac for later use on a *nix system, just be careful to let one thing finish before starting another. The second way is to make it impossible. With the help of your Web server, you may be able to do this.

The first secret to preventing overlapping calls to MacPerl is to use the .cgi filename extension (myscript.cgi) rather than the .acgi filename extension (myscript.acgi). This should alert your Web server (and will, if your Web server is the current version of Quid Pro Quo) that your CGI program can only do one thing at a time. If someone makes a second request of that CGI program while a first request is running, Quid Pro Quo will hold the second request until the first completes.

In "MacPerl: Power and Ease", the definitive reference for MacPerl, it states

"Unfortunately, most Mac OS web servers will wait for a CGI to finish running before responding to any other requests, whether for an HTML page, an image, or another CGI." [6]

That is not the case for the current version of Quid Pro Quo and perhaps other modern Mac OS web servers. Even if you use the .cgi extension, Quid Pro Quo will continue to serve HTML pages, graphics, and other CGI programs while waiting for your MacPerl CGI script to complete running. (This has since been corrected on the errata website for "MacPerl: Power and Ease", [7]) However, this can also be a problem; Quid Pro Quo will attempt to execute a second MacPerl CGI script while a first one is still running.

Thus, the second secret to preventing overlapping calls is to have but a single MacPerl CGI script on your server. Quid Pro Quo is smart enough to queue requests to myscript.cgi, but is unaware that otherscript.cgi is also a MacPerl script, and that trying to run otherscript.cgi while myscript.cgi is still running is a bad thing. At first, this may seem terribly limiting. But remember, you only need to worry about this when you cannot otherwise control access to the CGI scripts (e.g. because they are on a public website). Also note that one CGI script can do as many different things as you want, where you can control what it does by an HTML or environment variable. What you might have done as separate CGI scripts, you can do as separate Perl modules, "use"ing these modules from one master CGI script.

Different Syntax for Passing Data in the URL

In addition to specifying which CGI script to run, the URL can contain information which is passed to the CGI script in environment variables. The most common of these, which works identically under *nix and MacPerl, includes text after a question mark:

http://example.org/cgi-bin/perl.cgi?searchstring

or

http://example.org/cgi-bin/perl.cgi?firstname=David&lastname=Steffen

The string "searchstring" or "firstname=David&lastname=Steffen" will be returned in the QUERY_STRING (and will be parsed into HTML variables by the CGI.pm module).

However, *nix Perl allows another construction:

http://example.org/cgi-bin/perl.cgi/arguments/

This runs the script perl.cgi which resides in /cgi-bin/, passing /arguments/ in the PATH_INFO environment variable.

This will not work with MacPerl. What will work with MacPerl is:

http://example.org/cgi-bin/perl.cgi$/arguments/

or

http://example.org/cgi-bin/perl.cgi$arguments

...but this MacPerl-specific syntax will not work under *nix.

If you need to go back and forth between Mac OS and *nix, avoid passing arguments in the PATH_INFO environment variable. Passing arguments via the QUERY_STRING environment variable using the ? syntax works fine.

Filenames, case sensitivity, and special characters

Mac OS file names are case preserving, but not case sensitive. So, Mac OS keeps track of what case you use when you name a file, but then ignores it for most purposes. If you name your CGI script MyName.cgi, it will remain MyName.cgi, but if you request myname.cgi in the URL, your CGI will run. Not so under *nix; the URL which works on your Mac server will fail on the *nix server; you must use the same case in the URL as the filename.

Mac OS is totally indifferent to spaces or other special characters in a file name (except that the colon character ":" is not allowed) and we Mac users frequently give files names like "My First CGI!.cgi". *nix will allow such filenames, but you will have no end of grief trying to use them. Just say no to spaces and punctuation in filenames that will eventually used under *nix.

Newlines

The Mac OS uses the ASCII carriage return character (character code 13 decimal) to mark the end of a line. *nix uses the ASCII linefeed character (character code 10 decimal). Both scripts and data files used with MacPerl need to have the Mac OS newline to function correctly. In many cases, this conversion will be done automatically during transfer if you transfer files as text, and many text editors (e.g. BBEdit) can correct a file with the wrong newlines. The MacPerl application cannot; you must take care of this before opening a file with MacPerl.

Putting It All Together: Using MacPerl CGI scripts

How you plan to use your MacPerl CGI script determines how (or even if) you should use it. Here I discuss some of the ways I have used MacPerl CGI scripts or have seen others use MacPerl CGI scripts and the implications of those uses.

Developing CGI scripts that will Eventually Run under *nix

By and large, this works extremely well and is perhaps the best use for MacPerl CGI scripts. The reason it works well is that such use is highly controlled and relatively light. Also, because you are sitting there running the Mac, an occasional crash can be tolerated.

The two main things you need be concerned with, when using MacPerl to prototype a Perl CGI script destined for a *nix server, are portability issues and the small set of *nix Perl features that is not implemented in MacPerl.

If the CGI script you are prototyping uses "server push", you are going to have to read the MacPerl documentation [8] or my next column to find out how to make this work under MacPerl. If the CGI script uses pipes, `backquotes`, or the system() call to invoke other programs which run on the *nix server, you are out of luck; you are going to have to either change the CGI script to avoid these external programs or at least write stubs or other code to temporarily replace or bypass their use during testing. Consider the different file naming requirements of Mac OS and *nix, described above.

Other than that, save the script as CGI Script while testing it on the Mac, then save it as Plain Text for transfer to Unix. If it works on a Mac, it will work on Unix, and in most cases, vice versa. The only other things to watch out for are:

Using a MacPerl CGI script in a Slide Show

Increasingly, people use their laptop computers to deliver slide show presentations. Microsoft Powerpoint is frequently used for this application, but many of us prefer to use web pages and a browser (e.g. Netscape). If you use only static web pages, no web server is required. However, if you include a web server, the use of CGI scripts can provide additional flexibility, and such CGI scripts can be written in MacPerl.

Again, this use of MacPerl CGI scripts works quite well because use is controlled and light. However, having your Mac crash during a slide show is a drag, so you need to be fairly careful about what you do and do not attempt to minimize the chance of this happening. Either plan your presentation so you avoid overlapping calls to MacPerl, or use a single master CGI script for everything, naming it with a .cgi extension.

Using a CGI script to Provide a User Interface for a MacPerl Program

Everyone knows how to surf the Web. Thus, if you are developing a MacPerl program that is going to be used by a number of people in your organization, using the Web to provide a user interface has a number of advantages:

  1. You probably already know how to create web pages and forms. In any case, developing for the web is easy. Thus, you can get a user interface up very quickly.
  2. If you have an intranet, this gives you a client/server architecture for almost no effort.
  3. Because everyone knows how to surf the web and everyone has a web browser on their computer, little or no training or software installation will be required.
  4. Your application, which runs on a Mac, can be used by PC or *nix users.

There are a couple of disadvantages to using the web as your user interface:

  1. The Web is connectionless. Thus, saving state requires more work on your part. But if you have programmed for the web, you already know that.
  2. The limitations of being able to run a single MacPerl process can make your system unstable. However, depending on the size of the group, the sophistication of the users, and physical proximity to the Mac on which the system is running, a crash may not be the disaster it would be on a public server.

I suggest using all the suggestions given below for a public server.

Using a MacPerl CGI script in a Kiosk

By a kiosk, I mean a Mac which is running an "internal" web server which provides web pages to the public but only from the one Mac on which it runs. In this application, we have gone up a notch in terms of riskiness. Use should still be light as only the one Mac can access the website, but use is uncontrolled and the Mac is (in general) unattended so a crash is more of a problem.

Again, I suggest using all the suggestions given below for a public server. Although only one person at a time will be using your system, they may accidentally create overlapping calls to MacPerl if they rapidly move between pages. Finally, you should set up the server so that it is fully functional upon restart and you should make it easy to trigger a restart for whoever is maintaining the kiosk and possibly even for users.

Using a MacPerl CGI script on a Public, Macintosh Web Server

The decision to run your own web server, as opposed to setting up a site on a commercial or borrowed web server, and the decision of what operating system and web server software to run on such a server should be given considerable thought [9]. Mac OS is a reasonable choice of an operating system for a web server; it is more difficult to make reliable than a *nix-based server, but easier to make secure. Given that decision, however, you want to be much more careful before deciding to use MacPerl to create CGI scripts to run on that server.

There are a number of things one can do to improve the stability of a Mac web server; installing plenty of RAM in the machine and allocating plenty of RAM to all applications, dedicating a Mac as a server rather than try to use one Mac both as a workstation and a server, and removing unnecessary extensions will improve intrinsic stability. To recover from problems, restarting the server if necessary, I run the utilities "Keep It Up" [10], "Autoboot" [10], and "Okey Dokey" [11] and find that they help uptime. Finally, others have recommended hardware solutions to restarting a crashed Mac [12]; I have not used these myself but they seem worthy of consideration. Given that there are many reasons why your website might be unavailable (e.g. network problems beyond your control), a carefully set up and maintained Mac server may be nearly as stable as any other server OS.

There are many ways to create CGI scripts to run on a Mac-based Web server. For the ultimate in performance and flexibility, you can write your CGI program in C. Frontier [13] is a powerful scripting language which is tailored for web use and which, unlike MacPerl, supports threading and asynchronous execution. Applescript uses very little disk space or RAM, and appears to be somewhat more stable than MacPerl. Finally, many applications (e.g. databases) have built-in CGI support. However, you may still choose MacPerl because you already know Perl, because you want to take advantage of the wealth of Perl CGI scripts which are available, or because of the power and ease of the Perl language. If you do so, however, you should be aware of the potential problems and be prepared to work around them.

Factors important in using MacPerl as a CGI language on a public web server include the following:

  1. Give all CGI scripts the .cgi extension rather than the .acgi extension.
  2. Have only one MacPerl CGI script on the server. (That one script can be a dispatcher providing all the functionality normally associated with many CGI scripts.)
  3. Only use MacPerl CGI scripts on lightly used websites. If you are trying to develop an amazon.com or a slashdot, use other tools.
References

1) "Perl Cookbook" by Tom Christiansen and Nathan Torkington, ISBN 1-56592-243-3, Chapter 9.

2) Quid Pro Quo: http://www.socialeng.com/html/download.html. There are links on this page to their commercial products, but there is a link to download the free version as well.

3) "MacPerl: Power and Ease" by Vicki Brown and Chris Nandor. Paper version: ISBN 1-881957-32-2, Chapter 16. Web version: http://macperl.com/macperl/ptf_book/r/MP/335.CGIx.html

4) "MacPerl: Power and Ease" by Vicki Brown and Chris Nandor. Paper version: ISBN 1-881957-32-2, Chapter 12. Web version: http://macperl.com/macperl/ptf_book/r/MP/315.MP_Pkg.html

5) macperl.pod, in the MacPerl distribution. (Originally found in the folder "tpod".) Read this file using the "shuck" program (which comes with the MacPerl distribution.)

6) "MacPerl: Power and Ease" by Vicki Brown and Chris Nandor. Paper version: ISBN 1-881957-32-2, Chapter 16. Web version: http://macperl.com/macperl/ptf_book/r/MP/335.CGIx.html

7) Errata website for "MacPerl: Power and Ease": http://pudge.net/macperl/macperl_errata.txt.

8) README.CGI and Demo.acgi, in the MacPerl distribution. (Originally found in the folder "MacPerl CGI".)

9) "Philip and Alex's Guide to Web Publishing" by Philip Greenspun. Paper version: ISBN 1-55860-534-7, Chapter 8. Web version: http://www.photo.net/wtr/thebook/server.html

10) Autoboot and Keep It Up: http://www.vl-brabant.be/mac/

11) Okey Dokey is a little hard to find. One place that seems to have it is:
http://tucows.bold.net.au/mac/cpmac.html You will have to scroll down the page a bit to find Okey Dokey.

12) Rebound!: http://www.sophisticated.com/products/rebound/rebound.html
Kickoff! www.sophisticated.com/products/kick-off/kick-off.html

13) Frontier: http://frontier.userland.com/