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 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.
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 AddressNetsite
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.
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.
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 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.
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 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
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 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.
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:
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.
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 |
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.
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. |
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.
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 :
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
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.
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. |
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.
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 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. |
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.
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.
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.
The line numbers in Listing 5.1 are for reference only and are not to be typed in.
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). |
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.
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.
#!/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.
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.
#!/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.
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.
#!/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.
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'); |
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.