[Date Prev][Date Next][Thread Prev][Thread Next] [Search] [Date Index] [Thread Index]

[MacPerl-Scribes] Draft of CGI Article



Members:

Please find appended Draft 2 of my article on CGI programming with 
MacPerl.  Some notes:

1) It is very long - nearly twice as long as Arved's XS article.

2) The references are still stubs.  Any additional suggestions for references
    gratefully accepted.

3) Obviously, I'm putting it here because I want comments.

-David-

                        Using MacPerl for CGIs

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 CGIs for Web servers
running under the MacOS, however, and this article tell you how.

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

Why write a CGI in MacPerl?
--------------------------

The MacOS is primarily a single-user, interactive operating system and
has some significant limitations which impact its use on a public Web
server.  MacPerl has some additional limitations as a CGI language.
Nonetheless, there are a number of reasons 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.

2) You want to use a local, private web server on a Mac as a kiosk or to
run a slide show, and you want to add the flexibility and power of a
Perl CGI.

3) You want to add an easy user interface to a MacPerl program running
on a private network or even a single Mac.

4) Despite the difficulties, you want to run a public web server on your
Mac.

In my experience, MacPerl works excellently for prototyping, works well
on a single Mac or small, local network as a front end to a MacPerl
program or for a kiosk or slide show, and with care can be used on a
lightly loaded public server.

Mechanics of Running MacPerl CGIs
---------------------------------

A CGI script for MacPerl is usually identical to a *nix CGI; most *nix
CGIs 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.  As a Web
server, I have personally used MacHTTP/WebSTAR, Quid Pro Quo, and
Personal Web Sharing 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 the MacOS, 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 MacOS 8.0.  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 CGIs 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 3) Droplet 4) Runtime version You must save your script as CGI to
run it 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 CGIs.  On the other hand, if you
are prototyping a CGI on a Mac which is eventually going to run on a
*nix server, you must save it as Plain Text before transferring it to
the *nix system; the CGI form will be useless to a *nix-based Perl.

Your Web server may have to be configured to use CGIs, may have a
particular location on the disk where it wants the CGIs, and may have
conventions as to how it wants the CGIs named.  The latest version of
Quid Pro Quo (2.1) prompts you for a series of parameters the first time
you run it after an installation and allows you to change these and many
more parameters thereafter.  One of the parameters that it prompts you
for is the root folder for your website; all web pages and CGIs must be
contained within this folder (and its subfolders).  (This folder needs
to exist before the installation.)  By default, you need to have one
subfolder named "cgi-bin" and any CGIs you intend to run need to be
contained therein and 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 from your
*nix server.  You then open it with the MacPerl application and save it
as a CGI 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://your.server.name/cgi-bin/your-script-name.cgi

You do not need run either your CGI 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.
However, the CGI interface standard was designed with *nix in mind and
makes use of the Pipes and Environmental Variables of the
application-independent, simple, and powerful IPC model of *nix.
Because the MacOS IPC is completely different than that of *nix, CGIs
had to be implemented in an entirely different way.  Chuck Shotton, when
he wrote MacHTTP (one of the first Macintosh HTTP servers and the
predecessor to WebSTAR), defined a standard for how HTTP servers should
communicate with CGIs on the Mac based on AppleEvents, and as best I can
tell, all Macintosh HTTP servers adhere to this standard.  However, we
don't use this native MacOS model when writing CGIs with MacPerl.
Instead, the author of MacPerl, Matthias Neeracher, provided us with a
mechanism which makes porting CGIs between *nix and MacPerl nearly
seamless.

When you save a MacPerl script as a CGI, 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 AppleEvents 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 to be serviced more quickly by
avoiding the overhead of starting the CGI.  The 5 minute interval is
actually an editable resource in the application; it can be varied
between 1 minute 127 minutes, or set such that the CGI 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 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 instructions 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 MacOS and *nix; the different
interprocess communication (IPC) models used by MacOS and *nix, and the
fact that *nix can run many copies of the same program at the same time,
but the MacOS can only run one copy of a given program.  Because of work
done by Chuck Shotton Matthias Neeracher (described above), the
difference in MacOS and *nix IPC models has little impact on MacPerl
CGIs.  Unfortunately the inability of the MacOS to run multiple copies
of MacPerl has a major adverse impact on MacPerl CGIs.

Differences between Mac and *nix IPC
------------------------------------

The Mac interprocess communication model is based on AppleEvents whereas
that of the *nix operating system is based on Pipes and Environmental
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 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's anyway, and thus should be avoided.

Problems resulting from the inability of the MacOS to run two copies of MacPerl
-------------------------------------------------------------------------------

Unlike *nix, the Macintosh OS can only run one copy of any given
program.  Thus, under Unix, you can have 5 copies of the same CGI or 5
different CGIs running at the same time.  Under MacOS, you can only have
one MacPerl CGI running at a time.  What is worse, the current version
of MacPerl CGI "glue" behaves badly when you try to violate this.  The
exact consequence of this bad behavior seems to vary depending on your
version of MacOS 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 CGIs 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, which with the help of your
Web server, you may be able to do.

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 can only do one thing at a time.  If someone makes a
second request of that CGI while a first request is running, Quid Pro
Quo will hold the second request until the first completes.

The second secret to preventing overlapping calls is to have but a
single MacPerl CGI 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 CGIs (e.g. because they are
on a public website).  Also note that one CGI can do as many different
things as you want, where you can control what it does my an HTML or
environmental variable.  What you might have done as separate CGIs, you
can do as separate Perl modules, "use"ing these modules from one master
cgi.

Different Syntax for Passing Data in the URL
--------------------------------------------

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

http://foobar.net/cgi-bin/perl.cgi?searchstring

or

http://foobar.net/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://foobar.net/cgi-bin/perl.cgi/more arguments/

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

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

http://foobar.net/cgi-bin/perl.cgi$/more arguments/

or

http://foobar.net/cgi-bin/perl.cgi$more arguments

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

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

Filenames, case sensitivity, and special characters
---------------------------------------------------

The MacOS keeps track of what case you use when you name a file but then
ignores it.  If you name your CGI 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.

The MacOS 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.

Putting It All Together: Using MacPerl CGIs
-------------------------------------------

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

Testing CGI's that will Eventually Run under *nix
-------------------------------------------------

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

The two main things you need be concerned with when prototyping a Perl
CGI destined for a *nix server using MacPerl are portability issues and
the few *nix Perl features that are not implemented in MacPerl.

If the CGI you are prototyping uses "server push", you are going to have
to read my next column to find out how to make this work under MacPerl.
If the CGI uses pipes 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 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 MacOS and
*nix, described above.

Other than that, save the script as a CGI 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: * MacPerl is Perl 5.004.  5.005 and later
features won't work. * Make sure that any modules or other code you
"use" or "require" is available on both the Unix server and the Mac.
Remember that modules which have binary extensions may be difficult (or
even impossible) to port. * Remember to set your ownership and
permissions correctly on the Unix server.

Using a MacPerl CGI 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 CGIs can provide
additional flexibility, and such CGIs can be written in MacPerl.

Again, this use of MacPerl CGIs 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 for everything, naming it with a .cgi extension.

Using a CGI 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 almost for free. 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.

There are a couple of disadvantages: 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 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 the rapidly
move between pages.  Finally, you should set up the server so that upon
restart it is fully functional 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 on a Public, Macintosh Web Server
-----------------------------------------------------

I would suggest anyone contemplating running a public webserver on a Mac
think through the alternatives.  Placing your website on someone else's
server means a lot less cost and worry.  The Linux and BSD operating
systems both run on Mac hardware and are considerably more stable than
the MacOS.  Nonetheless, running your own server gives you more
flexibility and a Mac-based web server can be much easier to set up and
administer than a *nix base server, especially for someone from a
Macintosh background.

Given the decision to run a Mac Web server, you might choose a variety
of ways of creating CGIs.  For the ultimate in performance and
flexibility, you can write your CGI in C.  Frontier [6] 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 CGIs which are available, or because of the power and ease of the
Perl language.

There are a number of things one can do to improve the stability of the
Mac as a server; plenty of RAM, allocating plenty of RAM to all
applications, dedicate a Mac as a server rather than try to use one Mac
both as a workstation and a server, remove as many extensions as
possible, running utilities like "Keep It Up" [7], "Autoboot" [8], and
"Okey Dokey" [9] to restart an application that quits, to restart after
a crash, or to dismiss unexpected dialog boxes which might otherwise
stop everything forever.  Having done as much of that as you can, there
are some MacPerl specific suggestions as well: 1) Give all CGIs the .cgi
extension (which means forgoing the server push feature.) 2) Have a
single, multifunction MacPerl CGI. 3) Only use MacPerl CGIs on lightly
used websites.  If you are trying to develop an amazon.com or a
slashdot, use other tools.

Citations
---------

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

2) Quid Pro Quo URL

3) MPPE instructions for setting the time in a CGI

4) Ibid, the MacPerl::Quit() function

5) The MacPerl documentation for MacPerl::Quit()

6) Frontier

7) Keep It Up

8) Autoboot

9) Okey Dokey

Biography
---------

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.
David Steffen, Ph.D.
President, Biomedical Computing, Inc. <http://www.biomedcomp.com/>
Phone: (713) 610-9770 FAX: (713) 610-9769 E-mail: steffen@biomedcomp.com

==== Want to unsubscribe from this list?
==== Send mail with body "unsubscribe" to macperl-scribes-request@macperl.org