From a9105c79d3b9021e0846e8c7956c5be68c36eb08 Mon Sep 17 00:00:00 2001 From: Silvano Cerza Date: Fri, 16 Jan 2026 16:00:53 +0100 Subject: [PATCH] Add resproxy support --- Geo-IPinfo/lib/Geo/IPinfo.pm | 60 ++++++++++++++++++++++++++++++++ Geo-IPinfo/t/05-usage-resproxy.t | 36 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 Geo-IPinfo/t/05-usage-resproxy.t diff --git a/Geo-IPinfo/lib/Geo/IPinfo.pm b/Geo-IPinfo/lib/Geo/IPinfo.pm index aaa2e2d..2ace4f7 100644 --- a/Geo-IPinfo/lib/Geo/IPinfo.pm +++ b/Geo-IPinfo/lib/Geo/IPinfo.pm @@ -1136,6 +1136,66 @@ sub geo { #------------------------------------------------------------------------------- +sub resproxy { + my ( $self, $ip ) = @_; + + $ip = defined $ip ? $ip : ''; + + if ( $ip eq '' ) { + $self->{message} = 'IP address is required for resproxy lookup'; + return undef; + } + + my $validated_ip = Net::CIDR::cidrvalidate($ip); + if ( !defined $validated_ip ) { + $self->{message} = 'Invalid IP address'; + return undef; + } + + my $cache_key = 'resproxy/' . $ip; + my $cached_info = $self->_lookup_info_from_cache($cache_key); + + if ( defined $cached_info ) { + $self->{message} = ''; + return $cached_info; + } + + my $url = $self->{base_url} . 'resproxy/' . $ip; + my $response = $self->{ua}->get($url); + + if ( $response->is_success ) { + my $content_type = $response->header('Content-Type') || ''; + my $info; + + if ( $content_type =~ m{application/json}i ) { + eval { $info = from_json( $response->decoded_content ); }; + if ($@) { + $self->{message} = 'Error parsing JSON response.'; + return undef; + } + } + else { + $info = $response->decoded_content; + chomp($info); + } + + $info->{meta}->{time} = time(); + $self->{cache}->set( $cache_key, $info ); + $self->{message} = ''; + return $info; + } + + if ( $response->code == HTTP_TOO_MANY_REQUEST ) { + $self->{message} = 'Your monthly request quota has been exceeded.'; + return undef; + } + + $self->{message} = $response->status_line; + return undef; +} + +#------------------------------------------------------------------------------- + sub field { my ( $self, $ip, $field ) = @_; diff --git a/Geo-IPinfo/t/05-usage-resproxy.t b/Geo-IPinfo/t/05-usage-resproxy.t new file mode 100644 index 0000000..e60a2c0 --- /dev/null +++ b/Geo-IPinfo/t/05-usage-resproxy.t @@ -0,0 +1,36 @@ +#!perl -T + +use strict; +use warnings; +use Test::More; + +if ( $ENV{RELEASE_TESTING} ) { + plan tests => 10; +} +else { + plan( skip_all => "Basic usage tests not required for installation" ); +} + +use_ok('Geo::IPinfo'); + +my $ip; + +$ip = Geo::IPinfo->new( $ENV{IPINFO_TOKEN} ); +isa_ok( $ip, "Geo::IPinfo", '$ip' ); + +# Test resproxy with known residential proxy IP +my $resproxy = $ip->resproxy("175.107.211.204"); +ok( $resproxy, "resproxy() returns data for known residential proxy IP" ); +is( $resproxy->{ip}, "175.107.211.204", "IP field is correct" ); +ok( defined $resproxy->{last_seen}, "last_seen field is defined" ); +ok( defined $resproxy->{percent_days_seen}, "percent_days_seen field is defined" ); +ok( defined $resproxy->{service}, "service field is defined" ); + +# Test resproxy with IP that returns empty response +my $empty_resproxy = $ip->resproxy("8.8.8.8"); +ok( $empty_resproxy, "resproxy() returns data for 8.8.8.8" ); +ok( !exists $empty_resproxy->{ip}, "ip field does not exist for 8.8.8.8" ); + +# Test resproxy with invalid IP +is( $ip->resproxy("1000.1000.1.1"), + undef, "resproxy() returns undef for invalid IP" );