mirror of
https://github.com/deavmi/birchwood
synced 2024-09-20 09:23:38 +02:00
Client
- Added an overridable `onConnectionClosed()` that is called when the connection is closed by either us or the remote host - Added `closedConnectionHandler()` (gonna get rid of soon) - Calling `disconnect()` will now RATHER shutdown the socket than close it, it no longer performs any other sub-systems clean up - Implemented `doThreadCleanup()` which stops the receive and send managers, stops the eventy engine too - At every `read()` we do in the reader loop, check if the return value is `<0` or `==0` in which case set the running state to false and exit the loop - The exiting of the reader loop will now shutdown the socket (in case remote host caused it and not us (via `disconnect()` for example) ), `close()` the socket and call `doThreadCleanup()`
This commit is contained in:
parent
b4338aa3ed
commit
2305c19623
@ -205,6 +205,34 @@ public class Client : Thread
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the connection to the remote host is closed
|
||||||
|
*/
|
||||||
|
public void onConnectionClosed()
|
||||||
|
{
|
||||||
|
// TODO: Add log as default behaviour?
|
||||||
|
logger.log("Connection was closed, not doing anything");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the receiver realises the socket has
|
||||||
|
* closed
|
||||||
|
*/
|
||||||
|
private void closedConnectionHandler()
|
||||||
|
{
|
||||||
|
// Set the running state to false
|
||||||
|
this.running = false;
|
||||||
|
|
||||||
|
// Now we can wake up any sleeping managers and let them stop
|
||||||
|
this.sender.end();
|
||||||
|
this.receiver.end();
|
||||||
|
|
||||||
|
// TODO: Do any other clean up that needs to be done
|
||||||
|
|
||||||
|
// Call the event handler
|
||||||
|
onConnectionClosed();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests setting of the provided nickname
|
* Requests setting of the provided nickname
|
||||||
*
|
*
|
||||||
@ -926,8 +954,10 @@ public class Client : Thread
|
|||||||
/**
|
/**
|
||||||
* Disconnect from the IRC server gracefully
|
* Disconnect from the IRC server gracefully
|
||||||
*/
|
*/
|
||||||
public void quit()
|
public void quit(bool gracefulChannelLeave = false)
|
||||||
{
|
{
|
||||||
|
// TODO: Add graceful leaving of all channels?
|
||||||
|
|
||||||
/* Generate the quit command using the custom quit message */
|
/* Generate the quit command using the custom quit message */
|
||||||
Message quitCommand = new Message("", "QUIT", connInfo.quitMessage);
|
Message quitCommand = new Message("", "QUIT", connInfo.quitMessage);
|
||||||
sendMessage(quitCommand);
|
sendMessage(quitCommand);
|
||||||
@ -950,29 +980,38 @@ public class Client : Thread
|
|||||||
running = false;
|
running = false;
|
||||||
logger.log("disconnect() begin");
|
logger.log("disconnect() begin");
|
||||||
|
|
||||||
/* Close the socket */
|
/* Shutdown the socket */
|
||||||
socket.close();
|
|
||||||
logger.log("disconnect() socket closed");
|
|
||||||
|
|
||||||
// TODO: See libsnooze notes in `receiver.d` and `sender.d`, we could technically in some
|
/**
|
||||||
// ... teribble situation have a unregistered situaion which would then have a fallthrough
|
* Shutdown the socket unblocking
|
||||||
// ... notify and a wait which never wakes up (the solution is mentioned in `receiver.d`/`sender.d`)
|
* any reads and writes occuring
|
||||||
|
*
|
||||||
|
* Notably this unblocks the receiver
|
||||||
|
* thread and causes it to handle
|
||||||
|
* the shutdown.
|
||||||
|
*/
|
||||||
|
import std.socket : SocketShutdown;
|
||||||
|
socket.shutdown(SocketShutdown.BOTH);
|
||||||
|
logger.log("disconnect() socket shutdown");
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doThreadCleanup()
|
||||||
|
{
|
||||||
|
/* Stop the receive queue manager and wait for it to stop */
|
||||||
receiver.end();
|
receiver.end();
|
||||||
|
logger.log("doThreadCleanup() recvQueue manager stopped");
|
||||||
|
|
||||||
|
/* Stop the send queue manager and wait for it to stop */
|
||||||
sender.end();
|
sender.end();
|
||||||
|
logger.log("doThreadCleanup() sendQueue manager stopped");
|
||||||
/* Wait for receive queue manager to realise it needs to stop */
|
|
||||||
receiver.join();
|
|
||||||
logger.log("disconnect() recvQueue manager stopped");
|
|
||||||
|
|
||||||
/* Wait for the send queue manager to realise it needs to stop */
|
|
||||||
sender.join();
|
|
||||||
logger.log("disconnect() sendQueue manager stopped");
|
|
||||||
|
|
||||||
/* TODO: Stop eventy (FIXME: I don't know if this is implemented in Eventy yet, do this!) */
|
/* TODO: Stop eventy (FIXME: I don't know if this is implemented in Eventy yet, do this!) */
|
||||||
engine.shutdown();
|
engine.shutdown();
|
||||||
logger.log("disconnect() eventy stopped");
|
logger.log("doThreadCleanup() eventy stopped");
|
||||||
|
|
||||||
logger.log("disconnect() end");
|
logger.log("doThreadCleanup() end");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1019,28 +1058,42 @@ public class Client : Thread
|
|||||||
* FIXME: We need to find a way to tare down this socket, we don't
|
* FIXME: We need to find a way to tare down this socket, we don't
|
||||||
* want to block forever after running quit
|
* want to block forever after running quit
|
||||||
*/
|
*/
|
||||||
while(running)
|
readLoop: while(running)
|
||||||
{
|
{
|
||||||
/* Receieve at most 512 bytes (as per RFC) */
|
/* Receieve at most 512 bytes (as per RFC) */
|
||||||
ptrdiff_t bytesRead = socket.receive(currentData, SocketFlags.PEEK);
|
ptrdiff_t bytesRead = socket.receive(currentData, SocketFlags.PEEK);
|
||||||
|
|
||||||
|
// TODO: Should not be JUST unittest builds
|
||||||
|
// TODO: This sort of logic should be used by EVERY read
|
||||||
version(unittest)
|
version(unittest)
|
||||||
{
|
{
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
writeln("(peek) bytesRead: '", bytesRead, "' (status var or count)");
|
writeln("(peek) bytesRead: '", bytesRead, "' (status var or count)");
|
||||||
writeln("(peek) currentData: '", currentData, "'");
|
writeln("(peek) currentData: '", currentData, "'");
|
||||||
|
|
||||||
// On remote end closing connection
|
|
||||||
if(bytesRead == 0)
|
|
||||||
{
|
|
||||||
writeln("About to do the panic!");
|
|
||||||
*cast(byte*)0 = 2;
|
|
||||||
|
|
||||||
// FIXME: We need to set some state to disconnect here and tare
|
|
||||||
// ... down alles
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the remote host closed the connection
|
||||||
|
* OR some general error occurred
|
||||||
|
*
|
||||||
|
* TODO: See if the code is safe enough to only
|
||||||
|
* have to do this ONCE
|
||||||
|
*/
|
||||||
|
if(bytesRead == 0 || bytesRead < 0)
|
||||||
|
{
|
||||||
|
version(unittest)
|
||||||
|
{
|
||||||
|
import std.stdio;
|
||||||
|
writeln("Remote host ended connection or general error, Socket.ERROR: '", bytesRead, "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set running state to false, then exit loop */
|
||||||
|
this.running = false;
|
||||||
|
continue readLoop;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* FIXME: CHECK BYTES READ FOR SOCKET ERRORS! */
|
/* FIXME: CHECK BYTES READ FOR SOCKET ERRORS! */
|
||||||
@ -1064,7 +1117,18 @@ public class Client : Thread
|
|||||||
/* Chop off the LF */
|
/* Chop off the LF */
|
||||||
ubyte[] scratch;
|
ubyte[] scratch;
|
||||||
scratch.length = 1;
|
scratch.length = 1;
|
||||||
this.socket.receive(scratch);
|
long status = this.socket.receive(scratch);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the remote host closed the connection
|
||||||
|
* OR some general error occurred
|
||||||
|
*/
|
||||||
|
if(status == 0 || status < 0)
|
||||||
|
{
|
||||||
|
/* Set running state to false, then exit loop */
|
||||||
|
this.running = false;
|
||||||
|
continue readLoop;
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1103,7 +1167,19 @@ public class Client : Thread
|
|||||||
/* Guaranteed as we peeked this lenght */
|
/* Guaranteed as we peeked this lenght */
|
||||||
ubyte[] scratch;
|
ubyte[] scratch;
|
||||||
scratch.length = pos+1;
|
scratch.length = pos+1;
|
||||||
this.socket.receive(scratch);
|
long status = this.socket.receive(scratch);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the remote host closed the connection
|
||||||
|
* OR some general error occurred
|
||||||
|
*/
|
||||||
|
if(status == 0 || status < 0)
|
||||||
|
{
|
||||||
|
/* Set running state to false, then exit loop */
|
||||||
|
this.running = false;
|
||||||
|
continue readLoop;
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1113,13 +1189,34 @@ public class Client : Thread
|
|||||||
/* TODO: Dequeue without peek after this */
|
/* TODO: Dequeue without peek after this */
|
||||||
ubyte[] scratch;
|
ubyte[] scratch;
|
||||||
scratch.length = bytesRead;
|
scratch.length = bytesRead;
|
||||||
this.socket.receive(scratch);
|
long status = this.socket.receive(scratch);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the remote host closed the connection
|
||||||
|
* OR some general error occurred
|
||||||
|
*/
|
||||||
|
if(status == 0 || status < 0)
|
||||||
|
{
|
||||||
|
/* Set running state to false, then exit loop */
|
||||||
|
this.running = false;
|
||||||
|
continue readLoop;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* TODO: Yield here and in other places before continue */
|
/* TODO: Yield here and in other places before continue */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Shut down socket AND close it */
|
||||||
|
import std.socket : SocketShutdown;
|
||||||
|
socket.shutdown(SocketShutdown.BOTH);
|
||||||
|
socket.close();
|
||||||
|
|
||||||
|
/* Shutdown sub-systems */
|
||||||
|
doThreadCleanup();
|
||||||
|
|
||||||
|
// FIXME: Really invalidate everything here
|
||||||
|
|
||||||
|
/* Call the onDisconnect thing (TODO) */
|
||||||
|
onConnectionClosed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user