Author: bdonlan
Date: 2004-06-08 21:59:54 -0400 (Tue, 08 Jun 2004)
New Revision: 233

Modified:
   trunk/main/client/lib/Haver/Client/POE.pm
Log:
Fix race where a client message before S: ACCEPT could cause a disconnect due
to a S: WANT being sent at the same time


Modified: trunk/main/client/lib/Haver/Client/POE.pm
===================================================================
--- trunk/main/client/lib/Haver/Client/POE.pm   2004-06-09 01:54:40 UTC (rev 
232)
+++ trunk/main/client/lib/Haver/Client/POE.pm   2004-06-09 01:59:54 UTC (rev 
233)
@@ -106,6 +106,7 @@
                                        input
                                        send_raw
                                        send
+                                       _flush_msgq
                                        net_error
 
                                        destroy
@@ -269,6 +270,10 @@
        $heap->{UID} = $args{UID};
        $heap->{PASS} = $args{Password};
        $heap->{Host} = $args{Host};
+       undef $heap->{want};
+       $heap->{enabled} = 1;           # Set to 0 when graceful shutdown 
begins, to block user input
+       $heap->{accepted} = 0;          # Set to 1 when login is successful
+       $heap->{dead} = 0;                      # Set to 1 when the socket 
fails, to drop messages
        $args{Port} ||= 7070;
        $heap->{connect_wheel} =
        POE::Wheel::SocketFactory->new(
@@ -288,6 +293,7 @@
 
 sub disconnect {
        my ($kernel, $heap) = @_[KERNEL, HEAP];
+       $heap->{enabled} = 0;
        return if $heap->{closing};
        $heap->{closing} = 1;
        if ($heap->{want}) {
@@ -351,37 +357,57 @@
 
 sub send_raw {
        my ($kernel, $heap, @message) = @_[KERNEL,HEAP,ARG0..$#_];
-       return if ($heap->{want} && $heap->{want} eq "!impossible");
+       return if ($heap->{dead});
        eval { $heap->{conn}->put([EMAIL PROTECTED]); };
        if ($@) {
                # Ack, lost connection unexpectedly!
                # Hopefully we get net_error soon
-               $heap->{want} = "!impossible";
+               $heap->{dead} = 1;
                return;
        }
        _dprint 1, "C: ", join("\t", map { defined($_) ? $_ : '~UNDEF~' } 
@message), "\n";
        _call('dispatch', 'raw_out', @message);
+       $heap->{flushed} = 0;
 }
 
 sub send {
        my ($kernel, $heap, @message) = @_[KERNEL,HEAP,ARG0..$#_];
-       if ($heap->{want}) {
-               if (($heap->{want} ne uc $message[0]) &&
-                  ((uc $message[0] ne 'CANT') || ($message[1] ne 
$heap->{want}))) {
-                       _dprint 1, "(blocked) C: ", join("\t", @message), "\n";
-                       push @{$heap->{messageq} ||= []}, [EMAIL PROTECTED];
-                       return;
-               }
-               delete $heap->{want};
+       my $block = 0;
+       
+       if (!$heap->{enabled}) {
+               $block = 1;
+       } elsif ($heap->{accepted} && !$heap->{want}) {
+               $block = 0;
+       } elsif (!$heap->{want}) {
+               # Before we get a S: ACCEPT we can't send anything not in 
response to a S: WANT
+               $block = 1;
+       } elsif ($message[0] eq 'CANT') {
+               $block = ($message[1] ne $heap->{want});
+       } elsif ($message[0] ne $heap->{want}) {
+               $block = 1;
        }
+       
+       if ($block) {
+               _dprint 1, "(blocked) C: ", join("\t", @message), "\n";
+               push @{$heap->{messageq} ||= []}, [EMAIL PROTECTED];
+               return;
+       }
+       
+       delete $heap->{want};
+       
        $kernel->yield('send_raw', @message);
+       
+       $kernel->yield('_flush_msgq');
+}
+
+sub _flush_msgq {
+       my ($kernel, $heap) = @_[KERNEL,HEAP];
        if (exists $heap->{messageq}) {
                for (@{$heap->{messageq}}) {
                        $kernel->yield('send', @$_);
                }
                delete $heap->{messageq};
        }
-       $heap->{flushed} = 0;
 }
 
 ### SERVER EVENTS
@@ -428,7 +454,9 @@
 sub event_ACCEPT {
        my ($kernel, $heap) = @_[KERNEL,HEAP];
        $heap->{logged_in} = 1;
+       $heap->{accepted} = 1;
        _call('dispatch', 'login');
+       $kernel->yield('_flush_msgq');
 }
 
 sub event_REJECT {
@@ -673,7 +701,7 @@
 
 sub cleanup {
        my ($kernel, $heap) = @_[KERNEL, HEAP];
-       delete $heap->{$_} for qw(conn flushed closing UID PASS want messageq);
+       delete $heap->{$_} for qw(conn flushed closing UID PASS want messageq 
enabled accepted dead);
        $kernel->delay('force_close');
        
        if ($heap->{destroy_pending}) {


Reply via email to