Skip to content

Commit c2e83fd

Browse files
authored
Merge pull request #53 from toddr-bot/koan.toddr.bot/fix-reconnect-dies
fix: ReconnectToServer survives transient connection failures
2 parents 2d37863 + 6c0fc45 commit c2e83fd

File tree

3 files changed

+115
-4
lines changed

3 files changed

+115
-4
lines changed

lib/Net/Jabber/Bot.pm

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,14 @@ sub ReconnectToServer {
580580
INFO("Sleeping $sleep_time before attempting re-connect");
581581
sleep $sleep_time;
582582
$sleep_time *= 2 if ( $sleep_time < 300 );
583-
$self->_init_jabber();
583+
eval { $self->_init_jabber() };
584+
if ($@) {
585+
WARN("Reconnection attempt failed: $@");
586+
# Clean up partial client state so IsConnected() stays false
587+
# and next _init_jabber() creates a fresh client
588+
$self->jabber_client(undef);
589+
next;
590+
}
584591
if ( defined $background_subroutine ) {
585592
INFO("Running background routine.");
586593
&$background_subroutine( $self, 0 ); # call background proc so we can check for errors while down.
@@ -782,7 +789,7 @@ sub _jabber_in_iq_message {
782789
# convert 5.010000 to 5.10.0
783790
my $perl_version = $];
784791
$perl_version =~ s/(\d{3})(?=\d)/$1./g;
785-
$perl_version =~ s/\.0+(\d)/.$1/;
792+
$perl_version =~ s/\.0+(\d)/.$1/g;
786793

787794
$self->jabber_client->VersionSend(
788795
to => $from,

t/07-test_reconnect_failure.t

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!perl
2+
3+
use strict;
4+
use warnings;
5+
6+
# Override sleep to avoid delays in tests
7+
BEGIN { *CORE::GLOBAL::sleep = sub { }; }
8+
9+
use Test::More tests => 10;
10+
use Net::Jabber::Bot;
11+
12+
# stuff for mock client object
13+
use FindBin;
14+
use lib "$FindBin::Bin/lib";
15+
use MockJabberClient; # Test object
16+
17+
my $bot_alias = 'reconnect_fail_bot';
18+
my $server = 'talk.google.com';
19+
20+
my %forums_and_responses;
21+
$forums_and_responses{'test_forum1'} = [ "jbot:", "" ];
22+
23+
my $bot = Net::Jabber::Bot->new(
24+
server => $server,
25+
conference_server => "conference.$server",
26+
port => 5222,
27+
username => 'test_username',
28+
password => 'test_pass',
29+
alias => $bot_alias,
30+
message_function => sub { },
31+
background_function => sub { },
32+
loop_sleep_time => 5,
33+
process_timeout => 5,
34+
forums_and_responses => \%forums_and_responses,
35+
out_messages_per_second => 5,
36+
max_message_size => 800,
37+
max_messages_per_hour => 100,
38+
forum_join_grace => 0,
39+
);
40+
41+
isa_ok( $bot, "Net::Jabber::Bot" );
42+
ok( $bot->IsConnected(), "Bot connected after init" );
43+
44+
# Test 1: _init_jabber dies when Connect returns undef
45+
$bot->Disconnect();
46+
ok( !$bot->IsConnected(), "Bot disconnected" );
47+
48+
$Net::Jabber::Client::connect_fail_remaining = 1;
49+
eval { $bot->_init_jabber() };
50+
like( $@, qr/Jabber server is down/, "_init_jabber dies on connection failure" );
51+
52+
# The partial jabber_client object is left behind — this is the state
53+
# that ReconnectToServer must clean up
54+
ok( defined $bot->jabber_client, "Partial client exists after failed _init_jabber" );
55+
56+
# Clean up for next test
57+
$bot->jabber_client(undef);
58+
59+
# Test 2: ReconnectToServer survives transient failures and reconnects
60+
$Net::Jabber::Client::connect_fail_remaining = 2;
61+
$bot->ReconnectToServer();
62+
ok( $bot->IsConnected(), "ReconnectToServer reconnects after 2 transient failures" );
63+
is( $Net::Jabber::Client::connect_fail_remaining, 0, "All failures consumed" );
64+
65+
# Test 3: Background function runs after successful reconnect
66+
my $bg_called = 0;
67+
my $bot2 = Net::Jabber::Bot->new(
68+
server => $server,
69+
conference_server => "conference.$server",
70+
port => 5222,
71+
username => 'test_username2',
72+
password => 'test_pass',
73+
alias => 'bg_test_bot',
74+
message_function => sub { },
75+
background_function => sub { $bg_called++ },
76+
loop_sleep_time => 5,
77+
process_timeout => 5,
78+
forums_and_responses => \%forums_and_responses,
79+
out_messages_per_second => 5,
80+
max_message_size => 800,
81+
max_messages_per_hour => 100,
82+
forum_join_grace => 0,
83+
);
84+
85+
$bg_called = 0;
86+
$Net::Jabber::Client::connect_fail_remaining = 1;
87+
$bot2->Disconnect();
88+
$bot2->ReconnectToServer();
89+
ok( $bot2->IsConnected(), "Bot2 reconnects after 1 failure" );
90+
ok( $bg_called > 0, "Background function called after successful reconnect" );
91+
92+
# Test 4: Multiple failures with increasing backoff (all caught)
93+
$Net::Jabber::Client::connect_fail_remaining = 5;
94+
$bot->Disconnect();
95+
$bot->ReconnectToServer();
96+
ok( $bot->IsConnected(), "ReconnectToServer survives 5 consecutive failures" );

t/lib/MockJabberClient.pm

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,19 @@ sub SetCallBacks {
6060
$self->{message_callback} = $callbacks{'message'};
6161
}
6262

63+
our $connect_fail_remaining = 0;
64+
6365
sub Connect {
6466
my $self = shift;
65-
67+
6668
$self->{server} = shift;
67-
return 1; # Always confirm we're connected.
69+
70+
if ($connect_fail_remaining > 0) {
71+
$connect_fail_remaining--;
72+
return undef; # Simulate connection failure.
73+
}
74+
75+
return 1; # Confirm we're connected.
6876
}
6977

7078
sub AuthSend {

0 commit comments

Comments
 (0)