Recipe 17.2. Using Apache to Run Web Scripts


Problem

You want to run Perl, Ruby, PHP, or Python programs in a web environment.

Solution

Execute them using the Apache server.

Discussion

This recipe describes how to configure Apache for running Perl, Ruby, PHP, and Python scripts. It also illustrates how to write web-based scripts in each language.

There are typically several directories under the Apache root directory. Here I'll assume that directory to be /usr/local/apache, although it may be different on your system. For example, on Windows, you might find Apache under C:\Program Files. The directories under the Apache root include bin (which contains httpdthat is, Apache itselfand other Apache-related executable programs), conf (for configuration files, notably httpd.conf, the primary file used by Apache), htdocs (the root of the document tree), and logs (for logfiles). The layout might differ on your system. For example, you might find the configuration files in /etc/httpd and the logs under /var/log/httpd. Modify the following instructions accordingly.

To configure Apache for script execution, edit the httpd.conf file in the conf directory. Typically, executable scripts are identified either by location or by filename suffix. A location can be either language-neutral or language-specific.

Apache configurations often have a cgi-bin directory under the Apache root directory in which you can install scripts that should run as external programs. It's configured using a ScriptAlias directive:

ScriptAlias /cgi-bin/ /usr/local/apache/cgi-bin/ 

The second argument is the actual location of the script directory in your filesystem, and the first is the pathname in URLs that corresponds to that directory. Thus, the directive just shown associates scripts located in /usr/local/apache/cgi-bin with URLs that have cgi-bin following the hostname. For example, if you install the Ruby script myscript.rb in the directory /usr/local/apache/cgi-bin on the host localhost, you'd request it with this URL:

http://localhost/cgi-bin/myscript.rb 

When configured this way, the cgi-bin directory can contain scripts written in any language. Because of this, the directory is language-neutral, so Apache needs to be able to figure out which language processor to use to execute each script that is installed there. To provide this information, the first line of the script should begin with #! followed by the pathname to the program that executes the script. For example, a script that begins with the following line will be run by Perl:

#!/usr/bin/perl 

Under Unix, you must also make the script executable (use chmod +x), or it won't run properly. The #! line just shown is appropriate for a system that has Perl installed as /usr/bin/perl. If your Perl interpreter is installed somewhere else, modify the line accordingly. If you're on a Windows machine with Perl installed as C:\Perl\bin\perl.exe, the #! line should look like this:

#!C:\Perl\bin\perl 

For Windows, another option that is simpler is to set up a filename extension association between script names that end with a .pl suffix and the Perl interpreter. Then the script can begin like this:

#!perl 

Directories used only for scripts generally are placed outside of your Apache document tree. As an alternative to using specific directories for scripts, you can identify scripts by filename extension, so that files with a particular suffix become associated with a specific language processor. In this case, you can place them anywhere in the document tree. This is the most common way to use PHP. For example, if you have Apache configured with PHP support built in using the mod_php module, you can tell it that scripts having names ending with .php should be interpreted as PHP scripts. To do so, add this line to httpd.conf:

AddType application/x-httpd-php .php 

You may also have to add (or uncomment) a LoadModule directive for php.

With PHP enabled, you can install a PHP script myscript.php under htdocs (the Apache document root directory). The URL for invoking the script becomes:

http://localhost/myscript.php 

If PHP runs as an external standalone program, you'll need to tell Apache where to find it. For example, if you're running Windows and you have PHP installed as C:\Php\php.exe, put the following lines in httpd.conf (note the use of forward slashes in the pathnames rather than backslashes):

ScriptAlias /php/ "C:/Php/" AddType application/x-httpd-php .php Action application/x-httpd-php /php/php.exe 

For purposes of showing URLs in examples, I'm going to assume that Perl, Ruby, and Python scripts are in your cgi-bin directory, and that PHP scripts are in the mcb directory of your document tree, identified by the .php extension. That means the URLs for scripts in these languages will look like this:

http://localhost/cgi-bin/myscript.pl http://localhost/cgi-bin/myscript.rb http://localhost/cgi-bin/myscript.py http://localhost/mcb/myscript.php 

If you plan to use a similar setup, make sure that you have a cgi-bin directory that Apache knows about, and an mcb directory under your Apache document root. Then, to deploy Perl, Ruby, or Python web scripts, install them in the cgi-bin directory. To deploy PHP scripts, install them in the mcb directory.

Some of the scripts use modules or library files that are specific to this book. If you have these files installed in a library directory that your language processors search by default, they should be found automatically. Otherwise, you'll need to indicate where the files are located. An easy way to do this is by using SetEnv directives in your httpd.conf file to set environment variables that can be seen by your scripts when Apache invokes them. (Use of the SetEnv directive requires that the mod_env Apache module be enabled.) For example, if you install library files in /usr/local/lib/mcb, the following directives enable Perl, Ruby, and Python scripts to find them:

SetEnv PERL5LIB /usr/local/lib/mcb SetEnv RUBYLIB /usr/local/lib/mcb SetEnv PYTHONPATH /usr/local/lib/mcb 

For PHP, add /usr/local/lib/mcb to the value of include_path in your php.ini configuration file.

For background information on library-related environment variables and the php.ini file, see Section 2.3.

After Apache has been configured to support script execution, restart it. Then you can begin to write scripts that generate web pages. The remainder of this section describes how to do so for Perl, Ruby, PHP, and Python. The examples for each language connect to the MySQL server, run a simple query, and display the results in a web page. The scripts shown here indicate whether there are any additional modules or libraries that web scripts typically need to include. (Later on, I'll generally assume that the proper modules have been included and will show only script fragments.)

Before we proceed further, I should mention a couple of debugging tips:

  • If you request a web script and get an error page in response, have a look at the Apache error log, which is a useful source of diagnostic information when you're trying to figure out why a script doesn't work. A common name for this log is error_log in the logs directory. If you don't find any such file, check httpd.conf for an ErrorLog directive to see where Apache logs its errors.

  • Sometimes it's helpful to directly examine the output that a web script generates. You can do this by invoking the script from the command line. You'll see the HTML produced by the script, as well as any error messages that it might print. Some web modules expect to see a parameter string, and might even prompt you for one when you invoke the script at the command line. When this is the case, you might be able to supply the parameters as an argument on the command line to avoid the prompt. For example, the Ruby cgi module expects to see parameters, and will prompt you for them if they are missing:

    % myscript.rb (offline mode: enter name=value pairs on standard input) 

    At the prompt, enter the parameter values and then enter Ctrl-D (EOF). To avoid the prompt, supply the parameters on the command line:

    % myscript.rb "param1=val1;param2=val2;param3=val3"                   

    To specify "no parameters" explicitly, provide an empty argument:

    % myscript.rb ""                   

Web Security Note

Under Unix, scripts are associated with particular user and group IDs when they execute. Scripts that you execute from the command line run with your own user and group IDs, and have the filesystem privileges associated with your account. Scripts executed by a web server don't run with your user and group ID, nor will they have your privileges. Instead, they run under the user and group ID of the account the web server has been set to run as, and with that account's privileges. (To determine what account this is, look for User and Group directives in the httpd.conf configuration file.) This means that if you expect web scripts to read and write files, those files must be accessible to the account used to run the web server. For example, if your server runs under the nobody account and you want a script to be able to store uploaded image files into a directory called uploads in the document tree, you must make that directory readable and writable by the nobody user.

Another implication is that if other people can write scripts to be executed by your web server, those scripts too will run as nobody and they can read and write the same files as your own scripts. That is, files used by your scripts cannot be considered private only to your scripts. A solution to this problem is to use the Apache suEXEC mechanism. (If you are using an ISP for web hosting, you might find that suEXEC is already enabled.)


Perl

The following listing shows our first web-based Perl script, show_tables.pl. It produces an appropriate Content-Type: header, a blank line to separate the header from the page content, and the initial part of the page. Then it retrieves and displays a list of tables in the cookbook database. The table list is followed by the trailing HTML tags that close the page:

#!/usr/bin/perl # show_tables.pl - Display names of tables in cookbook database # by generating HTML directly use strict; use warnings; use Cookbook; # Print header, blank line, and initial part of page print <<EOF; Content-Type: text/html <html> <head> <title>Tables in cookbook Database</title> </head> <body bgcolor="white"> <p>Tables in cookbook database:</p> EOF # Connect to database, display table list, disconnect my $dbh = Cookbook::connect (); my $stmt = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES             WHERE TABLE_SCHEMA = 'cookbook' ORDER BY TABLE_NAME"; my $sth = $dbh->prepare ($stmt); $sth->execute (); while (my @row = $sth->fetchrow_array ()) {   print "$row[0]<br />\n"; } $dbh->disconnect (); # Print page trailer print <<EOF; </body> </html> EOF 

To try the script, install it in your cgi-bin directory and request it from your browser as follows:

http://localhost/cgi-bin/show_tables.pl 

show_tables.pl generates the Content-Type: header explicitly and it produces HTML elements by including literal tags in print statements. Another approach to web page generation is to use the CGI.pm module, which makes it easy to write web scripts without writing literal HTML tags. CGI.pm provides an object-oriented interface and a function call interface, so you can use it to write web pages in either of two styles. Here's a script, show_tables_oo.pl, that uses the CGI.pm object-oriented interface to produce the same report as show_tables.pl:

#!/usr/bin/perl # show_tables_oo.pl - Display names of tables in cookbook database # using the CGI.pm object-oriented interface use strict; use warnings; use CGI; use Cookbook; # Create CGI object for accessing CGI.pm methods my $cgi = new CGI; # Print header, blank line, and initial part of page print $cgi->header (); print $cgi->start_html (-title => "Tables in cookbook Database",                         -bgcolor => "white"); print $cgi->p ("Tables in cookbook database:"); # Connect to database, display table list, disconnect my $dbh = Cookbook::connect (); my $stmt = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES             WHERE TABLE_SCHEMA = 'cookbook' ORDER BY TABLE_NAME"; my $sth = $dbh->prepare ($stmt); $sth->execute (); while (my @row = $sth->fetchrow_array ()) {   print $row[0], $cgi->br (); } $dbh->disconnect (); # Print page trailer print $cgi->end_html (); 

The script includes the CGI.pm module with a use CGI statement, and then creates a CGI object, $cgi, through which it invokes the various HTML-generation calls. header⁠(⁠ ⁠ ⁠) generates the Content-Type: header and start_html⁠(⁠ ⁠ ⁠) produces the initial page tags up through the opening <body> tag. After generating the first part of the page, show_tables_oo.pl retrieves and displays information from the server. Each table name is followed by a <br /> tag, produced by invoking the br⁠(⁠ ⁠ ⁠) method. end_html⁠(⁠ ⁠ ⁠) produces the closing </body> and </html> tags. When you install the script in your cgi-bin directory and invoke it from a browser, you can see that it generates the same type of page as show_tables.pl.

CGI.pm calls often take multiple parameters, many of which are optional. To enable you to specify just those parameters you need, CGI.pm understands -name => value notation in parameter lists. For example, in the start_html⁠(⁠ ⁠ ⁠) call, the title parameter sets the page title and bgcolor sets the background color. The -name => value notation also allows parameters to be specified in any order, so these two statements are equivalent:

print $cgi->start_html (-title => "My Page Title", -bgcolor => "white"); print $cgi->start_html (-bgcolor => "white", -title => "My Page Title"); 

To use the CGI.pm function call interface rather than the object-oriented interface, you must write scripts a little differently. The use line that references CGI.pm should import the method names into your script's namespace so that you can invoke them directly as functions without having to create a CGI object. For example, to import the most commonly used methods, the script should include this statement:

use CGI qw(:standard); 

The following script, show_tables_fc.pl, is the function call equivalent of the show_tables_oo.pl script just shown. It uses the same CGI.pm calls, but invokes them as standalone functions rather than through a $cgi object:

#!/usr/bin/perl # show_tables_fc.pl - Display names of tables in cookbook database # using the CGI.pm function-call interface use strict; use warnings; use CGI qw(:standard);  # import standard method names into script namespace use Cookbook; # Print header, blank line, and initial part of page print header (); print start_html (-title => "Tables in cookbook Database",                   -bgcolor => "white"); print p ("Tables in cookbook database:"); # Connect to database, display table list, disconnect my $dbh = Cookbook::connect (); my $stmt = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES             WHERE TABLE_SCHEMA = 'cookbook' ORDER BY TABLE_NAME"; my $sth = $dbh->prepare ($stmt); $sth->execute (); while (my @row = $sth->fetchrow_array ()) {   print $row[0], br (); } $dbh->disconnect (); # Print page trailer print end_html (); 

Install the show_tables_fc.pl script in your cgi-bin directory and request it from your browser to verify that it produces the same output as show_tables_oo.pl.

This book uses the CGI.pm function call interface for Perl-based web scripts from this point on. You can get more information about CGI.pm at the command line by using the following commands to read the installed documentation:

% perldoc CGI % perldoc CGI::Carp                

Appendix D lists other sources of information for this module, both online and in print form.

Ruby

The Ruby cgi module provides an interface to HTML-generating methods. To use it, create a CGI object and invoke its methods to produce HTML page elements. Methods are named after the HTML elements to which they correspond. Their invocation syntax follows these principles:

  • If an element should have attributes, pass them as arguments to the method.

  • If the element has body content, specify the content in a code block associated with the method call.

For example, the following method call produces a <P> element that includes an align attribute and content of "This is a sentence":

cgi.p("align" => "left") { "This is a sentence" } 

The output looks like this:

<P align="left">This is a sentence.</P> 

To display generated HTML content, pass it in a code block to the cgi.out method. The following Ruby script, show_tables.rb, retrieves a list of tables in the cookbook database and displays them in an HTML document:

#!/usr/bin/ruby -w # show_tables.rb - Display names of tables in cookbook database require "cgi" require "Cookbook" # Connect to database, display table list, disconnect dbh = Cookbook.connect stmt = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES         WHERE TABLE_SCHEMA = 'cookbook' ORDER BY TABLE_NAME" rows = dbh.select_all(stmt) dbh.disconnect cgi = CGI.new("html4") cgi.out {   cgi.html {     cgi.head {       cgi.title { "Tables in cookbook Database" }     } +     cgi.body("bgcolor" => "white") {       cgi.p { "Tables in cookbook Database:" } +       rows.collect { |row| row[0] + cgi.br }.join     }   } } 

The collect method iterates through the row array containing the table names and produces a new array containing each name with a <br> appended to it. The join method concatenates the strings in the resulting array.

The script includes no explicit code for producing the Content-Type: header because cgi.out generates one.

Install the script in your cgi-bin directory and request it from your browser as follows:

http://localhost/cgi-bin/show_tables.rb 

If you invoke Ruby web scripts from the command line so that you examine the generated HTML, you'll see that the HTML is all on one line and is difficult to read. To make the output easier to understand, process it through the CGI.pretty utility method, which adds line breaks and indentation. Suppose that your page output call looks like this:

cgi.out {   page content here } 

To change the call to use CGI.pretty, write it like this:

cgi.out {   CGI.pretty(page content here) } 

PHP

PHP doesn't provide much in the way of tag shortcuts, which is surprising given that language's web orientation. On the other hand, because PHP is an embedded language, you can simply write your HTML literally in your script without using print statements. Here's a show_tables.php script that shifts back and forth between HTML mode and PHP mode:

<?php # show_tables.php - Display names of tables in cookbook database require_once "Cookbook.php"; ?> <html> <head> <title>Tables in cookbook Database</title> </head> <body bgcolor="white"> <p>Tables in cookbook database:</p> <?php # Connect to database, display table list, disconnect $conn =& Cookbook::connect (); $stmt = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES          WHERE TABLE_SCHEMA = 'cookbook' ORDER BY TABLE_NAME"; $result =& $conn->query ($stmt); while (list ($tbl_name) = $result->fetchRow ())   print ($tbl_name . "<br />\n"); $result->free (); $conn->disconnect (); ?> </body> </html> 

To try the script, put it in the mcb directory of your web server's document tree and request it from your browser as follows:

http://localhost/mcb/show_tables.php 

The PHP script includes no code to produce the Content-Type: header because PHP produces one automatically. (If you want to override this behavior and produce your own headers, consult the header⁠(⁠ ⁠ ⁠) function section in the PHP manual.)

Except for the break tags, show_tables.php includes HTML content by writing it outside of the <?php and ?> tags so that the PHP interpreter simply writes it without interpretation. Here's a different version of the script that produces all the HTML using print statements:

<?php # show_tables_print.php - Display names of tables in cookbook database # using print() to generate all HTML require_once "Cookbook.php"; print ("<html>\n"); print ("<head>\n"); print ("<title>Tables in cookbook Database</title>\n"); print ("</head>\n"); print ("<body bgcolor=\"white\">\n"); print ("<p>Tables in cookbook database:</p>\n"); # Connect to database, display table list, disconnect $conn =& Cookbook::connect (); $stmt = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES          WHERE TABLE_SCHEMA = 'cookbook' ORDER BY TABLE_NAME"; $result =& $conn->query ($stmt); while (list ($tbl_name) = $result->fetchRow ())   print ($tbl_name . "<br />\n"); $result->free (); $conn->disconnect (); print ("</body>\n"); print ("</html>\n"); ?> 

Sometimes it makes sense to use one approach, sometimes the otherand sometimes both within the same script. If a section of HTML doesn't refer to any variable or expression values, it can be clearer to write it in HTML mode. Otherwise it may be clearer to write it using print or echo statements, to avoid switching between HTML and PHP modes frequently.

Python

A standard installation of Python includes cgi and urllib modules that are useful for web programming. However, we don't actually need them yet, because the only web-related activity of our first Python web script is to generate some simple HTML. Here's a Python version of the MySQL table-display script:

#!/usr/bin/python # show_tables.py - Display names of tables in cookbook database import MySQLdb import Cookbook # Print header, blank line, and initial part of page print """Content-Type: text/html <html> <head> <title>Tables in cookbook Database</title> </head> <body bgcolor="white"> <p>Tables in cookbook database:</p> """ # Connect to database, display table list, disconnect conn = Cookbook.connect () cursor = conn.cursor () stmt = """        SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES        WHERE TABLE_SCHEMA = 'cookbook' ORDER BY TABLE_NAME        """ cursor.execute (stmt) for (tbl_name, ) in cursor.fetchall ():   print tbl_name + "<br />" cursor.close () conn.close () # Print page trailer print """ </body> </html> """ 

Put the script in Apache's cgi-bin directory and request it from your browser like this:

http://localhost/cgi-bin/show_tables.py 




MySQL Cookbook
MySQL Cookbook
ISBN: 059652708X
EAN: 2147483647
Year: 2004
Pages: 375
Authors: Paul DuBois

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net