WebReference.com - Chapter 4 from The mod_perl Developer's Cookbook, from Sams Publishing (5/6) | WebReference

WebReference.com - Chapter 4 from The mod_perl Developer's Cookbook, from Sams Publishing (5/6)

To page 1To page 2To page 3To page 4current pageTo page 6
[previous] [next]

mod_perl Developer's Cookbook

4.8. Remote IP Addresses and Hosts

You want to find out the hostname and/or the IP address of the current connection.

Technique

Use the request method get_remote_host() from the Apache class.

use Apache::Constants qw(:remotehost);
use strict;
sub handler {
 my $r = shift;
 my $host_or_ip = $r->get_remote_host;
 my $host = $r->get_remote_host(REMOTE_HOST);
 my $paranoid_host = $r->get_remote_host(REMOTE_DOUBLE_REV);
 # Continue along...
}

Comments

Often we want to know the hostname and/or the IP address of the remote client host. In the mod_perl environment this is provided by the get_remote_host() method from the Apache class and several Apache::Connection methods, as described in the previous recipe.

The get_remote_host() method returns the hostname that Apache will store in its log files. This is either a dotted quad IP address like 10.3.4.200, or a hostname like proxy.example.com. The type of information returned is determined by Apache's HostnameLookups directive. The default Apache configuration sets HostnameLookups to Off. Skipping the hostname lookup speeds up our server and reduces the Internet's total network traffic. So, unless you explicitly set HostnameLookups to On, calls to get_remote_host() will return IP addresses.

Luckily, some optional arguments to the get_remote_host() method provide a convenient way to look up and verify the hostname of the client. Specifying the REMOTE_HOST constant instructs the Apache server to do an address-to-hostname translation, resulting in the hostname of the connected client.

If you intend to use a hostname for any type of access control, you should instead pass REMOTE_DOUBLE_REV to the get_remote_host() method. This argument runs code that does a double-reverse DNS lookup: The normal address-to-hostname mapping is done, followed by a hostname-to-address mapping. If the two mappings match, then the hostname is valid; if they do not, get_remote_host() returns undef.

4.9. Detecting a Broken Connection

You have lots of data to send to the client and don't want to waste CPU cycles if the user prematurely ended the connection.

Technique

Use the aborted() method from the Apache::Connection class to detect the broken connection.

while (my $line = ) {
 # No need to waste cycles printing to nobody.
 last if $r->connection->aborted;
 print $line;
}

Comments

If you have your LogLevel set to info or below, every once in a while, you may see an information message similar to the following in your error log:

Broken pipe: client stopped connection before rflush completed

This represents the typical "user pressed stop" case—the end user got tired of waiting around and clicked the big red Stop button on the browser. Handling this case specifically used to be an issue with older versions of Apache, but recent versions handle it internally. For mod_perl users, the result of a broken connection is that all print() operations (and similar methods that write data to the client) become no-ops—no data is written to the socket once Apache detects that the connection has been terminated.

The aborted() method is provided by the Apache::Connection class as a way of letting you know that there is no longer an open connection to the client so you can take appropriate action. One problem with this approach is that it is dependent on how much data you send to the client—aborted() will only return true after Apache flushes its print buffers. If you have a long running process that only sends out a bit of data at a time, or you aren't printing at all, it may be a while before you can detect a client disconnect using aborted().

For more granular control, you can take advantage of the fileno() method, also provided by the Apache::Connection class, as in the following package:

package Cookbook::CheckConnection;
use IO::Select;
use strict;
sub client_connected {
 my $c = Apache->request->connection;
 # First, check to see whether Apache tripped the aborted flag.
 return if $c->aborted;
 # Now for the real test.
 # Check to see if we can read from the output file descriptor.
 my $s = IO::Select->new($c->fileno);
 return if $s->can_read(0);
 # Looks like the client is still there...
 return 1;
}
1;

The Cookbook::CheckConnection package offers the client_connected() utility function, which returns true if the client is still on the wire. After checking the status of the connection using aborted() (which has less overhead), the function checks the status of the output file descriptor using $r->connection->fileno() and the IO::Select module.

When called with no arguments, fileno() will return the output file descriptor, which can be used to check the status of the connection. Although calling $s->can_read() to check for a broken connection may seem counterintuitive, when the client aborts the connection Apache will populate the output file descriptor with a zero-byte packet available for reading.

You might use this new utility function from within a long-running or expensive process, such as when iterating through a fairly time-consuming database query, where the overhead of checking the client connection is worth the effort.

# Find shops in the vicinity of a given zipcode. We already
# have the latitude and longitude of the starting zip,
# as well as the search radius.
my $sql = qq(
 select shop.name, shops.address1, shops.city, shops.state,
     69*SQRT( POWER( (? - zips.latitude),2 ) +
     POWER( (? - zips.longitude),2 ) ) as distance
   from zipcodes zips, shops shops
   where
    69*SQRT( POWER( (? - zips.latitude),2 ) +
    POWER( (? - zips.longitude),2 ) )
    <= ?
   and substr(shops.zip,0,5)=zips.zipcode
   order by distance
);
$sth = $dbh->prepare($sql);
# No sense executing this SQL nightmare if the client aborted.
return OK unless Cookbook::CheckConnection::client_connected();
$sth->execute($lat, $long, $lat, $long, $radius);
my ($name, $address, $city, $state, $distance);
$sth->bind_columns(\($name, $address, $city, $state, $distance));
while ($sth->fetch) {
 # Do something with the results...
}

To page 1To page 2To page 3To page 4current pageTo page 6
[previous] [next]

Copyright © Pearson Education and
Created: March 18, 2002
Revised: March 18, 2002


URL: http://webreference.com/programming/perl/cookbook/chap4/5.html