NAME
Langertha::Raider::Provider::Fetch - Internal bounded fetch of a provider manifest from /.well-known/langertha.json
VERSION
version 0.503
SYNOPSIS
# Internal to Langertha-Raider -- no API promise.
my $fetch = Langertha::Raider::Provider::Fetch->new( allow_internal => 0 );
my $url = $fetch->target_url('provider.example'); # croaks on a bad target
my $got = $fetch->fetch_f($url)->get;
# { status => 'completed', url => ..., address => ..., body => ..., redirects => [...] }
# { status => 'refused' | 'failed', url => ..., error => ..., ... }
DESCRIPTION
Internal module. Its interface may change without notice.
Fetches a provider manifest (ADR 0007) the way a document from the network has to be fetched before anything trusts it:
https only, from
/.well-known/langertha.jsonof the origin the target names. A target with userinfo, a query or a fragment is refused before anything is sent.The target address is checked, and the connection goes to the checked address. The host is resolved once, every address it resolves to is classified ("address_kind"), and the request is sent to one of those addresses, never to a name that could resolve differently a second time. TLS still verifies the certificate against the host name (SNI and hostname check use the name, not the address).
Internal addresses are refused -- loopback, private (RFC 1918, shared address space, IPv6 unique local), link-local and reserved ranges -- unless "allow_internal" is set for a deliberately released internal Knarr or Skeid origin. Cloud metadata addresses, the unspecified address, multicast and the 240/4 range are refused always: none of them is ever a provider. A host whose addresses include one refused address is refused as a whole.
Limits: "max_bytes" of body (a larger
Content-Lengthstops the fetch at the header, a longer body while it arrives), "timeout" seconds for everything from resolving to the last byte, "max_redirects" redirects. No content encoding is asked for, so nothing is decompressed.A redirect is followed only within the origin (same scheme, host and port). A redirect to another origin is not followed and reported with its target, which is how a credential can never reach another origin through a redirect -- and this fetch sends none anyway: no
Authorization, no cookies, no API key, onlyAcceptand aUser-Agent.
"fetch_f" never fails for a network or policy reason; it resolves to a report whose status says how the fetch ended. Validation of the body is the caller's job (Langertha::Manifest).
allow_internal
Release loopback, private, link-local and reserved addresses (see "address_kind"). Default false. Never releases a never address.
max_bytes
Largest body accepted, in bytes. Default 1 MiB.
timeout
Seconds the whole fetch may take, resolving included. Default 10.
max_redirects
How many same-origin redirects are followed. Default 3.
loop
The IO::Async::Loop. Defaults to IO::Async::Loop->new.
resolver
Code reference taking a host name and returning a Future of its addresses as strings. Defaults to the loop's resolver (getaddrinfo). An address literal is never handed to it.
ssl_options
Extra SSL_* arguments for the TLS connection, such as SSL_ca_file. Default none: the system's CA store. Cannot switch off the certificate or hostname check -- those are set after it.
target_url
my $url = $fetch->target_url('provider.example'); # https://provider.example/.well-known/langertha.json
my $url = $fetch->target_url('knarr.internal:8443');
my $url = $fetch->target_url('https://provider.example/');
The manifest URL a command-line target names: HOST, HOST:PORT, [IPv6]:PORT or an https URL whose path is empty, / or the well-known path itself. Croaks with the reason on anything else: another scheme (http included), userinfo, a query, a fragment or another path.
address_kind
my ( $kind, $releasable ) = $fetch->address_kind('10.0.0.5'); # ( 'private', 1 )
my ( $kind ) = $fetch->address_kind('93.184.216.34'); # ( 'public' )
Classifies an IPv4 or IPv6 address (a %scope suffix is ignored): public, or one of loopback, private, link-local, reserved (releasable with "allow_internal") or metadata, unspecified, multicast, invalid (never). IPv4-mapped, IPv4-compatible, NAT64 (64:ff9b::/96) and 6to4 (2002::/16) IPv6 addresses are classified by the IPv4 address they carry. The second value is true for a releasable kind.
check_addresses
my $refusal = $fetch->check_addresses($host, @addresses);
undef when every address may be connected to, else the reason the first one that may not is refused.
fetch_f
my $report = await $fetch->fetch_f($url);
Fetches the manifest at $url (from "target_url") and resolves to a report hash:
status--completed,refused(an address or a redirect the policy does not allow) orfailed(network, TLS, HTTP status, a limit).url-- the URL asked for;final_url-- the one the body came from (after same-origin redirects).address-- the address connected to (once there is one).redirects-- the same-origin locations followed, in order.body-- the body octets (completedonly).error-- why (refusedandfailed);location-- the target of a redirect that was not followed.
check_host_f
my $checked = await $fetch->check_host_f('provider.example');
# { addresses => [ '93.184.216.34' ] }
# { status => 'refused' | 'failed', error => ... }
Resolves $host (an address literal stands for itself) and checks every address with "check_addresses". Resolves to the addresses, or to the refused or failed status and why.
origin
my $origin = $fetch->origin('https://Provider.example/v1'); # https://provider.example:443
Scheme, host and port of a URL (string or URI), lower-cased and with the default port spelled out, so two origins compare with eq; undef for a URL without a host.
SEE ALSO
Langertha::Raider::CLI::Provider --
raider provider inspectLangertha::Manifest -- the manifest's schema and validator (core)
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/Getty/langertha-raider/issues.
IRC
Join #langertha on irc.perl.org or message Getty directly.
CONTRIBUTING
Contributions are welcome! Please fork the repository and submit a pull request.
AUTHOR
Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.