Search code examples
perllwp-useragent

How to fail over


If I use wget to retrieve something from the geonames.org server, it reports two IP addresses, and the first one fails but it gets it from the second:

Resolving ws.geonames.org (ws.geonames.org)... 5.9.41.208, 176.9.107.169
Connecting to ws.geonames.org (ws.geonames.org)|5.9.41.208|:80... failed: Connection refused.
Connecting to ws.geonames.org (ws.geonames.org)|176.9.107.169|:80... connected.
HTTP request sent, awaiting response... 200 OK

But unfortunately I have to access it through perl using LWP::UserAgent and HTTP::Request. How can I make them try the second IP if the first fails?

    my $ua = LWP::UserAgent->new;
my $req = HTTP::Request->new(
    GET =>
      "http://ws.geonames.org/countrySubdivision?lat=$lat&lng=$long&radius=$radius&username=xyzzy");

my $res = $ua->request($req);

Solution

  • You can do it yourself: get all the IP addresses with the help of Net::DNS::Resolver, and then try all IP addresses until you get a successful response. Note that you have to supply the "Host" header yourself if working with an IP address, in case the server is doing name-based virtual hosts.

    Something like the following lines could work. Maybe there's even a CPAN module for this, I did not check:

    use Net::DNS;
    use LWP::UserAgent;
    
    my @addrs;
    {
        my $res   = Net::DNS::Resolver->new;
        my $query = $res->search("ws.geonames.org");
        if ($query) {
            for my $rr ($query->answer) {
                if ($rr->type eq "A") {
                    push @addrs, $rr->address;
                }
            }
        } else {
            die "DNS query failed: ", $res->errorstring, "\n";
        }
    }
    
    my $ua = LWP::UserAgent->new;
    
    my $res;
    for my $addr (@addrs) {
        $res = $ua->get("http://$addr/countrySubdivision?lat=$lat&lng=$long&radius=$radius&username=xyzzy", Host => 'ws.geonames.org');
        last if $res->is_success;
    }