Introduction to PerlScript

By Matt Sergeant, Copyright © 1998. Last Updated 12:24 06/10/1998

1. Introduction

I'm writing this as an introduction to PerlScript. Many people have asked me if a document like this is available and my answer always had to be "No.". I think I should change that, so here is my effort.

It is my hope that this document will bring more people to PerlScript, and away from having to use that dreaded VBScript.


2. What are Active Server Pages

Active Server Pages are a way of creating dynamic content on the web server. They work much the same way as CGI, or ISAPI applications - by running some code on the server and returning the information to the browser. Writing ASP code is a lot like writing a CGI application using a scripting language. The script you write however can be embedded within HTML. Microsoft see this as an advantage because it allows you have script and code become one. I see it as a very bad thing. See my previous tutorial for my reasons, but basically application developers are not designers - and I think a template based system is better.


2.1. So what does it look like then?

ASP code looks like ordinary scripting code, written in your language of choice (of course this will be PerlScript - right?), but to denote the start and end of script you use the delimiters "<%" and "%>". Outside of those delimiters you can use any text or html, and it will be rendered as such in the browser. If your HTML or text is outside of delimiters, but inside a loop construct, that text will be output as many times as the loop goes around:

     1 : <% @LANGUAGE = PerlScript %>
2 : <%
3 :     for (1..3) {
4 : %>
5 : <font size="<%= $_ %>">Font Size<%= $_
6 :         %></font><br>
7 : <%
8 :     }
9 : %>
 

You might also note the <% @LANGUAGE = PerlScript %> at line 1. This tells the ASP processor that this is PerlScript code. The default is VBScript. The output of this is the following HTML:

     1 : <font size="1">Font Size 1</font><br>
2 : <font size="2">Font Size 2</font><br>
3 : <font size="3">Font Size 3</font><br>
 

That's pretty much all you need to know. The rest is just Perl. All Perl functions are supported - including things like backticks, system, open, sockets. There are some issues in build 502 with opendir and backticks, but these should be sorted out with build 503.


3. Why PerlScript?

Anyone who is reading this who has come from VBScript might want to ask me "Why should I use PerlScript - VBScript works adequately?". I think the key there is "Adequately". Where VBScript works if you just want to use the built in object model, and do some simple database stuff, what if you want more power. Can you, in VBScript, do the following (without resorting to a server side OLE object):

I have a feeling that the answer is "No." - of course it was a loaded question. If you answered "Yes." then perhaps I missed the new release of MS VBScript...

Of course not everyone reading this article will be coming from a VBScript background. Perhaps you've come from a Perl background, and have used CGI, or mod_perl or some such solution. What can PerlScript offer you? I'll cover exactly what it can offer in detail, but the key thing is the object model. This is the built in system that allows access to: Inter-process communication, session management, form and query string values, cookies, timeouts, and many more things. Hopefully you will be used to CGI.pm handling all these things for you, but as you'll see there are some things that ASP does that even CGI.pm doesn't do (and probably shouldn't)


4. A Few Basics

Before we dive right in there are a few basics to learn. Most of these are based around how Perl deals with OLE objects. The ASP object model is based around OLE modules that are created for you in the main namespace at runtime. The key here is to know your object model inside out. There are unfortunately some simplifications that Microsoft have hard coded into VBScript that makes using OLE objects a lot simpler, however these simplifications don't fit well into the Perl way of doing things (in fact they would look broken in most languages). For example, some of the VBScript OLE syntax looks like you are assigning something to a function call:

     Session('UserId') = myUserId  

Which simply wouldn't work in most modern languages. Unfortunately this means that Perl has to go the long way around a lot of these things, however you will learn how the OLE model works very well doing it the Perl way. As is usual with Perl, it's a steep but long learning curve (rather than VBScripts shallow and short learning curve)


4.1. Method Calls

Method calls are implemented just like Perl function calls on objects (see the perlobj help page for details):

     $filename = $Server->MapPath('/index.html');  


4.2. Properties

OLE doesn't make any distinction between accessing properties and performing function calls, they all look like:

     Object.Name  

Where for a function call Name would be the name of the function, and for a property access, 'Name' would be the property name.

Perl makes a distinction here, which I think is beneficial. Luckily the documentation for the ASP object model (the documentation gets installed on your server when you set up IIS4 under /iishelp) always states which are methods, which are properties and which are collections. Properties are accessed in Perl just like a normal Perl object's properties, with a hash dereference:

     $Response->{ContentType} = 'image/gif';  

This makes it rather easier to see in your code where you are accessing properties and where you are calling functions.


4.3. Collection Objects

The ASP object model contains a number of what are called "Collection Objects". Collection objects are just ordinary OLE objects that support a number of special properties and methods. They all have a Count property and an Item method. Some Collection objects also support the Key method. Collections are sort of like Perl hashes, but also like Perl arrays. This is because the Item method supports access by either a Key (like a hash) or via a numeral (like an array). PerlScript supports both methods of access, although it doesn't use the appearance of accessing either a hash or an array - it simply uses the method access, as used by OLE. Note that hash access was implemented in earlier versions of Win32::OLE (specifically Activestate perl prior to release 500), and still exists but in a depracated manner (disabled under "use strict").

Win32::OLE also provides support for what is known as "default method call". This means that objects have default methods, and these default methdods can be called by simply referencing the object as though it were a function. One minor problem here is that this can't pass all the way up the tree to the first object. For example, the following is valid in VBScript:

     1 : Application.Contents.Item('One') = 1
2 : Application.Contents('Two') = 2
3 : Application('Three') = 3
 

And this is valid in PerlScript:

     1 : print $Application->Contents->Item('One');
2 : print $Application->Contents('One');
 

However the following isn't valid PerlScript:

     print $Application('One');  

because this is impossible code in PerlScript - $Application is an object and the parser would just croak on the above.

Also note that some Collections return an object, not a value. For example, the Cookies collection in the Request object returns a RequestDictionary object, so you need to dereference the object to get at the underlying value - this is something VBScript does automatically, but that you have to do manually in PerlScript:

     1 : $currUserId = $Request->Cookies->Item('UserId')->{Item};
2 : # or
3 : $currUserId = $Request->Cookies('UserId')->{Item};
 

In the code above, Cookies is the name of the collection object. This code would have looked like this in VBScript:

     1 : currUserId = Request.Cookies.Item('UserId')
2 : ' or
3 : currUserId = Request.Cookies('UserId')
 

The reason we cannot do this in Perl is because the above returns an OLE object, which is why we have to dereference the hash in Perl to get at the value. VBScript knows internally about OLE, because it was written with it in mind (as was Microsoft's JScript). Perl is a general purpose language which wasn't developed with OLE in mind, and so we have to be explicit.

Note that if we had written the above as:

     $currUserId = $Request->Cookies('UserId')  

then we could have used that $currUserId in any of the ASP object model functions (such as $Response->Write() which output's text to the browser) and the object model would have done the right thing (i.e. displayed the UserId), however we wouldn't be able to do a Perl comparison:

     if ($currUserId == 1) { ... }  

because what is in $currUserId is not an integer, it's an OLE object. You can discover this by doing:

     $Response->Write("$currUserId");  

Note how it's in quotes - Perl "interpolates" things in quotes, this means it displays what the contents of the variables are. In this instance we would have returned to the browser something along the lines of Win32::OLE=HASH(0xca60a0c).

The next thing to know is that only the cookies collection returns an OLE object. All the other collections in the ASP object model are VariantDictionary's, which are simple scalars, so only when using Cookies do you have to dereference. However be aware that other OLE objects may return an OLE object when accessing a Collection, for example the ADO RecordSet Field collection:

     my $ColValue = $RS->Fields('ColName')->{Value};  

It is of course a minefield. Your best tool for figuring all this out is OLEVIEW which comes with MSVC 4/5 which allows you to see the type library for all OLE objects on your server.


4.4. Win32::OLE and "use strict;"

The Win32::OLE module is quite changed since the Activestate Perl build 316. There were many changes implemented to ensure future compatibility, and yet remain backwards compatible. Perl's "use strict" option disables many of the backwards compatibility features, but also stops certain features from working. Currently Jan Dubois is working to enable those features under use strict, but until then all the code below is clearly marked as to whether or not it works with "use strict".

Jan has now completed most of this work with the advent of build 503. Make sure you are using that version otherwise some of the code below won't work. Specifically, this is anything that uses the method SetProperty (only used below for setting collection objects, which luckily doesn't happen very often as most collections are read only in this object model).

Also note that it is not possible to "use strict" with PerlScript without using either my Win32::ASP module with the :strict option enabled:

     1 : use strict;
2 : use Win32::ASP qw(:strict);
 

or by using "use vars qw( $Request $Response $Server );" (inserting the variables you need to use):

     1 : use strict;
2 : use vars qw( $Request $Response );
3 : $Response->Write ("You sent me a name of " . $Request->Form->Item('name'));
 

This is because the ASP objects are created magically in the main namespace, but this magic creates compilation errors under use strict.


5. The Object Model

The object model is the key to ASP, as VBScript users will know. Basically the object model is a set of OLE objects that control various things to do with your web environment.


5.1. The Application Object

The application object is responsible for sharing information between the whole web application, this can be your whole web site, or one area that you mark off as being a separate application (using the virual directory system).

If you are using the Frontpage extensions, this would be called a new Web.

Because of the issues of multiple users, it is advisable to use the application object for information sharing as little as possible. There is a lock/unlock method provided to prevent concurrency issues, but this has a performance impact.

Sharing information with the application object is done through the use of application variables. These are like global variables to your application. Application variables are stored in the two collections Contents and StaticObjects. Contents are "normal" application variables, and StaticObjects is read only and contains just those objects you created with the <OBJECT> tag (using SCOPE=APPLICATION).


5.1.1. Contents Collection

The contents collection, as stated above, stores all the application variables. Use it carefully.

Syntax

     1 : # Get value
2 : my $value = $Application->Contents($key);
3 : # Set value (beware - you will need to Lock/Unlock)
4 : $Application->Contents->SetProperty('Item', $key, $value);
 

Note that the get value method above is just shorthand for:

     my $value = $Application->Contents->Item($key)  

because Item is the default method for the Contents collection.

For iteration:

     1 : use Win32::OLE qw( in );
2 : foreach my $key (in ($Application->Contents)) {
3 :     $Response->Write( "$key = " . $Application->Contents($key) . "<br>\n" );
4 : }
 

You can store a scalar, an array or an OLE object (also called a server component in ASP terms) in Application variables. If you want to store a hash you need to flatten it into an array first, or flatten it into a scalar using Data::Dumper.


5.1.2. StaticObjects Collection

The StaticObjects collection is much the same as the above, only it contains the objects created with Application scope using the <OBJECT> tag. (details of this are PerlScript independant and available in the ASP docs). This collection is read only.

Syntax

     my $value = $Application->Contents($key);  

Again, iterate using similar syntax to that above. Also, again, this is a shortcut of using the "Item" method.


5.1.3. Lock Method

The Lock method is there to allow you to get around concurrency issues when accessing global variables. Be very careful with this It's very easy to get yourself into a deadlock situation. Read a book about concurrent programming. This is the advice I give you on using Lock/Unlock:

Syntax

     $Application->Lock;  

An example of using this is you might want to maintain a count of how many accesses your page has had:

     1 : $Application->Lock;
2 : $Application->Contents->SetProperty('Item', 'TotalHits',
3 :             $Application->Contents('TotalHits') + 1);
4 : $Application->Unlock;
 


5.1.4. Unlock Method

We just saw the unlock method in action, but here's the syntax in case you missed it:

     $Application->Unlock;  

If this is not called explicitly, the application is unlocked when the ASP page times out (see $Response->{TimeOut} below), or the page ends and returns to the browser.


5.2. The Request Object

The request object is used to get the information passed to the server from the browser. This information includes cookies, form values, the query string (anything after a '?' in a URL), client certificates and the server environment variables (because these aren't available in %ENV).


5.2.1. ClientCertificate Collection

Client Certificates are used for security as specified in the X.509 standard. Browsers send a client certificate if using the SSL3.0/PCT1 protocol (URL's starting with https:// instead of http://), and the server requests a certificate to prove identity. Requesting of client certificates is setup in the server configuration (i.e. the IIS Management Console).

Syntax:

     1 : my $cert = $Request->ClientCertificate('KeyName');
2 : # or
3 : my $cert = $Request->ClientCertificate('KeyName'.'SubField');
 

Where KeyName is any of the following:

And SubField is an optional 'parameter' you can append to the KeyName to retrieve an individual field in either the Subject or Issuer keys. These are the common SubFields:

Example

     1 : <%
2 : if (!$Request->ClientCertificate("Subject")) {
3 :     print "No client certificate was presented";
4 : }
5 : %>
 

The 'Flags' are values that are ||'d together, however there appears to be no Const definitions for them in the model anywhere. VB users have a server side include which can easily be converted to a PerlScript module - see Win32::ADO for an example of this. To see if an individual flag is set use:

     1 : <%
2 : if ($Request->ClientCertificate('Flags') && ceUnrecognisedIssuer) {
3 :     print "Unrecognised Issuer";
4 : }
5 : %>
 


5.2.2. Cookies Collection

The cookies collection in the request object is used to retrieve cookies. Setting cookies is done by the Response object (see below). If you don't know what cookies are, or what they are used for I'm not going to tell you, and you are probably on the wrong planet... ;-)

use strict;: Cookies now work with "use strict;" when using ActivePerl build 503 or greater. Thanks to Jan Dubois for that. However I still recommend using Win32::ASP for its greater flexibility in setting cookies.

Syntax:

     1 : my $cookieVal = $Request->Cookies('CookieName')->{Item};
2 : # or for sub cookies
3 : # (note the use of capitals which appears to be significant)
4 : if ($Request->Cookies('CookieName')->{HasKeys}) {
5 :     my $subCookieVal = $Request->Cookies('CookieName')->Item('SUBCOOKIE');
6 :     ...
7 : }
 

You can also iterate over all cookies sent:

     1 : foreach my $f (Win32::OLE::in ($Request->Cookies)) {
2 :     print $f, " = ", $Request->Cookies($f)->{Item}, "<br>\n";
3 : }
 

Or you can iterate over a single cookie's sub cookies (does that make sense?):

     1 : foreach my $f (Win32::OLE::in ($Request->Cookies('CookieName'))) {
2 :     print $f, " : ", $Request->Cookies('CookieName')->Item($f);
3 : }
 


5.2.3. Form Collection

The Form collection is used to retrieve values from forms of method="POST". You may not find it neccessary to use the Forms collection if using Win32::ASP as I've wrapped up getting form values into an easy to use function that figures out whether it's a GET or POST request.

Syntax:

     my $formVar = $Request->Form($name)->Item($index);  

Parameters:

If the form element was either a checkbox or a multiselect then you can get the number of elements in the form collection with:

     $Request->Form($name)->{Count}  

You can iterate through all form values in much the same way as you can with cookies. See above for details on that.

To make life a lot simpler, I recommend using Win32::ASP for form processing. This has the advantage of allowing you to invisibly switch between GET and POST, and also uses Perl's context return system, so if you ask for an array when the form element was a multiselect or a set of checkboxes, you get an array of values.

For example, say we had a form like this:

     1 : <form>
2 :     Computing: <input type="checkbox" name="Hobby"
3 :                 value="Computing"><br>
4 :     Reading: <input type="checkbox" name="Hobby"
5 :                 value="Reading"><br>
6 :     Other: <input type="checkbox" name="Hobby"
7 :                 value="Other"><br>
8 : <input type="submit">
9 : </form>
 

If we used the ASP object model we would have to grab each checkbox from the form separately:

     1 : my ($Comp, $Read, $Other);
2 : $Comp = $Request->Form('Hobby')->Item(1);
3 : $Read = $Request->Form('Hobby')->Item(2);
4 : $Other = $Request->Form('Hobby')->Item(3);
 

Or we could use a loop using Win32::OLE::in, but that's messy too. Wouldn't it be nicer to just go:

     1 : use Win32::ASP;
2 : my @Hobbies = GetFormValue('Hobby');
 

I suppose if I didn't think so I wouldn't have written it like that. I find Perl's arrays much easier to work with than other languages, because of the simple ability to do things like join and map. These functions are especially useful in putting information into a database, but I'll leave that as an excercise.


5.2.4. QueryString Collection

The QueryString collection object is used to get at the values normally associated with the QUERY_STRING environment variable (under CGI applications). This is everything after the "?" on a URL line. Normally these are the results of form submission with the GET method, however it can also result from a normal href: <a href="http://www.url.com/test.asp?query=string">

This collection acts in an identical way to the Form collection, so I won't go into detail, but this is the syntax:

     $Request->QueryString($name)->Item($index)  

Again the Count property exists, and you can iterate over the collection with Win32::OLE::in

Win32::ASP is again useful here, and provides a more Perl-like interface for you. It also figures out whether you are using GET or POST (i.e. QueryString or Form) and does the right thing automatically, which is great for debugging. I recommend you use GET for debugging/development and switch to POST for release.


5.2.5. ServerVariables Collection

The ServerVariables collection is what would normally be %ENV in a CGI script (or any normal web server programming environment), however VBScript is even limited to not being able to access the environment, so it got implemented as another OLE collection.

I'm not going to explain all the environment variables that the server returns here, see either some documentation on CGI, or the ASP documentation that comes with IIS. The syntax for getting individual ServerVariables is:

     my $value = $Request->ServerVariables($name)->{Item};  

You could also iterate through all the ServerVariables, setting up %ENV if you wanted:

     1 : foreach my $env (in ($Request->ServerVariables)) {
2 :     $ENV{$env} = $Request->ServerVariables($env)->{Item};
3 : }
 

I considered adding this code to Win32::ASP, but after deliberation I decided that the overhead would be unneccesarily high. Perhaps I will add it as a compile time directive some day soon.


5.2.6. TotalBytes Property

The TotalBytes property is mostly undocumented in the ASP documentation, but it is basically equivalent to $ENV{CONTENT_LENGTH} in CGI terms. It is the total number of bytes sent to the client in a POST request, and is retrieved with BinaryRead (see below).

This is useful if you wish to do any sort of file upload using ASP, in fact it is soon going to be possible to use the file upload facilities in CGI.pm with Win32::ASP. This will be achieved with the tie mechanism, to tie STDIN into the Win32::ASP class.


5.2.7. BinaryRead Method

The BinaryRead method is useful for the above in processing file uploads with ASP. Here is a translation of the example code that comes with IIS4:

     1 : use Win32::OLE::Variant;
2 : my $bincount = $Request->{TotalBytes};
3 : my $binread = Win32::OLE::Variant->new( VT_UI1, $Request->BinaryRead($bincount) );
 


5.3. The Response Object

The response object is responsible for sending output to the client's web browser. Any type of information can be sent, although HTML is the default. You can also send any type of HTTP header with the response object, such as cookies, redirection and status.


5.3.1. Cookies Collection

The response cookies collection is used for setting cookies, (whereas the request cookies collection views the contents of current cookies). Be careful not to assume any dualism between when cookies are set, and reading them again (i.e. don't assume that once set with the response object, that you can immediately read them again in the same script with the request object - either use a global variable, or wait for the next request).

Warning: Cookies do not get set if you try and send them after sending any output to the browser whatsoever. You can avoid this problem by turning on buffering in your page. See the Buffer property to do this.

Syntax:

     1 : $Response->Cookies->SetProperty('Item', 'Cookie2', "Test");
2 : $Response->Cookies('Cookie1')->SetProperty('Item', 'SubCookie1', "Testing subcookie1");
3 : $Response->Cookies('Cookie1')->SetProperty('Item', 'SubCookie2', "Testing subcookie2");
 

The first example sets an ordinary cookie. The second two examples show the ability of ASP to set cookie dictionaries. Think of these as a cookie that stores a hash. As you can imagine - this is quite useful.

Cookies also have other properties, I think these are probably self explanitory:

     1 : $Response->Cookies('CookieName')->{Expires} = "December 31, 2005";
2 : $Response->Cookies('CookieName')->{Path} = "/";
3 : $Response->Cookies('CookieName')->{Domain} = "server.com";
4 : $Response->Cookies('CookieName')->{Secure} = 1;
 

These settings are:

Only use these values after you have initially set the cookie using SetProperty, otherwise you get errors about use of undefined values (under use strict) or nothing happening (without use strict).

Example

     1 : $Response->Cookies->SetProperty('Item', 'Type', "Chocolate Chip");
2 : $Response->Cookies('Type')->{Expires} = "July 31, 1999";
3 : $Response->Cookies('Type')->{Domain} = "msn.com";
4 : $Response->Cookies('Type')->{Path} = "/www/home/";
5 : $Response->Cookies('Type')->{Secure} = 0;
 

We are sorry about the verbosity of the SetProperty function, when compared to VBScript, but anything else doesn't quite fit with the lanaguage. For a good argument about it (I've had plenty) contact Jan Dubois (jan.dubois@ibm.net), and have it explained why it has to be that way. (sorry Jan!)

Finally I would just like to mention that Win32::ASP has a more elegant interface to setting the cookies, which does it a more perl-like way. It uses a hash to set things like the Expires and Path, and does it all in one function call. It also allows you to set cookies that expire in a definite time from now, such as "+3h" for expiring in 3 hours time.


5.3.2. Buffer Property

This is a simple boolean value that says whether or not your output is cached before being sent to the browser. This allows you to send headers after you have used one of the output functions ($Response->Write or $Response->BinaryWrite).

Buffer cannot be set after any output has been sent to the client. For this reason it should be set as the first line of your script (or possibly after including any modules using "use ModuleName;".

Syntax

     $Response->{Buffer} = 1;  

Buffer can also give a speed up because it enables HTTP 1.1 Keep-Alive connections. See the HTTP 1.1 spec for details on that.


5.3.3. CacheControl Property

The CacheControl property enables you to override the default value of "Private" which means that proxy servers won't cache ASP output by default. This is probably a good thing because most ASP pages will be dynamic, nevertheless, I won't judge you on using it, so here's the syntax:

     $Response->{CacheControl} = "Public";  


5.3.4. Charset Property

This controls the character set that is sent as part of the Content-Type header. This is rarely used in PerlScript because Perl doesn't yet support wide characters very well (Perl 5.006 will, but that's a long time off).

     $Response->{CharSet} = "ISO-LATIN-7";  

The server does not check if your charset is valid - it simply inserts the string.


5.3.5. ContentType Property

The ContentType property allows you to send different types of data back to the browser (such as a GIF, or a PDF file for example). The default ContentType is "text/HTML".

Syntax

     $Response->{ContentType} = "image/GIF";  

Note that any binary formats (which is most formats other than "text/plain" and "text/html") require some forcing to be properly binary. This is because ASP assumes wide characters throughout (2 byte characters for internationalisation), and Perl only supports single byte characters (i.e. standard ASCII). To get around this, Perl (or the Win32::OLE module) pads all data out to 2 bytes with NULL bytes. If you leave this as default a web browser will stop when it sees NULL and you won't get your GIF. Here is some code I have in Win32::ASP to get around this, it works by explicitly converting to a non-wide OLE format:

     1 : use Win32::OLE::Variant;
2 :
3 : sub BinaryWrite (@) {
4 :     my ($output);
5 :     foreach $output (@_) {
6 :         if (length($output) > 128000) {
7 :             BinaryWrite (unpack('a128000a*', $output));
8 :         }
9 :         else {
10 :             my $variant = Win32::OLE::Variant->new( VT_UI1, $output );
11 :             $main::Response->BinaryWrite($variant);
12 :         }
13 :     }
14 : }
 

Just ignore the bit about unpack and 128000 - ask me another time. The important parts are to create a new Win32::OLE::Variant variable of type VT_UI1, and send that with BinaryWrite. Of course, don't forget to "binmode" your filehandle if reading from a file first.


5.3.6. Expires Property

The Expires property allows you to force an expire of the page. This is to prevent a browser cache from keeping your page too long (or prevent the browser from caching your page totally)

Syntax

     $Response->{Expires} = $minutes;  

Where $minutes is the number of minutes to store your page for in the browser's cache. Set $minutes to 0 to have it expire immediately (i.e. no caching).


5.3.7. Expires Absolute Property

The ExpiresAbsolute property is similar to the Expires property, only you specify a date and time in the VBScript date/time format.

Syntax

     $Response->{ExpiresAbsolute} = "Month dd, YYYY HH:MM:SS";  

Where Month is the full month name (January, February etc), dd is the day of the month, YYYY is the year in full format, HH is the hour in 24 hour format, and the rest is obvious. The time component is optional.


5.3.8. IsClientConnected Property

This read only property tells you if the browser is still waiting for results from the server, or if they gave up and went somewhere else. This is quite useful for long scripts, although why IIS doesn't kill the script itself is beyond me.

Syntax

     if ($Response->{IsClientConnected}) { ... }  


5.3.9. PICS Property

PICS is the Platform for Internet Content Selection or something like that. It stops little kiddies seeing dirty porn on the internet, anyway if you need to use PICS, you'll know what it's all about.

Syntax

     1 : $Response->{PICS} = '(PICS-1.1<http://www.rsac.org/ratingv01.html> \
2 : labels on "1997.01.05T08:15-0500" until "1999.12.31T23:59-0000" \
3 : ratings (v 0 s 0 l 0 n 0))';
 

(the \ indicates that this wasn't meant to wrap - it's all one line). I think the PICS validators gives you this string.


5.3.10. Status Property

The Status property allows you to modify the status line returned by the server. See the HTTP spec for valid status headers.

Syntax

     $Response->{Status} = "401 Unauthorized";  

Common status lines are:


5.3.11. AddHeader Method

The AddHeader method allows you to add arbitrary HTTP headers to your output. Some of the functionality of AddHeader is duplicated (for example Cookies), and the IIS docs recommend you use the other methods if available, however I think you're probably smart enough to figure out what your needs are.

Syntax

     $Response->AddHeader('My-Header', 'Value');  

The ASP object model docs say something about not using underscores in the name of the header (the first parameter), but it's explained really badly, so I don't have a clue what it's talking about.

As with the other things that output HTTP headers, call this before normal output, or turn on buffering.


5.3.12. AppendToLog Method

This allows you to write data into the IIS log file. Strikes me as a dumb idea, but if you want to do it, go ahead... (oh, and why isn't this a member function of the Server object!)

Syntax

     $Response->AppendToLog("This is a silly idea");  

The parameter can't contain any comma's because the default IIS log format is comma delimited (since when could CSV not handle commas in data???). Also I don't know how this will work if you have your server log format as NCSA instead. I guess it will silently fail or something... And apparently you have to set a parameter for the log format to allow this to work. I'm not going to tell you how to do this (it's in the ASP docs) because it's such a bad idea.


5.3.13. BinaryWrite Method

There was a brief example of using BinaryWrite above (under ContentType), so I won't go into detail. Needless to say it outputs binary instead of converting from wide characters to ASCII.

Syntax

     $Response->BinaryWrite($variant);  

Be careful that you have properly created a variant type using Win32::OLE::Variant first. See the code above under ContentType.


5.3.14. Clear Method

The Clear method deletes all buffered output. It will not clear any output if you didn't turn on buffering (see $Response->{Buffer} above), in fact it will create a runtime error. It also won't clear any headers you sent.

Syntax

     $Response->Clear;  


5.3.15. End Method

When exiting a PerlScript prematurely it's not sufficient to just 'die' or 'exit', you have to actually tell the server to stop processing ASP as well. Luckily for you I've overloaded both "die" and "exit" in Win32::ASP so as long as you include my module you don't have to worry about this.

Syntax

     $Response->End;  

I think this has some special magical interaction with VBScript (and probably JScript) so you just have to call this and all processing stops. With PerlScript you have to call both this and exit() or die(), or just use Win32::ASP and make life easier for yourself.


5.3.16. Flush Method

The flush method makes the server go to the toilet for you. Damn, that's not it. Instead it flushes all buffered output. As for the Clear method, this only works when buffering is turned on

Syntax

     $Response->Flush;  

For some reason, calling flush stops "Keep-Alive" requests from being honoured.


5.3.17. Redirect Method

This method tells the browser to go to another URL.

Syntax

     $Response->Redirect($URL);  


5.3.18. Write Method

Unless you are using Win32::ASP, you'll get to know the Write method quite well. Simply put it sends ASCII output to the web browser.

Syntax

     $Response->Write($output);  

$output is the text you want to output. It can contain HTML markup as required, although it cannot contain the string "%>" - you must substitute "%\>" for this otherwise you get an error. This is something to be careful of if you have a table that uses a percentage width:

     <TABLE WIDTH=100%>  

To output that you need to use the following code:

     $Response->Write("<TABLE WIDTH=100%\>");  

Of course this won't happen to you because you always follow the W3C's guidelines and put quotes around your attributes right?

A good way to use $Response->Write is with Perl's heredoc format:

     1 : $Response->Write(<<EOF);
2 : <TABLE WIDTH=200>
3 : <TR>
4 : <TD>This is a single element in a table</TD>
5 : </TR>
6 : </TABLE>
7 : EOF
 


5.4. The Server Object

The Server Object provides access to methods and properties on the server.


5.4.1. ScriptTimeout Property

This is the maximum amount of time a script will run before the server terminates it automatically. It cannot however terminate a running server component (an OLE object).

Syntax

     $Server->{ScriptTimeout} = $secs;  

Where $secs is the number of seconds for the script to run. The default is 90 seconds, although this can be changed in the metabase. The timeout value cannot be set to a value less than that in the metabase - well it can, but it will timeout at the value in the metabase.


5.4.2. CreateObject Method

This is the same as Win32::OLE->new (see the docs for Win32::OLE for more on this). It creates an instance of an OLE object (the ASP docs calls these Server Components).

Syntax

     my $component = $Server->CreateObject($progID);  

Where $progID is the type of object to create, and is in the format [Vendor.]Component[.Version] as a string.

Server created objects have page scope. This means they will be destroyed at the end of the page - but make sure you clean up the object first. For example, make sure you close ADO.Connections, otherwise you end up with dangling connections.

To change the scope of the object, the ASP docs detail storing the created object in either a Session (see below) or Application variable. This doesn't work with PerlScript at the moment - some work needs to be done investigating how this would be achieved.

You can however use a <OBJECT> tag with RUNAT=Server and SCOPE=[APPLICATION|SESSION]. This should work, although I've not personally tested it (anyone?).

You can destroy the object just as you can with Win32::OLE, by undef'ing it:

     undef $component;  


5.4.3. HTMLEncode Method

The HTMLEncode method converts characters that would normally be unprintable as HTML to their HTML equivalents. These characters include "<", ">", "©" etc. This is extremely useful if you need to print out as HTML something you got from another source (e.g. a database or another file).

Syntax

     <%= $Server->HTMLEncode("This is an XML tag: <p/>"); %>  


5.4.4. MapPath Method

This method is quite useful. Those of you who read my first tutorial (which promised a "Modularised PerlScript" tutorial this month - sorry ;-)) will have seen my use of it there. What this method does is read the server configuration and map HTTP server relative paths to machine relative paths.

Syntax

     my $file = $Server->MapPath("/users/matt/index.asp");  

If the path given starts with a "/" it maps it from the root, however it will also map paths higher up the tree than your current location, such as "images/my_pic.jpg". What it won't do is relative paths using either "." or ".." - dumb eh?

MapPath will also map paths that don't exist, in doing so it will figure out the path as far as it can go using the virtual directories defined on the server, and then just append the path to it (converting "/" to "\" as it goes).

To use a module that exists in the current file's path, you can do this:

     1 : my $path = $Server->MapPath($Request->ServerVariables('PATH_INFO'));
2 : $path =~ s/(.*)\\.*?$/$1/;
3 : unshift(@INC, $path);
4 : require MyModule;
 


5.4.5. URLEncode Method

This escapes the characters in URL encoding format for the given string.

Syntax

     my $encodedString = $Server->URLEncode($string);  

For example "Hello World" becomes "Hello%20World".


5.5. The Session Object

Sessions are determined to be a particular user's time on this application. Because of the statelessness of HTTP (i.e. connections are dropped at the end of the request), sessions need to be defined by the server. It does this by two methods:

  1. It sends a cookie to the browser with a unique identifier
  2. It maintains a timeout for each user. The session is over when the user doesn't do anything and the timeout is reached.
The sequence of events is that happens when a user requests an ASP page is like this:
  1. The user sends the request, along with cookies for that server/page.
  2. If one of the cookies is a session ID, the server checks if that session has timed out.
  3. If it has timed out, a new session ID is assigned (and a new session environment created).
  4. If it hasn't timed out, the session is restored from in memory
  5. If no cookie was sent, a new session is created.

Session objects/variables exist in the same way that Application objects/variables exist, and the access method is the same:

     1 : # Read a session variable:
2 : my $sessvar2 = $Session->Contents('Name');
3 : # or
4 : my $sessvar3 = $Session->Contents->Item('Name');
5 :
6 : # Set a session variable:
7 : $Session->Contents->SetProperty('Item', 'Name', $sessvar);
 

One nice thing you can store is an array. However you can't store a hash without flattening it into an array first:

     $Session->Contents->SetProperty('Item', 'Matt', [1,2,3]);  

Note you are setting a reference to an array, if you have a pre-constructed array use a backslash "\" before the variable name: "\@MyArray".


5.5.1. Contents Collection

Contents is the collection that stores all session variables that weren't created using the <OBJECT> syntax. It can be accessed using either the Item method, or by simply using as a function (because Item is the default method). You can also iterate over the collection using "in".

Syntax

     my $sessvar = $Session->Contents('MySessVar');  

which is just a short way of saying:

     my $sessvar = $Session->Contents->Item('MySessVar');  

To set a session variable (which is all a member of the Contents collection is) you need to use the SetProperty method:

     $Session->Contents->SetProperty('Item', 'MySessVar', 'My Session Value');  


5.5.2. StaticObjects Collection

This collection contains all objects created with the <OBJECT> tag with a scope of SESSION. This is so you can iterate over all the static session objects you have created. This collection is read only.

Syntax

     my $sessobj = $Session->StaticObjects('MyObj');  


5.5.3. CodePage Property

Sets or retrieves the codepage that will be used to display dynamic content. I guess this is in the session object so that a user can select a language for his session, and have it remain that way.

Syntax

     $Session->{CodePage} = $codepage;  

This has to be a valid codepage for the system the script is running on.


5.5.4. LCID Property

LCID is the Locale Identifier. It is an international standard. If you need to use it you probably know how it's formatted.

Syntax

     $Session->{LCID} = $localeid;  


5.5.5. SessionID Property

This is the internal identifier for the session that the ASP dll uses to compare with the cookie sent. I think this value is read only, but the docs don't state it clearly (and I've never had a need to use this).

Syntax

     my $sessId = $Session->{SessionID};  

The ASP docs say not to use this as a database primary key, as the same session id can be re-created after a server restart. I'm not going to tell you not to - learn the hard way if you want.


5.5.6. Timeout Property

The timeout property is the length of time the session runs for in minutes. Unlike the script timeout property, this can be set lower than the default which is 20 minutes.

Syntax

     $Session->{Timeout} = 60 * 8; # 8 hours  


5.5.7. Abandon Method

This will abort the current session, just as a timeout would. All objects stored in session objects are destroyed.

Syntax

     $Session->Abandon;  


6. Where to find more

This document, and the Win32::ASP module, and many other things Perl, are archived on my web site at FastNet Software Ltd. Click on Perl for all my Perl things (or call me if you have contract work available!).


7. Copyright

I really like to distribute things under the Artistic Licence (the licence that Perl is distributed under), but I put a lot of work into this document, and so I followed Tom Christiansen's views on this:

This document is Copyright © Matt Sergeant. All Rights Reserved.

This document is not public domain. I hope someday that this might go into a book, or other publication (such as a magazine article) in some modified form. While it is copyright by me with all rights reserved, permission is granted to freely distribute verbatim copies of this document provided that no modifications outside of formatting be made, and that this notice remain intact. You are permitted and encouraged to use its code and derivatives thereof in your own source code for fun or for profit as you see fit. But so help me, if in six months I find some book out there with a hacked-up version of this material in it claiming to be written by someone else, I'll tell all the world that you're a jerk. Furthermore, your lawyer will meet my lawyer over lunch to arrange for you to receive your just deserts. Count on it.

Many thanks to the following people for their help: