POE::C...solver(3pm)

πŸ“¦
version 0.921 Β· 5 versions
, in debian bookworm, ubuntu jammy, ubuntu noble

πŸ’– POE::Component::Resolver πŸ’–

okay bestie, gather round, because we're talking about POE::Component::Resolver β€” that's the POE::Component::Resolver module from the User Contributed Perl Documentation, version 0.921, and it's literally the thing that saves your whole program from dying while DNS does its lil thing. no cap. πŸ¦‹


✨ NAME ✨

POE::Component::Resolver β€” a non-blocking getaddrinfo() resolver. it's giving "I do the waiting so you don't have to." πŸ‘‘


🌸 VERSION 🌸

version 0.921 πŸ’…


πŸŽ€ SYNOPSIS πŸŽ€

okay so here's the whole vibe, verbatim, don't touch a single character:

#!/usr/bin/perl

use warnings;
use strict;

use POE;
use POE::Component::Resolver qw(AF_INET AF_INET6);

my $r = POE::Component::Resolver->new(
        max_resolvers => 8,
        idle_timeout  => 5,
        af_order      => [ AF_INET6, AF_INET ],
        # sidecar_program => $path_to_program,
);

my @hosts = qw( ipv6-test.com );
my $tcp   = getprotobyname("tcp");

POE::Session->create(
        inline_states => {
                _start => sub {
                        foreach my $host (@hosts) {
                                $r->resolve(
                                        host    => $host,
                                        service => "http",
                                        event   => "got_response",
                                        hints   => { protocol => $tcp },
                                ) or die $!;
                        }
                },

                _stop => sub { print "client session stopped\n" },

                got_response => sub {
                        my ($error, $addresses, $request) = @_[ARG0..ARG2];
                        use YAML; print YAML::Dump(
                                {
                                        error => $error,
                                        addr => $addresses,
                                        req => $request,
                                }
                        );
                },
        }
);

POE::Kernel->run();

like. look at it. it's beautiful. 😭


🌈 DESCRIPTION 🌈

POE::Component::Resolver runs Socket::getaddrinfo() calls in subprocesses, where they're allowed to block for as long as they need to. that's the whole trick bestie β€” the blocking goes in the baby process, not your main character event loop. 🧚

by default it runs eight subprocesses and prefers address families in whatever order getaddrinfo() hands them back. you can override both of those defaults with constructor parameters. and fyi, getaddrinfo() delegates to your operating system's resolver, which may itself be reconfigured according to the usual conventions. iykyk. πŸ’œ


πŸ‘‘ PUBLIC METHODS πŸ‘‘

πŸ’– new

makes you a brand new resolver. returns an object that you must hold onto and use to make requests. check the synopsis above for the whole picture. ✨

it accepts up to four optional named parameters.

  • af_order β€” may contain an arrayref of the address families to permit, in the order they're preferred. without af_order, the component will prefer IPv4 addresses over IPv6 for legacy compatibility. this may change in the future as IPv6 gains more widespread acceptance. and psst β€” check "ENVIRONMENT VARIABLES" for a way to override the default without hacking modules. πŸ‘€
# Prefer IPv6 addresses, but also return IPv4 ones.
my $r1 = POE::Component::Resolver->new(
        af_order => [ AF_INET6, AF_INET ]
);

# Only return IPv6 addresses,
# or nothing in cases where only IPv4 addresses exist.
my $r2 = POE::Component::Resolver->new(
        af_order => [ AF_INET6 ]
);
  • idle_timeout β€” decides how long to keep idle resolver subprocesses around before cleaning them up, in seconds. defaults to 15.0 seconds. ⏳
  • max_resolvers β€” controls the component's parallelism by defining the max number of sidecar processes to manage. defaults to 8, but you can go fewer or more depending on the resources you have and how much parallelism you actually need. ✨
# One at a time, but without the pesky blocking.
my $r3 = POE::Component::Resolver->new( max_resolvers => 1 );
  • sidecar_program β€” the disk location of a program that will perform blocking lookups on standard input and print the results on standard output. the sidecar program is only needed in special environments where bundling and executing extra utilities is tricky. PAR is one such environment.

the sidecar program needs to contain at least two statements:

use POE::Component::Resolver::Sidecar;
POE::Component::Resover::Sidecar->main();
bestie i am COPYING that verbatim, typos and all, because the rule is the rule. πŸ’…

πŸ¦‹ resolve

resolve() kicks off a new request to resolve a domain. the request gets enqueued in the component until a sidecar process is free to service it. resolve() returns a request ID that you can use to cancel() a request before it's done β€” or undef if the request couldn't even begin (like during shutdown, oop πŸ™ˆ). resolve requires two parameters and accepts some extra optional ones.

  • host and service β€” required. contain the host (name or Internet address) and service (name or numeric port) that get passed verbatim to getaddrinfo(). see Socket for details.
  • event β€” optional. contains the name of the event that will carry the resolver response. if you omit it, it defaults to resolver_response; you may wanna specify a shorter event name tbh.
  • hints β€” optional. if you specify it, it must contain a hashref of hints exactly as getaddrinfo() expects them. see Socket for details.
  • misc β€” optional continuation data that gets passed back in the response. it can contain literally any type of data your application needs. πŸŽ€

πŸ’€ cancel

cancel a request, given the request's ID.

my $request_id = $resolver->resolve("poe.dyndns.org", "http");
$resolver->cancel($request_id);

🌸 shutdown

shut down the resolver. POE::Component::Resolver retains resources β€” including child processes β€” for up to idle_timeout seconds. this may keep programs running up to idle_timeout seconds longer than they should. 😭

POE::Component::Resolver will release its resources (including child processes) when its shutdown() method is called. periodt.


⭐ unpack_addr

in scalar context, unpack_addr($response_addr_hashref) returns the addr element of $response_addr_hashref in a numeric form appropriate for the address family of the address.

sub handle_resolver_response {
        my ($error, $addresses, $request) = @_[ARG0..ARG2];

        foreach my $a (@$addresses) {
                my $numeric_addr = $resolver->unpack_addr($a);
                print "$request->{host} = $numeric_addr\n";
        }
}

in list context, it returns the numeric port and address.

sub handle_resolver_response {
        my ($error, $addresses, $request) = @_[ARG0..ARG2];

        foreach my $a (@$addresses) {
                my ($$numeric_addr, $port) = $resolver->unpack_addr($a);
                print "$request->{host} = $numeric_addr\n";
        }
}

unpack_addr() is a convenience wrapper around getnameinfo() from Socket. you're certainly welcome to use the discrete function instead, no pressure bestie. 🫢

unpack_addr() returns bleak emptiness on failure, regardless of context. you can check for an undef return.


🌟 PUBLIC EVENTS 🌟

πŸ’œ resolver_response

the resolver response event includes three parameters.

$_[ARG0] and $_[ARG1] contain the retrn values from Socket's getaddrinfo() call. these are an error message (if the call failed), and an arrayref of address structures if the call succeeded. πŸ”₯

the component provides its own error message, 'component shut down'. this response is given for every pending request at the time the user shuts down the component.

$_[ARG2] contains a hashref of information provided to the resolve() method. specifically, the values of resolve()'s host, service and misc parameters. πŸ’–


🌈 ENVIRONMENT VARIABLES 🌈

🧚 POCO_RESOLVER_IPV

the POCO_RESOLVER_IPV environment variable sets this component's default Internet Protocol Version search order. if the variable exists, it should contain a string with the numbers 4 and/or 6. POE::Component::Resolver will treate these as Internet Protocol versions to consider, in the order they are preferred. πŸ‘‘

POE::Component::Resolver's new() method accepts an af_order parameter that overrides this environment variable.

default to IPv4 addresses only:

export POCO_RESOLVER_IPV=4

default to IPv6 addresses only:

export POCO_RESOLVER_IPV=6

prefer IPv6, but accept IPv4 if needed:

export POCO_RESOLVER_IPV=64

prefer IPv4, but accept IPv6 if needed:

export POCO_RESOLVER_IPV=46

πŸ’… COMPATIBILITY ISSUES πŸ’…

πŸ“ Microsoft Windows

this module requires "Microsoft TCP/IP version 6" to be installed. steps for Windows XP Pro (the steps for your particular version of Windows may be subtly or drastically different):

  1. open your Control Panel
  2. open your Network Connections
  3. select your network connection from the available one(s)
  4. in the Local Area Connection Status dialog, click the Properties button
  5. if "Microsoft TCP/IP version 6" is listed as an item being used, you are done. βœ…
  6. otherwise click Install...
  7. choose to add a Protocol
  8. and install "Microsoft TCP/IP version 6" from the list of network protocols.

😭 BUGS 😭

  • there is no timeout on requests. (yes really. i know. screaming.)
  • there is no way to cancel a pending request.
not me pointing out the cancel method exists and then the bugs section saying there's no way to cancel a pending request. bestie, i don't make the rules, i just report them faithfully. πŸ’€

πŸ” TROUBLESHOOTING πŸ”

🌸 programs linger for several seconds before exiting

programs should shutdown() their POE::Component::Resolver objects when they're through needing asynchronous DNS resolution. programs should additionally destroy their resolvers if they intend to run awhile and want to reuse the memory they consume. 🫢

in some cases, it may be necessary to shutdown components that perform asynchronous DNS using POE::Component::Resolver... such as POE::Component::IRC, POE::Component::Client::Keepalive and POE::Component::Client::HTTP.

by default, the resolver subprocesses hang around for idle_timeout, which defaults to 15.0 seconds. destroying the Resolver object will clean up the process pool. assuming only that is keeping the event loop active, the program will then exit cleanly. ✨

alternatively, reduce idle_timeout to a more manageable number, such as 5.0 seconds.

otherwise something else may also be keeping the event loop active. πŸ‘€


πŸ“œ LICENSE πŸ“œ

except where otherwise noted, this distribution is Copyright 2011 by Rocco Caputo. all rights reserved. this distribution is free software; you may redistribute it and/or modify it under the same terms as Perl itself.


and that's the whole thing bestie!! πŸ’–βœ¨πŸŽ€ this was documented in perl v5.26.1, dated 2018-04-08, and it's POE::C...solver(3pm) in the manual β€” like, the header said so and everything. slay, go resolve some hostnames non-blockingly, you absolute icon. πŸ‘‘πŸ¦‹πŸ’…

πŸ“¦ where this page comes from