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 )