URI-AnyService
view release on metacpan or search on metacpan
lib/URI/AnyService.pm view on Meta::CPAN
#pod to your specific protocol.
#pod
#pod This allows you to use a URL like C<smtp://example.com> without having to set a default port in
#pod the URL.
#pod
#pod =head1 GLOBAL VARIABLES
#pod
#pod =head2 %SERVICE_PORTS
#pod
#pod This is a hash of schemes/protocols to port numbers. It is loaded from the C</etc/services> file,
#pod as soon as you C<use> this module.
#pod
#pod Most of the time, you shouldn't mess with this. But, if there is some new protocol that you need
#pod to add that happens to not exist in your C</etc/services> files, you can do so with:
#pod
#pod $URI::AnyService::SERVICE_PORTS{'my-protocol'} = 1234;
#pod
#pod This variable is not exportable, so you need to use the full package syntax.
#pod
#pod =cut
our %SERVICE_PORTS;
#pod =head1 USE OPTIONS
#pod
#pod =head2 :InternalServicesData
#pod
#pod use URI::AnyService ':InternalServicesData';
#pod
#pod This will force the internal services data to be loaded, even if the C</etc/services> file is
#pod available. This is useful for testing, or if you know that the C</etc/services> files on your
#pod servers are not consistent.
#pod
#pod Please note that there are no guarantees that the internal data is up-to-date or accurate. It's
#pod mostly used as a "last resort" fallback.
#pod
#pod =cut
my $found_services_in;
my $force_internal_services_data = 0;
sub _load_services {
my $fh;
# XXX: Windows support not tested. This is just the usual location where it's expected to be.
my $services_file = $^O eq 'MSWin32' ? 'C:\Windows\System32\drivers\etc\services' : '/etc/services';
if (!$force_internal_services_data && -f -r $services_file) {
open $fh, '<', $services_file or Carp::croak("Can't open /etc/services: $!");
$found_services_in = $services_file;
}
else {
$fh = \*DATA;
$found_services_in = '<DATA>';
}
while (my $line = <$fh>) {
next if $line =~ /^#/;
$line =~ s/#.*$//;
next unless ($line // '') =~ /^$URI::scheme_re\s+/;
my ($service, $port_proto, @other_services) = split /\s+/, $line;
my ($port, $proto) = split m!/!, $port_proto;
$SERVICE_PORTS{$service} = $port;
$SERVICE_PORTS{@other_services} = $port if @other_services;
}
close $fh;
}
sub import {
my ($class, @opts) = @_;
$force_internal_services_data = 1 if grep { $_ eq ':InternalServicesData' } @opts;
_load_services();
}
#pod =head1 CONSTRUCTOR
#pod
#pod URI::AnyService->new($str);
#pod URI::AnyService->new($str, $scheme);
#pod
#pod This constructor is very similiar to L<URI's version|URI/CONSTRUCTORS>, including the
#pod somewhat-legacy two parameter form. In most cases, you should just use the whole URL string.
#pod
#pod A scheme that was defined in the services file is required.
#pod
#pod =cut
# Less /o bugs, more modern usage, still using the URI scheme RE
my $scheme_re = qr/^($URI::scheme_re):/;
my $scheme_re_sans_colon = qr/^($URI::scheme_re)/;
### XXX: Most of this code for `new` and `_scheme` were copied from URI, but irrelevant parts
### have been removed and optimized.
sub new {
my ($class, $uri, $scheme) = @_;
$uri = defined $uri ? "$uri" : ""; # stringify
# Get rid of potential wrapping
$uri =~ s/^<(?:URL:)?(.*)>$/$1/;
$uri =~ s/^"(.*)"$/$1/;
$uri =~ s/^\s+//;
$uri =~ s/\s+$//;
# We ARE the implementor class
$scheme = $1 if $uri =~ s/$scheme_re//;
Carp::croak("No scheme defined in URI: $uri") unless $scheme;
Carp::croak("Scheme '$scheme' not found in $found_services_in") unless $SERVICE_PORTS{lc $scheme};
# Find all funny characters and encode the bytes
$uri = URI->_uric_escape($uri);
$uri = "$scheme:$uri";
my $self = bless \$uri, $class;
$self;
}
sub _no_scheme_ok { 0 }
sub _scheme {
my $self = shift;
unless (@_) {
( run in 1.080 second using v1.01-cache-2.11-cpan-71847e10f99 )