At
view release on metacpan or search on metacpan
lib/At/UserAgent.pm view on Meta::CPAN
say "[DEBUG] [At] Retrying with fresh DPoP nonce..." if $ENV{DEBUG};
$headers->{DPoP} = $self->_generate_dpop_proof( $url, 'POST', $req->{skip_ath} ) if $self->token_type eq 'DPoP';
if ( $ENV{DEBUG} ) {
say "[DEBUG] [At] POST (Retry) $url";
say "[DEBUG] [At] Headers (Retry): " . JSON::PP::encode_json($headers);
}
$tx = $agent->post( $url, $headers, %args );
$res = $tx->result;
if ( my $nonce = $res->headers->header('DPoP-Nonce') ) { $self->dpop_nonce($nonce); }
if ( $ENV{DEBUG} ) {
say "[DEBUG] [At] Response Code (Retry): " . $res->code;
say "[DEBUG] [At] Response Headers (Retry): " . JSON::PP::encode_json( $res->headers->to_hash );
}
}
}
if ( $res->is_success ) {
my $content = $res->body ? ( $res->headers->content_type // '' ) =~ m[json] ? $res->json : $res->body : ();
return wantarray ? ( $content, $res->headers->to_hash ) : $content;
}
my $msg = $res->message;
if ( my $body = $res->body ) {
my $json;
try { $json = JSON::PP::decode_json($body) }
catch ($e) { }
if ($json) {
my $details = $json->{error} // '';
if ( $json->{message} && $json->{message} ne $details ) {
$details .= ( $details ? ': ' : '' ) . $json->{message};
}
$msg .= ": " . $details if $details;
$msg .= " - " . $json->{error_description} if $json->{error_description};
}
else {
$msg .= " (" . $body . ")";
}
}
return At::Error->new( message => $msg, fatal => 1 );
}
method websocket( $url, $cb ) {
$agent->inactivity_timeout(0); # Disable inactivity timeout for firehose
$agent->websocket(
$url => sub ( $ua, $tx ) {
if ( !$tx->is_websocket ) {
$cb->( undef, At::Error->new( message => "WebSocket handshake failed", fatal => 0 ) );
return;
}
# Keep-alive heartbeat every 20 seconds
my $id = Mojo::IOLoop->recurring(
20 => sub {
return unless $tx;
$tx->send( [ 1, 0, 0, 0, 9, '' ] ); # Raw Ping frame
}
);
# Activity watchdog: if we don't get a message for 10 seconds, close and reconnect
my $watchdog;
my $reset_watchdog = sub {
Mojo::IOLoop->remove($watchdog) if defined $watchdog;
$watchdog = Mojo::IOLoop->timer(
10 => sub {
$tx->finish( 4000, "Watchdog timeout" );
}
);
};
$reset_watchdog->();
$tx->on(
message => sub ( $tx, $msg ) {
$reset_watchdog->();
$cb->( $msg, undef );
}
);
$tx->on(
finish => sub ( $tx, $code, $reason ) {
Mojo::IOLoop->remove($id) if defined $id;
Mojo::IOLoop->remove($watchdog) if defined $watchdog;
$cb->( undef, At::Error->new( message => "WebSocket finished: $code " . ( $reason // '' ), fatal => 0 ) );
$tx = undef;
}
);
}
);
}
method _set_auth_header($token) { $self->auth($token); }
} 1;
__END__
=pod
=encoding utf-8
=head1 NAME
At::UserAgent - Abstract Base Class for AT Protocol User Agents
=head1 DESCRIPTION
C<At::UserAgent> defines the interface for HTTP clients used by L<At>. It handles DPoP proof generation, automatic
nonce management, and authentication headers.
=head1 Subclasses
=over
=item L<At::UserAgent::Mojo>
Uses L<Mojo::UserAgent>. Recommended for asynchronous or high-performance applications.
=item L<At::UserAgent::Tiny>
Uses L<HTTP::Tiny>. A lightweight, zero-dependency alternative. Does not support firehose/WebSockets.
=back
=head1 Attributes
=head2 C<accessJwt()>
The current access token.
( run in 0.763 second using v1.01-cache-2.11-cpan-98d9bbf8dc8 )