Skip to main content.

Web Based Programming Tutorials

Homepage | Forum - Join the forum to discuss anything related to programming! | Programming Resources

Web Programming with Perl5

Without a title - Title


5
Putting It All Together




The first step to mastering CGI programming and creating useful and dynamic programs is understanding what goes on when your CGI script is run. The transaction that occurs between the Web server and the browser before your script is run--and the data that is transferred back and forth during this transaction--is the only input your script has. Thus understanding exactly how something occurs and what occurs during this transaction is crucial.



After looking at all that happens during the transaction, we'll focus on how Perl5 modular libraries such as CGI.pm and the libwww modules can be used to greatly simplify and enhance
programming CGI scripts. CGI.pm and the libwww modules provide simple APIs (applications programming interfaces) which allow you to supply a few simple arguments to a supported Web-related function. The module then generates appropriate HTML output for you. Tasks such as creating HTML forms, imagemaps, and headers are just a few of the tasks enhanced by using these Perl5 modules in your program.

HTTP Transactions

HTTP transactions are stateless; that is, neither the browser nor the server store the "status" or state of the other. During an HTTP transaction, a client, such as Netscape, establishes a connection to a remote server, then issues a request in the form of a URI. The remote server then processes the request, returns a response, and closes the connection.

Figure 5.1 depicts the stages in an HTTP transaction that include a CGI script.

Figure 5.1. HTTP transactions with CGI scripts.



NOTE:

A URI, or Uniform Resource Identifier, simply refers to the formatted string that references a specific network resource. URIs have been known by many names:

WWW Address

Netsite

Uniform Resource Locator (URL)

Uniform Resource Name (URN)
Universal Document Identifiers







Throughout this book, the "URI" notation will be used.


Certain data and variables are passed between the browser and the server during each step of an HTTP transaction. A transaction is the process that begins with typing a URI (http://www.coolsite.com/index.html) into a Web browser and ends with the Web server sending the appropriate response. That response is usually just a Web page or a GIF image but can be anything from the output of a CGI Perl5 script to a stream of video. The data and variables, or messages, passed back and forth between the browser and the server during the transaction are called HTTP headers. Understanding the details of the HTTP transaction and being aware of the syntax and content of these headers is critical. The content of these headers is made available by the Web server in the form of environment variables to your CGI. Using information obtained from these environment variables, you could, for example, send out different Web pages based on what kind of Web browser someone is using. Let's now explore the interaction between the browser, also known as the client, and the Web server during a typical HTTP transaction.

The Connection

On the Internet, all communication takes place over a TCP/IP (Transport Control Protocol/Internet Protocol) connection. TCP/IP is the method or protocol used to transport data across the Internet. TCP/IP connections are established via a three-way handshake. The browser, or client, sends a connection request, the server responds, and then the client acknowledges the response.

The first step in an HTTP transaction begins when a client sends a connection request to the HTTP port on a server.

Port 80 is usually configured as the HTTP port on most Web servers. The Web server then responds to the connect request with a connect response. The client acknowledges the connect response and proceeds to send the first part of the request.



TIP:

Though port 80 is typically used for a Web server (also known as an httpd), different ports can be used. The port number can be configured on a UNIX-based server in the
/etc/services file, or through Netscape's configuration server. You can actually have multiple Web servers, each using different ports, running on the same machine simultaneously. Browsers will always default to connecting to port 80, however. This behavior can be changed by adding a :portnumber to the end of the URI. For example:

http://www.coolsite.com

will default to port 80, and thus is identical to

http://www.coolsite.com:80

To connect to a Web server configured to run on port 81, you must explicitly specify the port number in the URI:

http://www.coolsite.com:81

Sites that use Netscape server software typically are actually running two Web servers. One on port 80, which serves Web pages, and one on port 81, which serves as a configuration server. So, to configure a Netscape Web server, you actually connect to a separate Web server (HTTPD) running on a separate port.


The Request

The server acknowledges the first part of the request. The client then sends the second part, and without waiting for a response, follows up with the third and final part of the request.



NOTE:

If you have an external modem, take note of the send and receive lights the next time you click on a link in your Web browser. TCP/IP handshaking overhead is responsible for much of the activity.


At this point, a TCP/IP connection has been established between your browser and the Web server, and the Web server has received an HTTP request from the browser. Contrary to what a casual observer sees, this request contains more than just what is typed in the URI field in your Web browser.

The browser takes the URI that you give it, and from it derives the address of the server to contact, contacts it, and sends it a request. There are actually two types of requests a browser can send: the older SimpleRequest and the newer FullRequest. Both types of requests have the same basic format, but the FullRequest is much more versatile. The SimpleRequest has the following format:

GET <URI> CrLf

where GET is a method that basically asks the server to retrieve whatever data is identified by the <URI> element. It is important to note that if the <URI> element points to a CGI script or some other data-producing executable process, the output generated by running the script or the
process will be returned to the browser, not the contents of the script or the source code. The CrLf is your basic carriage return followed by a linefeed sequence.

The GET and POST methods are by far the most commonly used methods and will be discussed in more detail in the "Methods" section. With the new FullRequest scheme, methods other than GET and POST are now possible. Also, additional headers may be appended to the request. The FullRequest has the following format:

METHOD <URI> "HTTP/1.1"    CrLf

name: value            CrLf

name: value            CrLf

where METHOD is one of the methods discussed in the "Methods" section and <URI> is the specified URI. The name: value header will likely be one of the headers discussed below. Methods The methods in Table 5.1 are included, or proposed for inclusion (at the time of this writing), in the current HTTP specification in a FullRequest.



NOTE:

Note that some of the methods described in Table 5.1 are not yet fully implemented in currently released browsers but are accessible by using CGI.pm or the HTTP::Request module. Fully elaborating on these methods is beyond the scope of this text. For more details on these methods, please consult the following URL:


http://www.w3.org/pub/WWW/Protocols/HTTP/Methods.html

Table 5.1. HTTP FullRequest methods.

Method Description
GET The GET method is used every time your browser requests a document. Variables may be sent using this method through URI Encoding (described in the "GET Method" section).
POST The POST method is used most prominently to submit URI encoded data or text from an HTML form.
HEAD The HEAD method is used in the same way as the GET method, but the server returns only the HTTP headers and no document body.
DELETE This method instructs the server to attempt to delete the data referenced by the specified URI.
PUT This method instructs the server to append data in the body section of the request to the specified URI.
LINK By adding header meta-information to an object, this method can link an object to another specified object.
UNLINK This method removes headers (meta-information) that are specified in the request for the specified object.
SHOWMETHOD This method allows the client to request interface specifications for methods not covered in the current HTTP specification.
SPACEJUMP Similar to the TEXTSEARCH method, this header is used to specify the coordinates of a mouse click on a gif image.
TEXTSEARCH This method takes the text from the body of the request and instructs the server to perform a simple search for the specified text.
GET Method The GET method is used for all normal document requests. In other words, a request with a GET method is what your Web browser sends to the Web server to request a document. The GET method is very simple, only requiring a single argument: which URI to get. Here's the syntax of the GET method:

GET http://www.netsite.com/directory/filename CrLf

The GET method can also be used to send name/value data back to the server. Data is returned by URI encoding it and appending it to the end of the URI. URI encoding refers to the replacement of special characters such as tabs, spaces, question marks, and quotes with their HEX equivalents. All data passed from the browser to the Web server must be URI encoded. URI encoding will be discussed in more depth in the next section. The process of URI encoding the name/value pair data and then appending it to the end of the URI is what happens when a browser submits a GET method HTML form. In other words, a form called like this

<FORM METHOD="GET" ACTION="/cgi-bin/mycgi.cgi">

will cause the browser to URI encode the name/value pair form data (the names and values of the fields you create in your form), then append it to the end of the URI. Digital's AltaVista search engine uses the GET method. A search for the best web site on AltaVista caused the following URI to be generated and sent by Netscape:

http://altavista.digital.com/cgi-bin/query?pg=q&what=web&fmt=.&q=the+best+web+site

Breaking this URI down into its components, the first part of the URI calls a CGI called query in the cgi-bin/ directory:

http://altavista.digital.com/cgi-bin/query

This is followed by a ?, indicating the presence of URI encoded data, and four URI encoded name/value pairs:

?pg=q&what=web&fmt=.&q=the+best+web+site

The URI encoding of this string makes it look like a bunch of garbage at first glance. URI encoded name/value pairs are separated by the & character. The + character represents whitespace. Thus, this string translates into the following four name/value pairs once URI decoded:

pg = q
what = web
fmt = .
q = the best web site

Using the GET method in all cases is not recommended, as servers usually have size limitations on the length of URIs due to operating system environment constraints. For example, the size is 1240 bytes in the UNIX environment. The GET method is useful, however, in cases where you need to send your script an argument from a URI; for example, if you had written a script called classifieds.cgi, and you wanted the script to support multiple product categories. A good way to do this would be to send the category to the script as a variable in the URI. An HTML page could have links to the different categories like this:

<a href=http://mysite.com/cgi-bin/class.cgi?category=pets>Pets</a>

<a href=http://mysite.com/cgi-bin/class.cgi?category=stuff>Stuff</a>

<a href=http://mysite.com/cgi-bin/class.cgi?category=cars>Cars</a>

When your script is called, you can use the category variable to create different dynamic output based on what was specified in the category variable. Another advantage to this method is that users can bookmark your script, and since the variables are part of the URI, they can bookmark different categories.



CAUTION:

Be very careful when using GET requests to supply your script with input. A malicious user can easily change or modify the variables in the URI that calls your script. Depending on how you use the variables, this could easily cause undesirable and possibly dangerous results.


POST Method The POST method also URI encodes your data, but instead of the data being tagged on to the URI, it is sent separately after all of the other request headers and is available as STDIN to your script. CGI.pm provides transparent access to the data in STDIN generated by a POST method request. The POST method uses the CONTENT_LENGTH environment variable to indicate how many bytes of the standard input are used for the encoded input data. The CGI script will keep reading input data until it has read CONTENT_LENGTH bytes of data. The CONTENT_TYPE environment variable indicates to the script what kind of data is being sent. The content-type for HTML forms returning name/value data with the POST method should always be application/x-www-form-urlencoded.

To use the POST method in an HTML form, simply specify POST as the method:



<FORM METHOD="POST" ACTION="/cgi-bin/mycgi.cgi">

Later, you will see how CGI.pm or the HTTP::Request module can be used to construct headers with these methods from within your scripts and decode URI encoded form data. The ability to easily generate GET, POST, and other headers makes the following tasks possible:

URI Encoding Why should you care about URI encoding? Here are three reasons:

  1. All data contained in HTML forms passed from the browser to the Web server (your CGI) is URI encoded. This data must be decoded.

  2. Data that is sent to your CGI attached to the URI using the GET method must be encoded.

  3. CGI.pm and the CGI::Base module transparently handle URI encoding and decoding.

  4. Because all of your data passed between the browser and the server is being encoded and decoded, you should understand how and why.

URI encoded data is introduced in two instances. First, data contained in HTML forms (using GET or POST methods) will be automatically URI encoded by the browser before being sent to your script. Second, if you want to send input to your script, you must know how to encode the data when you append it to the URI if the browser is not doing it for you.

URI encoded data is appended to the URI in the following manner:

http://www.netsite.com/mycgi?query_string

where query_string represents a string of URI encoded name/value pairs. Here's a typical query_string:

name=John+Doe&age=30&grade=96%25

Each name/value pair is separated by the ampersand character (&). In this example %25 represents the % character. In addition, the plus symbol (+) represents a space. When decoded, this example contains the following name/value pairs: name = John Doe

age= 30
grade = 96%

There are many ASCII characters that must be URI encoded. Use the following table to determine which HEX values you must use to represent the characters shown in Table 5.2.

Table 5.2. URI Characters That Must Be Encoded.

Character URI Encoded HEX Equivalent
Tab %09
Space %20
" %22
# %23
% %25
& %26
( %28
) %29
, %2C
. %2E
/ %2F
: %3A
; %3B
< %3C
= %3D
> %3E
? %3F
@ %40
[ %5B
\ %5C
] %5D
^ %5E
' %60
{ %7B
| %7C
} %7D
~ %7E
So, if someone typed the following into a text field form:

My name is Chris, (ckemp@ro.com); or "http://ro.com/~ckemp".



ÂDo you like my home page?

Netscape (or whatever) would encode it and send it to your CGI as:

My+name+is+Chris%2C+%28ckemp%40ro%2Ecom%29%3B+or+%22http%3A%2F%2Fro%2Ecom%2F%7Eckemp%22%2E



Â+Do+you+like+my+home+page%3F

The special characters are simply replaced with their HEX equivalents. Headers In a FullRequest, the browser may also send a series of headers in RFC-822 format. The most common headers are Accept, which tells the server which MIME object types the browser can handle, and User-Agent, which gives the type and version of the browser. These headers are placed into the environment of your script by the Web server when your script is run, then some additional variables are added to your environment by the Web server. Thus, the variables in Table 5.3 are available to your script.

Table 5.3. Headers.

HTTP_ACCEPT All MIME types that the client will accept, as given by HTTP headers. Each item in this list is separated by commas.Format: type/subtype, type/subtype
HTTP_USER_AGENT The browser the client is using to send the request. General Format: software/version library/version
SERVER_SOFTWARE The name and version of the information server software answering the request (and running the gateway). Format: name/version
SERVER_NAME The server's hostname, DNS alias, or IP address as it would appear in self-referencing URLs.
GATEWAY_INTERFACE The revision of the CGI specification to which this server complies. Format: CGI/revision
SERVER_PROTOCOL The name and revision of the information protocol this request came in with.Format: protocol/revision
SERVER_PORT The port number to which the request was sent.
REQUEST_METHOD The method with which the request was made. For HTTP, this is GET, HEAD, POST, and so on.
PATH_INFO The extra path information, as given by the client. In other words, scripts can be accessed by their virtual pathname, followed by extra information at the end of this path. The extra information is sent as PATH_INFO.
PATH_TRANSLATED The server provides a translated version of PATH_INFO, which takes the path and does any virtual-to-physical mapping to it.
SCRIPT_NAME A virtual path to the script being executed, used for self-referencing URLs.
QUERY_STRING The information that follows the ? in the URI that referenced this script.
REMOTE_HOST The hostname making the request.
REMOTE_ADDR The IP address of the remote host making the request.
AUTH_TYPE If the server supports user authentication and the script is protected, this is the protocol-specific authentication method used to validate the user.
REMOTE_USER If the server supports user authentication and the script is protected, this is the username they have authenticated as.
REMOTE_IDENT If the HTTP server supports RFC 931 identification, then this variable will be set to the remote username retrieved from the server.
CONTENT_TYPE For queries that have attached information, such as HTTP POST and PUT, this is the content-type of the data.
CONTENT_LENGTH The length of the said content as given by the client.
Later, in Example 1, I'll show you how all of these headers are accessible as environment variables by using CGI.pm and the HTTP::Headers class from within your script. So, putting it all together, here's an example of a typical FullRequest HTTP request:

POST /cgi-bin/Echo2 HTTP/1.0

Accept: */*; q=0.300

Accept: application/octet-stream; q=0.100

Accept: text/plain

Accept: text/html

From: somebody@somewhere.com

User-Agent: Mozilla/5.0Platinum

Content-length: 588

Content-type: application/x-www-form-urlencoded

Now that I've discussed the request, it's time to look at the response from the Web server.

What Happens after the Request

After the HTTP request has occurred and the target of the request is your CGI script, a few things occur. Unless your script is being run on an ISP's server using a "CGI-wrap" type program (discussed in Chapter 3, "Security on the Web"), the Web server executes your script. Every process (or running program) in a UNIX environment is executed by (or owned by) the user who executes it. This makes it easier to keep track of which users are allocating which resources and running which programs. However, in the case of a CGI script, the user causing the script to be run is unknown--some Web browser somewhere. The Web server software executes your script as some non-privileged user like nobody or webuser. (You can actually use any name you want; the name can be configured in your Web server's config file.) For security reasons, this nobody user running your script typically has almost no privileges on the system.

So, now you have a program running on the server as nobody or webuser or something similar. It is important to know the name of the user that your script is running as, especially if your program must read or write to any files. Remember that in a UNIX environment, file permissions are very important. If the Web server is running your CGI as nobody and it needs to write to a file owned by betty, and this file is chmod'd to 744 (-rwxr- -r- -) so only the owner can write to it, your CGI will fail because it can't write to the file.

After your program is run, all the variables discussed in the previous section are stored in its environment and available to it. Using these variables in your CGI is quite simple by using the Perl WWW libraries.

The following output is the actual environment that was generated by a Netscape Communications Server after a request was sent from Netscape Navigator:

SERVER_SOFTWARE   : Netscape-Communications/1.12

SERVER_NAME       : somewhere.corp.sgi.com

GATEWAY_INTERFACE : CGI/1.1 <br>

SERVER_PROTOCOL   : HTTP/1.0

SERVER_PORT       : 80

REQUEST_METHOD    : GET

PATH_INFO         : /path/foo

PATH_TRANSLATED   : /var/www/htdocs/path/foo

SCRIPT_NAME       : /html_tutorial/cgi-bin/env.cgi

QUERY_STRING      : query_string

REMOTE_HOST       : amiga.huntsville.sgi.com

REMOTE_ADDR       : 123.255.123.1

AUTH_TYPE         :

REMOTE_USER       :

REMOTE_IDENT      :

CONTENT_TYPE      :

CONTENT_LENGTH    :

HTTP_ACCEPT       : image/gif, image/jpeg, */*

HTTP_USER_AGENT   : Mozilla/2.0S (X11; I; IRIX 6.2 IP22)

HTTP_REFERER      : http://server.sgi.com/html/cgi.html

ANNOTATION_SERVER :

The Response

Now that your CGI script is running, a response must immediately be sent back to the browser. The response follows a specific format, which will be discussed later, called an HTTP Response Header. HTTP Response Headers



NOTE:

An HTTP Response Header is sent by the server in response to any HTTP request, not just requests for CGI scripts. If the client requests an HTML page, a response header is generated by the server and is sent to the browser before the HTML page. HTTP response headers are not typically generated for requests for GIF and JPEG images.


Here is an example of an HTTP response header:

HTTP/1.0 200 OK

Server: Netscape-Communications/1.12

Date: Monday, 21-Oct-96 01:42:01 GMT

Last-modified: Thursday, 10-Oct-96 17:17:20 GMT

Content-length: 1048

Content-type: text/html

CrLf

The contents (or body) of the message would follow after the Content-type and a Carriage-
Return Line-Feed (CrLf) sequence. Parsed Header Output There are two ways the response can be handled. By default, the Web server reads and parses the output (STDOUT) of your CGI script before sending the output on to the browser. The Web server will then automatically generate all necessary HTTP response headers. This is called parsed header output. The output of your CGI script is actually appended to the response headers generated by the Web server. If your script contains no output (HTML, or text, or whatever), it is expected to output either a Location or Status header (discussed below in the "Status Response Header" and "Location Response Header" sections). Thus, in summary, the Web server sends to the client in this order: a response header, a blank line (CrLf), then any output from your CGI script. This presents some problems:

Non-Parsed Header Output Fortunately, there is an easy way around this problem. By simply beginning the name of your scripts with nph-, the Web server will not add any headers or interfere in any way with the output of your script. So, if you were working on a script called mycgi.pl, just call it nph-mycgi.pl, and the Web server won't interfere with its headers. This is called a non-parsed-header script, or NPH script. The output of a NPH script is not parsed and is sent directly to the client. This is highly recommended. On most popular Macintosh based Web servers, all CGI scripts are treated as NPH scripts. Figure 5.2 depicts an HTTP transaction with a NPH CGI script.

Figure 5.2. The HTTP transaction with a NPH CGI.



NOTE:

This feature must be enabled explicitly in the config files for some servers.


The CGI must respond with a valid and appropriate response header that tells the browser what to expect or do next. If you use NPH scripts, you must be sure to output the proper headers; otherwise, the client will see a 500 error message.

There are three basic Response headers: Status, Content-Type, and Location. All responses must contain either a Status header or a Location header that redirects the browser to another URI. If the script will be outputting HTML, text, or some other MIME compliant data object, the
Status
header must be followed by a Content-Type header that specifies the MIME type of the object that follows. Other RFC-822 compliant headers such as Date and Server can also be
specified in the response header. Status Response Header The Status response header (or status line) indicates which version of HTTP the server is running, together with a result code and an optional message. It should not be sent if the script returns a Location header.

The first line of the response from the server typically looks something like this:

HTTP/1.0 200 OK

where HTTP/1.0 is the version of HTTP the server is running, 200 is the result code, and OK is the message that describes the result code. There are many other result codes besides 200 that can be sent. The following Status response headers can be sent to the browser by your CGI in NPH mode. Success 2xx Result codes in the 200s indicate success. The body section if present is the object returned by the request. The body must be in MIME format and may be only in text/plain, text/html, or one of the formats specified as acceptable in the request in the HTTP/ACCEPT header.

200 OK The request was successful.
201 CREATED Following a POST command, indicates success, but the textual part of the response indicates the URI by which the newly created document should be known.
202 Accepted The request has been accepted for processing, but the processing has not been completed.
203 Partial Information When received in the response to a GET command, this indicates that the returned meta-information is not a definitive set of the object from a server with a copy of the object but is from a private overlaid Web. This may include annotation information about the object, for example.
204 No Response Server has received the request, but there is no information to send back, and the client should stay in the same document view. This is mainly to allow input for scripts without changing the document at the same time.

Redirection 3xx Result codes in the 300s indicate the action to be taken (normally automatically) by the client in order to fulfill the request.

301 Moved The data requested has been assigned a new URI; the change is permanent. The response contains a header line of the form: URI: <url> CrLf, which specifies an alternative address for the object in question.
302 Temporarily Moved The data requested actually resides under a different URL; however, this is not permanent. The response format is the same as for Moved.
303 Method Like the found response, this suggests that the client go try another network address. In this case, a different method may be used, too, rather than GET. Method: <method> <url>body-section. The body-section contains the parameters to be used for the method. This allows a document to be a pointer to a complex query operation.
304 Not Modified If the client performs a conditional GET request, but the document has not been modified as specified in the If-Modified-Since variable, this response is generated.



Client ERROR 4xx The 4xx codes are intended for cases in which the client seems to have erred. The body section may contain a document describing the error in human readable form. The document is in MIME format and may only be in text/plain, text/html, or one of the formats specified as acceptable in the request.

400 Bad request The request had bad syntax or was impossible to be satisfied.
401 Unauthorized The parameter to this message gives a specification of authorization schemes that are acceptable. The client should retry the request with a suitable Authorization header.
402 Payment Required The parameter to this message gives a specification of charging schemes acceptable. This code is not currently supported.
403 Forbidden The request is for something forbidden. Authorization will not help.
404 Not found The server has not found anything matching the URI given.

Server ERROR 5xx The 5xx codes are for the cases in which the server has erred, or all indications point to the server as being the cause of the error. Like the 400 codes, the body section may contain a document describing the error in human readable form.

500 Internal Error The server encountered an unexpected condition that prevented it from fulfilling the request.
501 Not Implemented The server does not support the ability to satisfy this request.
502 Bad Gateway The server received an invalid response from the gateway or an upstream server.
503 Service Unavailable The server cannot process the request due to a high load or maintenance of the server.
504 Gateway Timeout The response did not return within a time that the gateway was prepared to wait. Similar to Internal error 500 but has more diagnostic value.
In the next section, we will look at how the header() method in CGI.pm can be used in your script to set the Status response header. Content-Type Response Header The most common response header is Content-Type, which contains the MIME type of the object being returned (usually "text/html"), and Content-Length, which indicates the size of the object. Location Response Header If you did not want to return a file but instead wanted your script to send the browser to another URI, the Location response header can be used.

The headers are terminated by an empty line and followed by the "body" of the message. The "body" refers to the text or HTML or other MIME compliant message in the object.

So, bringing all this together, here is the syntax of a basic HTTP response header:

"HTTP/1.0" result-code message CrLf

Header: Value CrLf

Header: Value CrLf

CrLf

BODY

Connection closed by foreign host.

Here is a real-world example of the complete response generated by a Netscape Communications Server. This response was generated after a simple GET request was sent for an HTML file. A similar response could be generated by your CGI script using the header() method in CGI.pm.

HTTP/1.0 200 OK

Server: Netscape-Communications/1.12

Date: Monday, 21-Oct-96 01:42:01 GMT

Last-modified: Thursday, 10-Oct-96 17:17:20 GMT

Content-length: 1048

Content-type: text/html

<HTML>

<HEAD>

<TITLE>Welcome to My Web Page</TITLE>

</HEAD>

<BODY>

Here is my web page.  <b>Do you like it?</b>

</BODY>

</HTML>

Connection closed by foreign host.

Notice that after the response headers are sent, a blank line (CrLf) is sent, which is followed by the body of the response. The body is the Web page, or the data your script outputs. After the data has been sent (the body), the server drops the connection.

When generating your own response headers for NPH scripts (highly recommended), the contents of these response headers are easily modified using CGI.pm, or the HTTP::Response class modules. In the next section, I will show you how CGI.pm or the HTTP::Response class can be used to easily generate response headers.

Perl5 and the WWW Libraries in Action

Perl, with the help of CGI.pm or the WWW libraries, allows complete control over every aspect of the client/server HTTP transaction. Using the CGI::Request WWW library in your scripts to manage and process variables passed between the client and the server during a transaction makes your code more secure, easier to maintain, and easier to write.

CGI.pm versus Individual libwww Modules

CGI.pm and the modular WWW libraries are very similar in function but very different in purpose. CGI.pm includes all of the functionality of the individual modules, and in most cases, more. In fact, Lincoln Stein, the author of CGI.pm and the WWW modules, developed the CGI.pm module in parallel with the CGI, URL, and HTML modules. The individual modules are designed to provide a very optimized solution to about nine different aspects of programming with Perl for the Web. Modules exist for the Web functions listed in Table 5.4. Table 5.4. Function of libwww modules.

Module Function
CGI:: Allows control over HTTP request and response headers. Mini HTTP server, CGI-based imagemapping, "smart" forms.
File:: Parsing of directory listings and a persistent counter.
Font:: Interface to Adobe Font Metrics Files.
HTML:: Creation and parsing of HTML elements. Format HTML as text or PostScript.
HTTP:: Generation of HTTP requests, responses, and headers. Content negotiation and status code processing.
LWP:: Low-level I/O, socket interface, file type recognition.
MIME:: Encoding and decoding Base64 and QP.
URI:: Filtering, processing of URIs, elimination of unsafe characters in strings.
WWW:: Parse robot.txt files.
Think of each of the individual modules as a very focused and optimized solution to a specific task. Most of the time, you will be writing scripts that use features from more than one of the preceding modules. If you have to load any more than three modules, it is actually more efficient to just load the entire CGI.pm. Loading each of the modules separately actually is more resource intensive due to the overhead associated with loading multiple files. The CGI.pm module also has a more robust feature set than the individual modules.

Anything your CGI prints to STDOUT gets sent back to the Web server and out to the browser, unless you are using nph-CGIs, in which case, STDOUT goes directly to the browser without being parsed by the server. Traditionally, before Perl5 and CGI.pm, HTML forms were created by CGI scripts by printing the HTML form elements to STDOUT. Traditionally, the name/value pairs would have to be URI-decoded from either the URI (GET method) or STDIN (POST method). Back in the days of Perl 4, include scripts such as cgi-lib.pl were written to deal with these nuisances. However, there were still problems. Using cgi-lib.pl, the state of a form (the
contents of the variables) was lost from invocation to invocation because neither the browser nor the server retained data from a previous transaction, debugging was difficult, namespace was trampled, and the code was messy and would emit errors before the HTTP response headers were printed if Perl was run in strict mode.

URL:

If you still aren't convinced, a complete rundown on all of the benefits of using CGI.pm and Perl5 over the old methods such as cgi-lib.pl can be found at the following URL:

http://perl.com/perl/info/www/!cgi-lib.html

CGI.pm was created to address these problems and build a wide and extensible bridge between your CGI Perl script and the CGI interface. With CGI.pm, HTML forms can be generated easily by making a few Perl function calls. When you use CGI.pm to generate HTML forms, the values of the input fields from previous queries (if there were any) are automatically used to initialize the form, so the state of the form is preserved from invocation to invocation. URI encoding and
decoding of special characters is handled transparently by CGI.pm. GET and POST methods are handled appropriately. If your script is run from the UNIX shell, the option of inputting variables from the command line is given. In short, CGI.pm takes care of almost all of the gory details of CGI and lets you focus on writing your program.

The easiest way to learn to use CGI.pm or any of the modules is to simply start using them in your scripts.

Example 1: Creating Forms with CGI.pm

The example in Listing 5.1 introduces the usage of CGI.pm in a typical script that generates and displays an HTML form. When run, the script parses the input from GET and POST methods, then prints the HTML output for a Web page with a form on it. If the script finds any data in the input stream (a previously filled-out form), it will be printed at the bottom of the page. The output is shown in Figure 5.3.



NOTE:

The line numbers in Listing 5.1 are for reference only and are not to be typed in.


Listing 5.1. Using CGI.pm with forms

1   #!/usr/local/bin/perl

2   use CGI;

3   $query = new CGI;

4

5   print $query->header;

6

7   print $query->start_html("Using CGI.pm with Forms");

8   print "<H2> Example 1: Using CGI.pm with Forms</H2>

9         <HR>\n";

10

11  &print_prompt($query);

12  &do_work($query);

13  print $query->end_html;

14

15  # Subroutines ###

16

17  sub print_prompt {

18  my($query) = @_;

19  print $query->start_multipart_form;

20

21  print "

22  <table border=0 cellpadding=0 cellspacing=0 width=550>

23  <tr><td>",

24  $query->textfield(-name=>`first_name',

25                    -default=>`John',

26                    -size=>25),

27  "<BR>First Name</td><td>",

28  $query->textfield(-name=>`mi', -size=>2, -default=>`A'),

29  "<BR>M.I.</td><td>",

30  $query->textfield(-name=>`last_name',

31                    -default=>`Doe',

32                    -size=>25),

33  "<BR>Last Name</tr></td>

34  <tr><td colspan=3>",

35  $query->textfield(-name=>`address', -size=>55),

36  "<BR>Street Address</td></tr>

37  <tr><td>",

38  $query->textfield(-name=>`city',

39                    -size=>25),

40  "<BR>City</td><td>",

41  $query->textfield(-name=>`state', -size=>2),

42  "<BR>State</td><td>",

43  $query->textfield(-name=>`zip',

44                    -size=>10),

45  "<BR>Zip Code</tr></td>

46   </table>";

47

48  print "<HR><table border=0 cellspacing=0

49              cellpadding=0 width=550>

50        <tr><td><EM>What Operating Systems Do You Use?</EM>

51        <BR>";

52  print $query->checkbox_group(

53         -name=>`Operating Systems',

54         -values=>[Mac, `DOS', `SiliconGraphics IRIX', `NT 4.0'],

55         -linebreak=>`yes',

56         -defaults=>[`SiliconGraphics IRIX',Macintosh]);

57

58  print "</td><td><P><EM>What Platform is used most?</EM><BR>",

59  $query->radio_group(

60                     -name=>`platform',

61                     -values=>[SiliconGraphics,Sun,Digital,Macintosh],

62                     -linebreak=>`yes',

63                     -default=>`SiliconGraphics');

64

65  print "</tr></td></table>";

66  print "<HR><P><EM>How are you connected to the Internet?</EM><BR>";

67  print $query->popup_menu(

68        -name=>`Connection',

69        -values=>[`ISDN','T-1','Direct Ethernet','Phone','Satellite'],

70        -default=>`Phone');

71

72  print $query->hidden(`Hidden Reference','Bet you did not see me');

73

74  print "<P><EM>What Peripherals are

75         connected to your computer?</EM><BR>";

76  print $query->scrolling_list(

77         -name=>`configuration',

78         -values=>[`CDROM','Sound Card','Video Camera','3D Graphics'],

79         -size=>4,

80         -multiple=>`true');

81

82  print "<P><EM>What do you like about the World Wide Web?</EM><BR>";

83  print $query->textarea(-name=>`Comments',

84                         -rows=>8,

85                         -columns=>60);

86

87  print "<p>";

88  print $query->checkbox(`Add me to your mailing list');

89  print "<P>",$query->reset;

90  print $query->submit(`Action','Send Free Catalog');

91  print $query->submit(`Action','No Free Catalog');

92  print $query->endform;

93  print "<HR>\n";

94      }

95

96  sub do_work {

97

98  unless ($query->param) {

99      print "<b>No query submitted yet.</b>";

100     return;

101     }

102  my($query) = @_;

103  my(@values,$key);

104  print "<H2>Here are the current settings:</H2>";

105  foreach $key ($query->param) {

106     print "<b>$key</b> -> ";

107     @values = $query->param($key);

108     print join(", ",@values),"<BR>\n";

109     }

110  print "<HR></body></html>";

111  }

Figure 5.3. The output from Example 1.

This program is a nice demonstration of using CGI.pm to generate a form and then using the variables entered by the user. Lines 1 and 2 define the script as Perl and include the CGI.pm module. Starting with line 4, CGI.pm is introduced into the script. Let's take a closer look at the different elements from CGI.pm used in this script. Parsing the Input Take a look at line 3:

$query = new CGI;

Line 3 calls the new() method of the CGI class. The new() method parses GET and POST method variables and all of the parameters from the input stream, does all URI decoding, and then stores the results as a new CGI object into the variable named $query. Yes, this one little line does all that. Though this sounds complicated, it greatly simplifies programming a CGI. You can now make simple method calls to this $query object to get environment variables, make forms, and do many other useful things. For example, in line 5, the $query->header method generates a
Content-type:
/text/html\n\n MIME header. In other words, by simply printing $query->header, CGI.pm interprets this statement and prints the default HTTP header for you. In fact, all of the HTML form elements in this example are generated in this way by calling methods such as $query->textfield(), $query->checkbox_group(), and $query->radio_group(), which take certain arguments and automatically generate all of the HTML that make up these HTML form elements. We'll take a look at the methods that automatically generate HTTP headers and HTML form elements next. Generating HTTP Headers If you recall from the beginning of the chapter, the first thing a CGI script must send back to the browser (especially a NPH CGI) is a Content-type header that describes the contents (MIME type) of the document that follows.

The $query->header() method returns the required Content-type: MIME header discussed earlier in this chapter. In line 5 of Example 1, $query->header() was called without any arguments, which caused it to default to text/html. Status codes and other HTTP headers can be passed
using the $query->header() method as well. Let's generate an HTTP header that returns a few different parameters. This requires that we give $query->header() a few different arguments. Passing multiple arguments to a function is quite simple; here's an example that generates an HTTP
response header with a MIME type and one of the status codes we discussed earlier in the chapter:

print $query->header(-type=>`/text/html',

                     -status=>`204 No Response');

If your CGI contained this line as the first thing it printed, a well-behaved browser such as Netscape should receive the 204 No Response header and do absolutely nothing.

If you wanted to use a redirect to send the browser to some other URL, a redirect() method exists to facilitate this:

print $query->redirect(`http://somewhere.else/');

Generating HTML Headers $query->start_html generates all necessary HTML headers to begin a web page, as demonstrated in line 7 of example 1.

print $query->start_html(-title=>`My Online Storefront,

                         -author=>`ckemp@ro.com',

                         -base=>`true',

                         -BGCOLOR=>"#ffffff"',

                 -NEWHEADER=>`whatever');

Adding additional HTML tags to this method is as easy as specifying them. For example, NEWHEADER in the preceding example could be anything. The base tag, if set to true, will resolve relative URLs in the document.

To end the HTML document, use the end_html() method:

print $query->end_html;

Generating HTML Form Elements

print $query->startform(-method=>$method,

                        -action=>$action,

                        -encoding=>$encoding);

<... form stuff  ...>

print $query->endform;

The defaults are

method: POST

action: this script

encoding: application/x-www-form-urlencoded

In all of the following methods that generate HTML form elements using CGI.pm, the field will be initialized with its previous contents from earlier invocations of the script. When the script is first called, or if the value was not set upon the last invocation, the field will have no value.

Many of the parameters for the following methods are identical. The -name parameter is a required parameter for all of the methods and has the same function in all methods except where noted. Parameters will be introduced only once unless their meaning changes in a particular method, in which case, the differences will be noted. Textfields $query->textfield()will return an HTML textfield form element, as demonstrated in several instances in lines 24-44 in example 1. The textfield() method accepts the following parameters:

print $query->textfield(-name=>`field_name',

                        -default=>`starting value',

                        -size=>50,

                        -maxlength=>80);

name The required name for the field.
default The default starting value for the field contents (optional).
size The size of the field in characters (optional).
maxlength The maximum number of characters the field will accept (optional).
Textareas $query->textarea()will return an HTML textarea form element, as demonstrated in lines 83-85 in Example 1. A textarea is similar to a textfield, but it allows you to specify rows and columns for a multiline text entry box. The starting value for the field can be long and contain multiple lines. The textfield() method accepts the following parameters:

print $query->textarea(-name=>`field_name',

                       -default=>`starting value',

                       -rows=>10,

                       -columns=>50);

-rows The height in number of lines of text the field will occupy.

-columns	The width in number of fixed-width characters the field will occupy.

Standalone Checkboxes $query->checkbox()will return an HTML checkbox form element, as demonstrated in line 88 in Example 1. The checkbox() method is used to create an isolated checkbox that isn't logically related to any other checkboxes. The checkbox() method accepts the following parameters:

print $query->checkbox(-name=>`checkbox_name',

                       -checked=>`checked',

                       -value=>`ON',

                       -label=>`CLICK ME');

-name In addition to being the required name for the checkbox, this value will also be used for the user-readable label printed next to the checkbox.
-checked Specifies that the checkbox is turned on by default (optional).
-value Specifies the value of the checkbox when it is checked. If not provided, the word on is assumed (optional).
-label The user-readable label to be attached to the checkbox. If not provided, the checkbox name is used (optional).

Checkbox Groups $query->checkbox_group() will return an HTML checkbox group form element, as demonstrated in lines 52-56 in Example 1. The checkbox_group() method creates a list of checkboxes that are related by the same name. The checkbox_group()method accepts the following parameters:

print $query->checkbox_group(-name=>`group_name',

                             -values=>[`eenie','meenie','minie','moe'],

                             -default=>[`eenie','moe'],

                             -linebreak=>`true',

                             -labels=>\%labels);

-values An array reference used for the user-readable labels printed next to the checkboxes as well as for the values passed to your script in the query string.
-default Can be either a reference to a list containing the values to be checked by default or can be a single value to be checked. If this argument is missing or undefined, then nothing is selected when the list first appears (optional).
-linebreak Can be set to true to place line breaks between the checkboxes so that they appear as a vertical list. Otherwise, they will be strung together on a horizontal line (optional).
-labels A pointer to an associative array relating the checkbox values to the user-visible labels that will be printed next to them. If not provided, the values will be used as the default (optional).

Radio Groups $query->radio_group() will return a radio button group, as demonstrated in lines 59-63 in Example 1. The radio_group() method creates a set of logically related radio buttons. Unlike the checkbox group, turning one member of the radio group on turns the others off. The only difference in the parameters between the radio_group and the checkbox_group is the -default parameter. The radio_group() method accepts the following parameters:

print $query->radio_group(-name=>`group_name',

  -values=>[`eenie','meenie','minie'],

                          -default=>`meenie',

                          -linebreak=>`true',

                          -labels=>\%labels);

-default The name of the default button to turn on. If not specified, the first item will be the default. You can provide a nonexistent button name such as -default =>`-' to start up with no buttons selected

Popup Menus $query->popup_menu() will return an HTML popup menu form element, as demonstrated in lines 67-70 in Example 1. The popup_menu() method creates an HTML menu the user can select from. The popup_menu() method accepts the following parameters:

print $query->popup_menu(-name=>`menu_name',

                         -values=>[`eenie','meenie','minie'],

                         -default=>`meenie',

                         -labels=>\%labels);

-default The name of the default menu choice. If not specified, the first item will be the default. The values of the previous choice will be maintained across queries.

Scrolling Lists $query->scrolling_list() will return an HTML scrolling list form element, as demonstrated in lines 76-80 in Example 1. The scrolling_list() method creates an HTML scrolling list. The scrolling_list() method accepts the following parameters:

print $query->scrolling_list(-name=>`list_name',

                             -values=>[`eenie','meenie','minie','moe'],

                             -default=>[`eenie','moe'],

                             -size=>5,

                             -multiple=>`true',

                             -labels=>\%labels);

-default Either a reference to a list containing the values to be selected by default or can be a single value to select. If this argument is missing or undefined, then nothing is selected when the list first appears optional).
-size The number of vertical rows to be displayed in the list (optional).
-multiple If set to true, allows multiple simultaneous selections; otherwise only one selection will be allowed at a time.

Hidden Fields $query->hidden() will return an invisible HTML form element, as demonstrated in line 72 in Example 1. The hidden() method produces a text field that can't be seen by the user. It is useful for passing state variable information from one invocation of the script to the next. The hidden() method accepts the following parameters:

print $query->hidden(-name=>`hidden_name',

                     -default=>[`value1','value2'...]);

Password Field $query->password_field() will return a password HTML form element. The password_field() method is identical to textfield(), except that its contents will be starred out on the Web page. The password_field() method accepts the following parameters:

print $query->password_field(-name=>`secret',

                             -value=>`starting value',

                             -size=>50,

                             -maxlength=>80);

Submit/Reset/Default Buttons $query->submit() will return the submit button form element, as demonstrated in lines 90 and 91 in Example 1. Every form should have one of these. The submit() method accepts the following parameters:

print $query->submit(-name=>`button_name',



                     -value=>`value');

$query->reset() will return the reset button form element, as demonstrated in line 89 in Example 1. The reset button restores the form to its values from the last time the script was called, not necessarily to the defaults. Every form should have one of these. The reset() method has no parameters:

print $query->reset

$query->defaults()will return a button that, when pressed, will cause the form to be completely reset to its defaults, wiping out all the changes the user ever made. The defaults() method accepts the following parameters:

print $query->defaults(`button_label')

Retrieving Data from HTML Form Fields When the form is processed, the value of the text field can be retrieved with

$value = $query->param(`field_name');

For fields that may contain multiple values, such as multiple selections from a scrolling list, an array can be returned:

@values = $query->param(`field_name');

If you want to change a field from its initial value after the script has been called once, you can do so like this:

$query->param(`field_name',"I'm the new value of field_name")

Likewise, an array of values could also be stored to a variable.

Example 2: Open a New Browser Window

Ever wonder how you could open a new browser window? This simple example in Listing 5.2 illustrates how CGI.pm can be used to generate a form with a target element that points to a new window. When you click the button on the first page, a new window some_target is opened to display the output in the else clause. The output from Listing 5.2 is shown in Figure 5.4.

Listing 5.2. Open a new browser window

#!/usr/local/bin/perl

use CGI;

$query = new CGI;

print $query->header;

print $query->start_html(`New Window');

if (!$query->param) {

    print $query->startform(-target=>`some_target');

    print $query->submit(`Action','New Browser Window');

    print $query->endform;

} else {

    print "<H1>Here's your new window!</H1>\n";

}

print $query->end_html;

Figure 5.4. The output from Example 2.

Example 3: Upload a File to the Server

Listing 5.3 shows how CGI.pm and CGI::Carp can be used with Netscape 2.0+ browsers to make possible the transfer of files from the browser to the server. CGI.pm is used to set up a multipart form that accepts the data in the file. Then the filefield() returns a file upload field that causes a window on the remote browsers machine to be created, prompting them for a file to transfer. The filefield() has the following parameters:

print $query->filefield(-name=>`uploaded_file',

                        -default=>`starting value',

                        -size=>50,

                        -maxlength=>80);

where -name is the name for the field. The other parameters are optional. The -default parameter specifies the starting value for the file name. The -size parameter is the size of the field in characters. The -maxlength parameter is the maximum number of characters the filefield may contain.

After the file is transferred to the server, the filename entered can be determined by calling param():

$filename = $query->param(`uploaded_file');

where uploaded_file is whatever you called the upload field. The file can be accessed normally or as a filehandle:

# Read a text file and print it out

        while (<$filename>) {

           print;

        }

# Copy a binary file to somewhere safe

        open (OUTFILE,">>/usr/local/web/users/feedback");

        while ($bytesread=read($filename,$buffer,1024)) {

           print OUTFILE $buffer;

        }

Listing 5.3 shows an example of the File Upload field in action, the output of which is shown in Figure 5.5.

Listing 5.3. Upload a file to the server

#!/usr/local/bin/perl

use CGI qw(:standard);

use CGI::Carp;

print header();

print start_html("Example 3: Upload File to Server");

print h1("File Upload Example"),

    `This example demonstrates how to prompt the remote user to

    select a remote file for uploading. `,

    strong("This feature only works with Netscape 2.0 browsers."),

    p,

    `Select the `,cite(`browser'),' button to choose a text file

    to upload.  When you press the submit button, this script

    will count the number of lines, words, and characters in

    the file.';

@types = (`count lines','count words','count characters');

# Start a multipart form.

print start_multipart_form(),

    "Enter the file to process:",

    filefield(`filename','',45),

    br,

    checkbox_group(`count',\@types,\@types),

    p,

    reset,submit(`submit','Process File'),

    endform;

# Process the form if there is a file name entered

if ($file = param(`filename')) {

    $tmpfile=tmpFileName($file);

    print hr(),

          h2($file),

          h3($tmpfile);

    my($lines,$words,$characters,@words) = (0,0,0,0);

    while (<$file>) {

        $lines++;

        $words += @words=split(/\s+/);

        $characters += length($_);

    }

    grep($stats{$_}++,param(`count'));

    if (%stats) {

        print strong("Lines: "),$lines,br if $stats{`count lines'};

        print strong("Words: "),$words,br if $stats{`count words'};

        print strong("Characters: "),$characters,br if $stats{`count characters'};

    } else {

        print strong("No statistics selected.");

    }

}

end_html;

Figure 5.5. Output from Example 3.

Example 4: Using CGI::Carp to Debug Scripts

CGI::Carp contains a very useful feature called fatalsToBrowser. Instead of getting the typical "Server Error" message when something is wrong with your script (as you are writing it), the STDERR is formatted and outputted as HTML to the browser. This prevents you from having to run your script from the shell to find out where bugs are.

Listing 5.4 is an example of script using CGI::Carp qw(fatalsToBrowser) that will fail due to the garbage-line foo bar baz;. Instead of getting a "Server Error," the output looks like Figure 5.6.

Listing 5.4. Using CGI::Carp to debug scripts

#!/usr/local/bin/perl

use CGI::Carp qw(fatalsToBrowser);

# This line invokes a fatal error message at compile time.

foo bar baz;

Figure 5.6. Output from Example 4.

Using the HTTP::Request Module Directly

Obviously, CGI.pm is very powerful. Sometimes simple CGI scripts may not need all of CGI.pm's bells and whistles.

The CGI::Request module is great for CGI scripts that just need access ENV variables. The CGI::Request module loads a lot faster and is less resource intensive than CGI.pm for simple tasks like this.

The GetRequest method in CGI::Request parses the ENV variable and breaks it down into name-value pairs. GetRequest also will remove any dangerous, meta-, or other illegal characters that could cause a security risk on your system.

Insert the following lines at the beginning of your script:

use CGI::Request

GetRequest();

The most frequently used headers can be accessed through the following methods. These methods can be used both to read and to set the value of a header. The header value is set if you pass an argument to the method. The old header value is always returned.

$h->date This header represents the date and time at which the message was originated. Example: $h->date(time); # set current date
$h->expires This header gives the date and time after which the entity should be considered stale.
$h->if_modified_since This header is used to make a request conditional. If the requested resource has not been modified since the time specified in this field, then the server will return a 304 Not Modified response instead of the document itself.
$h->last_modified This header indicates the date and time at which the resource was last modified. Example: # check if document is more than 1 # hour old if ($h->last_modified<time - 60*60) {
$h->content_type The content-type header field indicates the media type of the message content. Example: $h->content_type(`text/html');
$h->content_encoding The content-encoding header field is used as a modifier to the media type. When present, its value indicates what additional encoding mechanism has been applied to the resource.
$h->content_length A decimal number indicating the size in bytes of the message content.
$h->user_agent This header field is used in request messages and contains information about the user agent originating the request. Example: $h->user_agent(`Mozilla/1.2');
$h->server The server header field contains information about the software being used by the origin server program handling the request.
$h->from This header should contain an Internet e-mail address for the human user who controls the requesting user agent. The address should be machine-usable, as defined by RFC822. Example: $h->from(`Gisle Aas <aas@sn.no>`);
$h->referer Used to specify the address (URI) of the document from which the requested resource address was obtained.
$h->uri This header field may contain one or more URIs by which the resource origin of the entity can be identified.
$h->www_authenticate This header must be included as part of a 401 Unauthorized response. The field value consists of a challenge that indicates the authentication scheme and parameters applicable to the requested URI.
$h->authorization A user agent that wishes to authenticate itself with a server may do so by including this header.
$h->authorization_basic This method lets you get/set an authorization header that uses the "Basic Authentication Scheme." It will return a list of two values. The first is the username and the second the password. It also expects two arguments when it is used to set the header value. Example: $h->authorization_basic(`user', `passwd');

Summary

Programming CGI applications for the WWW requires a fundamental understanding of the underlying mechanics of HTTP transactions. The communication which occurs between the Web browser and the server on which your CGI program is being run is fully accessible to your CGI program. Understanding this communication, or transaction, allows you to write dynamic, powerful, and efficient CGI programs. Understanding tools such as CGI.pm and the libwww modules make it easier than ever to harness this power in your programs.