At the time of this writing, there is only one Web browser that has the functionality to support an embedded Perl interpreter. That browser is Netscape, through its plug-in mechanism. There has been a rumor that the Microsoft browser will also support plug-ins soon, but we did not investigate this.
As we've already mentioned, at the time of this writing, there is only one example of a Netscape plug-in that invokes Perl. It does not actually embed the Perl interpreter into the plug-in, but rather it uses UNIX pipes to communicate with the Perl executable. Before we take a look at it, let's consider the list of potential components of an embedded mechanism in a browser. There are a number of features that definitely should be part of any future Perl plug-in, and others that certainly might be nice to have in a fully implemented plug-in, once someone finally takes the time and expertise to write it and release it.
- Secure Runtime Environment: Above all else, the Perl plug-in needs to be secure. If it's going to be released to the public, especially, it should be configured to be as secure and restrictive in its default capabilities as possible. The Safe module, which we looked at in some detail in Chapter 3, "Security on the Web," gives us the capability to completely mask given operations considered to be insecure, to keep them from ever being executed by the Perl interpreter. Any Perl plug-in should use the Safe/Opcode modules, by default. The Perl plug-in we'll look at later certainly does.
- Encryption/Signatures: You might want to assure that the code you're executing has come from a source you trust, via your PGP keyring, for instance. If the decoded and/or signature-verified code is from a known, trusted source, you could execute it straight away without too many worries. If it isn't, then you'd (hopefully) want to inspect the code carefully and understand what it will do before executing it. The PGP module we looked at in Chapter 3 might be a viable means for decrypting or verifying the signature on a chunk of Perl code before executing it.
- Cross-Platform Compatibility: One of the nicest things about the Java and JavaScript languages is that their applets, or scripts, are guaranteed to run on any platform that has the embedded Java within the Netscape (or Microsoft) browser. When using Perl, on the other hand, there's no way to ensure that a given script will run on any platform. There are commands, modules, and other capabilities that are platform-dependent in Perl. The capability to fork() or exec() a process, for instance, is pretty much limited to the UNIX platform. Because Perl is so UNIX-centric, many of the libraries and modules contain platform-specific code. There is a general trend away from this tendency, but it's a slow and sometimes painful one. Other times, it's just not possible to make a given program or module compatible with all of the architectures that can run Perl. The core functionality of Perl is usually available to any script, however. With this in mind, you could design the scripts to be served to clients appropriately. Alternatively, you could force the client to have the Mozilla-UNIX identifier before serving the script to it if there were known incompatibilities with other architectures. Finally, if you really need to get something like a module or library to work on an unsupported or untested architecture, you could always port the code to that architecture. Be sure to let the author know and submit changes you had to make to get the module working, and so on.
- Byte-Code Interpretation: Byte-Code is a form of a Perl program or script that has been precompiled. When bytecode is sent over the Net as an executable, it's called an applet in the Java world. There are several advantages to running bytecode as opposed to parsing and compiling a text file. The first, and probably the most important from a shared-code standpoint, is that it's much more difficult (but not impossible) to change a bytecode program once it's been created than it is to change a given textfile script. This adds some assurance to the client that what they receive and execute is indeed what you intended for them to receive as the server. The second advantage is speed. The bytecode program is in its final form, already parsed and reduced to an opcode tree. Thus, when you feed it to Perl, Perl can load and execute it directly, reducing the time to get a result. Finally, there's an enhancement in platform-independence when serving bytecode. Again, as previously mentioned, this isn't really an issue when executing Perl code, because there are operations that may reduce to bytecode on one architecture but still won't execute on another architecture. This is a win with the Java language, however, because most or all of its operations are guaranteed to run under any architecture.
- Browser Communications: It's certainly nice to be able to communicate back to the browser the result of running a given program. It's arguable, on the other hand, how much the client really wants to know. Java provides the programmer with the capability to create and manipulate windows or drawing areas (rectangles) within the browser window. These features are commonly used to do animation and other dynamic processes within the browser, often for entertainment's sake. This also gives the browser the capability to execute code and produce output, such as summing the values from a form or figuring the tax on a purchase, which might otherwise require a CGI routine on the server. Perl, as a language, provides extremely powerful capabilities for processing data and manipulating text, but it is still a little weak on the animation/windowing side of things. Of course, you can use external modules and libraries, which do handle this in a much nicer way, to communicate with the browser. We'll look at an example a bit later, using the OpenGL module. You could also use the Java interface for communicating with the browser/client and use Perl for processing the data. In the simple case, and possibly the most utilitarian, all the client may need to know is whether the script ran successfully, or possibly the results, if the script is manipulating some local files, for instance.
So, now that we've considered what could and should be a part of a Perl plug-in, let's take a look at the one that has already been developed.
The Netscape plug-in development kit provides the developer with a simple interface to and from the Netscape browser. The plug-in developer defines the MIME type/extension that he or she wants Netscape to associate with the plug-in, and whenever any URL of this type is received, the plug-in is invoked and is passed the content of the URL. It's then up to the plug-in to process the content as it sees fit and exit with an appropriate value that is returned to Netscape.
The existing Perl plug-in is quite simple in its implementation, but it has an enhanced entertainment value and visual appeal because it's implemented using the OpenGL 3D Graphics Rendering kit. It can communicate with the browser directly, creating windows within the browser for its output.
The OpenGL plug-in source is distributed with the OpenGL Perl5 extension, available from the CPAN in the ~modules/by-module/OpenGl
directory. If you don't have OpenGL, you can obtain a library, called MesaGL, which emulates most of OpenGL's features and capabilities for free. MesaGL is available by anonymous ftp from ftp://iris.ssec.wisc.edu/pub/Mesa
.
You'll need to install the OpenGL or MesaGL C library and have the OpenGL Perl5 extension, as well as the Safe/Opcode module available, for the plug-in to work.
Once you've installed everything, you can try some of the sample scripts that come with OpenGL. In particular, the OpenGL Perl plug-in is distributed with a sample Perl script called quest.glp, which, when run by itself, produces a window with a simple animated 3D maze, which looks something like Figure 15.1.
Figure 15.1. The Maze object.
While the script is running, the Maze object you see will actually be rotating on its 3D axis and slowly moving closer to, then farther away from, the foreground. It's not particularly useful, granted, but it is visually appealing. Now, let's take a closer look at the source code for the plug-in to see how it works its simple magic.
As with any Netscape plug-in, there are C code routines that you must code up to make it do what you want. As we've previously mentioned, the plug-in must be able to tell Netscape what MIME type it's able to handle. This is done through the NPP_GetMIMEDescription()
function in the npshell.c source code file. This particular plug-in sets itself up to handle URLs of the
application/Perl-opengl:.glp
type. Whenever a URL with the extension .glp is received by Netscape, it will invoke the plug-in and pass in the contents. Once the plug-in is invoked, the NPP_Write()
function is eventually called, and here is where the plug-in developer chooses to actually invoke Perl, using the UNIX popen(3S)
routine. Once Perl is started and waiting at the other end of the pipe for input, the contents of the URL (the Perl script) is fed to it, using the Safe::reval()
method discussed in Chapter 3. This also implies that a Safe Compartment is created, and the exported symbols from the OpenGL
module are permit()
'd into it. We end up with a Netscape window that looks like Figure 15.2.
The html that invokes the plug-in has the EMBED
tag
<EMBED src="quest.glp", width=300, height=300>
which causes the plug-in to be invoked with the right extension.
Hopefully, as time allows, we'll start to see other useful Netscape plug-ins that embed Perl and implement an encryption method for the exchange of scripts and bytecodes.