NAME

Langertha::HTTP::UserAgent - LWP::UserAgent that keeps credentials on their origin across redirects

VERSION

version 0.503

SYNOPSIS

my $ua = Langertha::HTTP::UserAgent->new( agent => 'my-app', timeout => 30 );
my $engine = Langertha::Engine::Anthropic->new( api_key => $key, user_agent => $ua );

DESCRIPTION

The LWP::UserAgent an engine builds for its synchronous requests ("user_agent" in Langertha::Role::HTTP), and so also the one the synchronous fallback of the _f methods runs over (Langertha::Request::SyncHTTP). It takes the same constructor arguments as LWP::UserAgent and differs only in how it follows redirects: through Langertha::HTTP::Redirect, the policy the Net::Async::HTTP backend follows as well. A redirect to another origin carries no credential header (of any name) and no credential in the URL; a redirect from https to http is not followed (karr k374).

Pass one as user_agent when you bring your own agent and want that policy; a plain LWP::UserAgent keeps LWP's own redirect behaviour.

Built with "connect_host" and "connect_address" it also pins the connection: a request to that host connects to that address instead of resolving the name, while the Host header, TLS SNI and the certificate's name check still use the host name (karr k375). An engine builds its agent this way when it has a "connect_address" in Langertha::Role::HTTP.

connect_host

The host name "connect_address" applies to (compared case-insensitively with the host of each request's URL). Given together with "connect_address" or not at all.

connect_address

An IPv4 or IPv6 address literal (no brackets, port or scope). A request to "connect_host", on any port and over http or https, opens its TCP connection to this address; the name is not resolved. Everything else about the request still names the host: the Host header, and for https the SNI (not sent when the host is itself an address literal) and the name the certificate is checked against (SSL_verifycn_name). Certificate verification itself stays as the agent's ssl_opts configure it, as for an unpinned request. Requests to other hosts are not affected.

A redirect from "connect_host" to another host is not followed (the returned 3xx carries a Client-Warning saying so): the address was checked for this host only, and a new host would be resolved again. A request that would go through a proxy ($ua->proxy, env_proxy) fails with a 500 response instead of being sent, because the proxy would resolve the name.

How: for the duration of each pinned request, the two methods LWP::Protocol::http marks for subclasses to override are wrapped: _extra_sock_opts (the socket options, for http and https) and _check_sock (called with the socket before the request is written). They act only for this agent's own protocol objects and only for the pinned host, so a request another agent sends meanwhile (from a content callback, say) is not affected. Before anything is written, the socket's peer must be the pinned address and, over https with LWP's verify_hostname on, its certificate must be for the host; otherwise the request is not sent and a 500 response says why. That also covers a socket handed out by a conn_cache: an agent sharing its conn_cache with an unpinned agent could otherwise be given a connection the other agent opened elsewhere. The Client-Peer comparison after the request is a backstop in case a later LWP stops calling those hooks, and that one only detects a wrong peer after the request was sent; a response from a connection that never passed the check (no _check_sock call, or a request_send handler answering in place of the network) is refused as well, with a 500. With verify_hostname on (LWP's default) the check also requires the session's certificate chain to have verified, so a reused socket another agent opened without verification does not pass.

connect_address_error

my $error = Langertha::HTTP::UserAgent::connect_address_error($address);

undef when $address is an IPv4 or IPv6 address literal usable as "connect_address", else a message saying why not.

redirect_ok

LWP's hook, called with the request it is about to send for a redirect. Applies "guard_referral" in Langertha::HTTP::Redirect — which refuses every method but GET and HEAD whatever requests_redirectable says, and strips the request in place when it goes to another origin — then refuses what "redirect_ok" in LWP::UserAgent refuses. A refusal by the policy is named in a Client-Warning header on the returned 3xx response. With a "connect_address", a redirect from "connect_host" to another host is refused as well.

tls_identity_error

my $error = Langertha::HTTP::UserAgent::tls_identity_error( $socket, $host, chain => 1, name => 1 );

undef when the IO::Socket::SSL connection $socket passes the requested checks, else a message saying which failed: chain requires OpenSSL's verification result of the session to be X509_V_OK (recorded even for a session opened without verification), name requires the peer certificate to be for $host (http scheme rules). Used before a pinned request is written ("connect_address").

send_request

LWP's per-hop dispatch. Unchanged unless the agent has a "connect_address" and the request goes to "connect_host"; then the connection is pinned as described there.

same_address

Langertha::HTTP::UserAgent::same_address( '::ffff:127.0.0.1', '127.0.0.1' );   # 1

True when two IP address literals are the same address (compared packed, so ::1 and 0:0:0:0:0:0:0:1 match); an IPv4 address reported IPv4-mapped by an IPv6 socket matches its IPv4 form.

SEE ALSO

SUPPORT

Issues

Please report bugs and feature requests on GitHub at https://github.com/Getty/langertha/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 <getty@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2026 by Torsten Raudssus https://raudssus.de/.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.