WebReference.com - Chapter 4 from The mod_perl Developer's Cookbook, from Sams Publishing (5/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
You want to find out the hostname and/or the IP address of the current connection.
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...
}
[previous] [next] |
Copyright © Pearson Education and
Created: March 18, 2002
Revised: March 18, 2002
URL: https://webreference.com/programming/perl/cookbook/chap4/5.html