--- loncom/loncnew	2004/02/09 10:58:03	1.40
+++ loncom/loncnew	2004/03/02 16:25:17	1.44
@@ -2,7 +2,7 @@
 # The LearningOnline Network with CAPA
 # lonc maintains the connections to remote computers
 #
-# $Id: loncnew,v 1.40 2004/02/09 10:58:03 foxr Exp $
+# $Id: loncnew,v 1.44 2004/03/02 16:25:17 albertel Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -262,9 +262,16 @@ sub SocketTimeout {
     Log("WARNING", "A socket timeout was detected");
     Debug(0, " SocketTimeout called: ");
     $Socket->Dump();
+    if(exists($ActiveTransactions{$Socket})) {
+	FailTransaction($ActiveTransactions{$Socket});
+    }
     KillSocket($Socket);	# A transaction timeout also counts as
                                 # a connection failure:
     $ConnectionRetriesLeft--;
+    if($ConnectionRetriesLeft <= 0) {
+	Log("CRITICAL", "Host marked dead: ".GetServerHost());
+    }
+
 }
 #----------------------------- Timer management ------------------------
 
@@ -326,11 +333,13 @@ sub Tick {
 	    if($successCount == 0) { # All connections failed:
 		Debug(5,"Work in queue failed to make any connectiouns\n");
 		EmptyQueue();	# Fail pending transactions with con_lost.
+		CloseAllLondConnections(); # Should all be closed but....
 	    }
 	} else {
 	    ShowStatus(GetServerHost()." >>> DEAD!!! <<<");
 	    Debug(5,"Work in queue, but gave up on connections..flushing\n");
 	    EmptyQueue();	# Connections can't be established.
+	    CloseAllLondConnections(); # Should all already be closed but...
 	}
        
     }
@@ -521,7 +530,9 @@ sub CompleteTransaction {
 	unlink $Transaction->getFile();
     }
 }
+
 =pod
+
 =head1 StartClientReply
 
    Initiates a reply to a client where the reply data is a parameter.
@@ -537,6 +548,7 @@ sub CompleteTransaction {
     The data to send to apached client.
 
 =cut
+
 sub StartClientReply {
     my $Transaction   = shift;
     my $data     = shift;
@@ -554,7 +566,9 @@ sub StartClientReply {
 	      cb       => \&ClientWritable,
 	      data     => $data);
 }
+
 =pod
+
 =head2 FailTransaction
 
   Finishes a transaction with failure because the associated lond socket
@@ -564,8 +578,7 @@ sub StartClientReply {
   - The transaction is 'live' in which case we initiate the sending
     of "con_lost" to the client.
 
-Deleting the transaction means killing it from the 
-%ActiveTransactions hash.
+Deleting the transaction means killing it from the %ActiveTransactions hash.
 
 Parameters:
 
@@ -573,6 +586,7 @@ Parameters:
  
    The LondTransaction we are failing.
  
+
 =cut
 
 sub FailTransaction {
@@ -584,9 +598,6 @@ sub FailTransaction {
 	Debug(1," Replying con_lost to ".$transaction->getRequest());
 	StartClientReply($transaction, "con_lost\n");
     }
-    if($ConnectionRetriesLeft <= 0) {
-	Log("CRITICAL", "Host marked dead: ".GetServerHost());
-    }
 
 }
 
@@ -614,7 +625,10 @@ Close all connections open on lond prior
 =cut
 sub CloseAllLondConnections {
     foreach my $Socket (keys %ActiveConnections) {
-	KillSocket($Socket);
+      if(exists($ActiveTransactions{$Socket})) {
+	FailTransaction($ActiveTransactions{$Socket});
+      }
+      KillSocket($Socket);
     }
 }
 =cut
@@ -666,6 +680,7 @@ sub KillSocket {
     #
     if($ConnectionCount == 0) {
 	EmptyQueue();
+	CloseAllLondConnections; # Should all already be closed but...
     }
 }
 
@@ -740,119 +755,119 @@ transaction is in progress, the socket a
 
 sub LondReadable {
 
-   my $Event      = shift;
-   my $Watcher    = $Event->w;
-   my $Socket     = $Watcher->data;
-   my $client     = undef;
+    my $Event      = shift;
+    my $Watcher    = $Event->w;
+    my $Socket     = $Watcher->data;
+    my $client     = undef;
 
-   &Debug(6,"LondReadable called state = ".$Socket->GetState());
+    &Debug(6,"LondReadable called state = ".$Socket->GetState());
 
 
-   my $State = $Socket->GetState(); # All action depends on the state.
+    my $State = $Socket->GetState(); # All action depends on the state.
 
-   SocketDump(6, $Socket);
-   my $status = $Socket->Readable();
+    SocketDump(6, $Socket);
+    my $status = $Socket->Readable();
 
-   &Debug(2, "Socket->Readable returned: $status");
+    &Debug(2, "Socket->Readable returned: $status");
 
-   if($status != 0) {
-      # bad return from socket read. Currently this means that
-      # The socket has become disconnected. We fail the transaction.
+    if($status != 0) {
+	# bad return from socket read. Currently this means that
+	# The socket has become disconnected. We fail the transaction.
 
-      Log("WARNING",
-         "Lond connection lost.");
-      if(exists($ActiveTransactions{$Socket})) {
-         FailTransaction($ActiveTransactions{$Socket});
-      }
-      $Watcher->cancel();
-      KillSocket($Socket);
-      $ConnectionRetriesLeft--;       # Counts as connection failure
-      return;
-   }
-   SocketDump(6,$Socket);
+	Log("WARNING",
+	    "Lond connection lost.");
+	if(exists($ActiveTransactions{$Socket})) {
+	    FailTransaction($ActiveTransactions{$Socket});
+	}
+	$Watcher->cancel();
+	KillSocket($Socket);
+	$ConnectionRetriesLeft--;       # Counts as connection failure
+	return;
+    }
+    SocketDump(6,$Socket);
 
-   $State = $Socket->GetState(); # Update in case of transition.
-   &Debug(6, "After read, state is ".$State);
+    $State = $Socket->GetState(); # Update in case of transition.
+    &Debug(6, "After read, state is ".$State);
 
-   if($State eq "Initialized") {
+    if($State eq "Initialized") {
 
 
-   } elsif ($State eq "ChallengeReceived") {
+    } elsif ($State eq "ChallengeReceived") {
 	#  The challenge must be echoed back;  The state machine
 	# in the connection takes care of setting that up.  Just
 	# need to transition to writable:
+	
+	$Watcher->cb(\&LondWritable);
+	$Watcher->poll("w");
 
-      $Watcher->cb(\&LondWritable);
-      $Watcher->poll("w");
+    } elsif ($State eq "ChallengeReplied") {
 
-   } elsif ($State eq "ChallengeReplied") {
+    } elsif ($State eq "RequestingVersion") {
+	# Need to ask for the version... that is writiability:
 
-   } elsif ($State eq "RequestingVersion") {
-      # Need to ask for the version... that is writiability:
-      
-      $Watcher->cb(\&LondWritable);
-      $Watcher->poll("w");
-      
-   } elsif ($State eq "ReadingVersionString") {
-      # Read the rest of the version string... 
-   } elsif ($State eq "SetHost") {
-      # Need to request the actual domain get set...
-      
-      $Watcher->cb(\&LondWritable);
-      $Watcher->poll("w");
-   } elsif ($State eq "HostSet") {
-      # Reading the 'ok' from the peer.
-      
-   } elsif ($State eq "RequestingKey") {
+	$Watcher->cb(\&LondWritable);
+	$Watcher->poll("w");
+
+    } elsif ($State eq "ReadingVersionString") {
+	# Read the rest of the version string... 
+    } elsif ($State eq "SetHost") {
+	# Need to request the actual domain get set...
+
+	$Watcher->cb(\&LondWritable);
+	$Watcher->poll("w");
+    } elsif ($State eq "HostSet") {
+	# Reading the 'ok' from the peer.
+
+    } elsif ($State eq "RequestingKey") {
 	#  The ok was received.  Now we need to request the key
 	#  That requires us to be writable:
 
-      $Watcher->cb(\&LondWritable);
-      $Watcher->poll("w");
+	$Watcher->cb(\&LondWritable);
+	$Watcher->poll("w");
 
-   } elsif ($State eq "ReceivingKey") {
+    } elsif ($State eq "ReceivingKey") {
 
-   } elsif ($State eq "Idle") {
+    } elsif ($State eq "Idle") {
    
-   # This is as good a spot as any to get the peer version
-   # string:
+	# This is as good a spot as any to get the peer version
+	# string:
    
-   if($LondVersion eq "unknown") {
-      $LondVersion = $Socket->PeerVersion();
-      Log("INFO", "Connected to lond version: $LondVersion");
-   }
+	if($LondVersion eq "unknown") {
+	    $LondVersion = $Socket->PeerVersion();
+	    Log("INFO", "Connected to lond version: $LondVersion");
+	}
 	# If necessary, complete a transaction and then go into the
 	# idle queue.
 	#  Note that a trasition to idle indicates a live lond
 	# on the other end so reset the connection retries.
 	#
-      $ConnectionRetriesLeft = $ConnectionRetries; # success resets the count
-      $Watcher->cancel();
-      if(exists($ActiveTransactions{$Socket})) {
-         Debug(5,"Completing transaction!!");
-         CompleteTransaction($Socket, 
-                             $ActiveTransactions{$Socket});
-      } else {
-         Log("SUCCESS", "Connection ".$ConnectionCount." to "
-            .$RemoteHost." now ready for action");
-      }
-      ServerToIdle($Socket);	# Next work unit or idle.
+	$ConnectionRetriesLeft = $ConnectionRetries; # success resets the count
+	$Watcher->cancel();
+	if(exists($ActiveTransactions{$Socket})) {
+	    Debug(5,"Completing transaction!!");
+	    CompleteTransaction($Socket, 
+				$ActiveTransactions{$Socket});
+	} else {
+	    Log("SUCCESS", "Connection ".$ConnectionCount." to "
+		.$RemoteHost." now ready for action");
+	}
+	ServerToIdle($Socket);	# Next work unit or idle.
 	
-   } elsif ($State eq "SendingRequest") {
+    } elsif ($State eq "SendingRequest") {
 	#  We need to be writable for this and probably don't belong
 	#  here inthe first place.
 
-      Deubg(6, "SendingRequest state encountered in readable");
-      $Watcher->poll("w");
-      $Watcher->cb(\&LondWritable);
+	Deubg(6, "SendingRequest state encountered in readable");
+	$Watcher->poll("w");
+	$Watcher->cb(\&LondWritable);
 
-   } elsif ($State eq "ReceivingReply") {
+    } elsif ($State eq "ReceivingReply") {
 
 
-   } else {
-	 # Invalid state.
-      Debug(4, "Invalid state in LondReadable");
-   }
+    } else {
+	# Invalid state.
+	Debug(4, "Invalid state in LondReadable");
+    }
 }
 
 =pod
@@ -932,131 +947,108 @@ sub LondWritable {
 
     SocketDump(6,$Socket);
 
-   if      ($State eq "Connected")         {
+    #  If the socket is writable, we must always write.
+    # Only by writing will we undergo state transitions.
+    # Old logic wrote in state specific code below, however
+    # That forces us at least through another invocation of
+    # this function after writability is possible again.
+    # This logic also factors out common code for handling
+    # write failures... in all cases, write failures 
+    # Kill the socket.
+    #  This logic makes the branches of the >big< if below
+    # so that the writing states are actually NO-OPs.
+
+    if ($Socket->Writable() != 0) {
+	#  The write resulted in an error.
+	# We'll treat this as if the socket got disconnected:
+	Log("WARNING", "Connection to ".$RemoteHost.
+	    " has been disconnected");
+	if(exists($ActiveTransactions{$Socket})) {
+	    FailTransaction($ActiveTransactions{$Socket});
+	}
+	$Watcher->cancel();
+	KillSocket($Socket);
+	return;
+    }
 
-      if ($Socket->Writable() != 0) {
-         #  The write resulted in an error.
-         # We'll treat this as if the socket got disconnected:
-         Log("WARNING", "Connection to ".$RemoteHost.
-                        " has been disconnected");
-         FailTransaction($ActiveTransactions{$Socket});
-         $Watcher->cancel();
-         KillSocket($Socket);
-         return;
-      }
-      
-   #  "init" is being sent...
-   
-   } elsif ($State eq "Initialized")       {
 
-      # Now that init was sent, we switch 
-      # to watching for readability:
 
-      $Watcher->cb(\&LondReadable);
-      $Watcher->poll("r");
+    if      ($State eq "Connected")         {
 
-   } elsif ($State eq "ChallengeReceived") {
-      # We received the challenge, now we 
-      # are echoing it back. This is a no-op,
-      # we're waiting for the state to change
-	
-      if($Socket->Writable() != 0) {
+	#  "init" is being sent...
+ 
+    } elsif ($State eq "Initialized")       {
 
-         $Watcher->cancel();
-         KillSocket($Socket);
-         return;
-      }
+	# Now that init was sent, we switch 
+	# to watching for readability:
+
+	$Watcher->cb(\&LondReadable);
+	$Watcher->poll("r");
 	
-   } elsif ($State eq "ChallengeReplied")  {
-      # The echo was sent back, so we switch
-      # to watching readability.
-
-      $Watcher->cb(\&LondReadable);
-      $Watcher->poll("r");
-   } elsif ($State eq "RequestingVersion") {
-      # Sending the peer a version request...
-      
-      if($Socket->Writable() != 0) {
-         $Watcher->cancel();
-         KillSocket($Socket);
-         return;
-      }
-   } elsif ($State eq "ReadingVersionString") {
-      # Transition to read since we have sent the
-      # version command and now just need to read the
-      # version string from the peer:
-      
-      $Watcher->cb(\&LondReadable);
-      $Watcher->poll("r");
+    } elsif ($State eq "ChallengeReceived") {
+	# We received the challenge, now we 
+	# are echoing it back. This is a no-op,
+	# we're waiting for the state to change
+	
+    } elsif ($State eq "ChallengeReplied")  {
+	# The echo was sent back, so we switch
+	# to watching readability.
+
+	$Watcher->cb(\&LondReadable);
+	$Watcher->poll("r");
+    } elsif ($State eq "RequestingVersion") {
+	# Sending the peer a version request...
+
+    } elsif ($State eq "ReadingVersionString") {
+	# Transition to read since we have sent the
+	# version command and now just need to read the
+	# version string from the peer:
       
-   } elsif ($State eq "SetHost") {
-      #  Setting the remote domain...
+	$Watcher->cb(\&LondReadable);
+	$Watcher->poll("r");
       
-      if($Socket->Writable() != 0) {
-         $Watcher->cancel();
-         KillSocket($Socket);
-         return;
-      }
-   } elsif ($State eq "HostSet") {
-      # Back to readable to get the ok.
+    } elsif ($State eq "SetHost") {
+	#  Setting the remote domain...
+
+    } elsif ($State eq "HostSet") {
+	# Back to readable to get the ok.
       
-      $Watcher->cb(\&LondReadable);
-      $Watcher->poll("r");
+	$Watcher->cb(\&LondReadable);
+	$Watcher->poll("r");
       
 
-   } elsif ($State eq "RequestingKey")     {
-      # At this time we're requesting the key.
-      # again, this is essentially a no-op.
-      # we'll write the next chunk until the
-      # state changes.
-
-      if($Socket->Writable() != 0) {
-         # Write resulted in an error.
-
-         $Watcher->cancel();
-         KillSocket($Socket);
-         return;
-
-      }
-   } elsif ($State eq "ReceivingKey")      {
-      # Now we need to wait for the key
-      # to come back from the peer:
+    } elsif ($State eq "RequestingKey")     {
+	# At this time we're requesting the key.
+	# again, this is essentially a no-op.
+
+    } elsif ($State eq "ReceivingKey")      {
+	# Now we need to wait for the key
+	# to come back from the peer:
 
-      $Watcher->cb(\&LondReadable);
-      $Watcher->poll("r");
+	$Watcher->cb(\&LondReadable);
+	$Watcher->poll("r");
 
-   } elsif ($State eq "SendingRequest")    {
+    } elsif ($State eq "SendingRequest")    {
  
-   # At this time we are sending a request to the
+	# At this time we are sending a request to the
 	# peer... write the next chunk:
 
-      if($Socket->Writable() != 0) {
 
-         if(exists($ActiveTransactions{$Socket})) {
-            Debug(3, "Lond connection lost, failing transactions");
-            FailTransaction($ActiveTransactions{$Socket});
-         }
-	      $Watcher->cancel();
-         KillSocket($Socket);
-         return;
-	    
-      }
+    } elsif ($State eq "ReceivingReply")    {
+	# The send has completed.  Wait for the
+	# data to come in for a reply.
+	Debug(8,"Writable sent request/receiving reply");
+	$Watcher->cb(\&LondReadable);
+	$Watcher->poll("r");
 
-   } elsif ($State eq "ReceivingReply")    {
-      # The send has completed.  Wait for the
-      # data to come in for a reply.
-      Debug(8,"Writable sent request/receiving reply");
-      $Watcher->cb(\&LondReadable);
-      $Watcher->poll("r");
-
-   } else {
-      #  Control only passes here on an error: 
-      #  the socket state does not match any
-      #  of the known states... so an error
-      #  must be logged.
+    } else {
+	#  Control only passes here on an error: 
+	#  the socket state does not match any
+	#  of the known states... so an error
+	#  must be logged.
 
-      &Debug(4, "Invalid socket state ".$State."\n");
-   }
+	&Debug(4, "Invalid socket state ".$State."\n");
+    }
     
 }
 =pod
@@ -1229,10 +1221,12 @@ sub QueueTransaction {
 		Debug(5,"Starting additional lond connection");
 		if(MakeLondConnection() == 0) {
 		    EmptyQueue();	# Fail transactions, can't make connection.
+		    CloseAllLondConnections; # Should all be closed but...
 		}
 	    } else {
 		ShowStatus(GetServerHost()." >>> DEAD !!!! <<<");
 		EmptyQueue();	# It's worse than that ... he's dead Jim.
+		CloseAllLondConnections; # Should all be closed but..
 	    }
 	}
     } else {			# Can start the request:
@@ -1275,7 +1269,7 @@ sub ClientRequest {
     Debug(8,"Data: ".$data." this read: ".$thisread);
     $data = $data.$thisread;	# Append new data.
     $watcher->data($data);
-    if($data =~ /(.*\n)/) {	# Request entirely read.
+    if($data =~ /\n$/) {	# Request entirely read.
 	if($data eq "close_connection_exit\n") {
 	    Log("CRITICAL",
 		"Request Close Connection ... exiting");