Compare commits

...

94 Commits

Author SHA1 Message Date
Tristan B. Kildaire 0f75841600 Upgraded 2020-07-20 19:45:25 +02:00
Tristan B. Kildaire 5fc42a6ef9 ACTUALLY updated bformat 2020-06-17 08:25:23 +02:00
Tristan B. Kildaire 72139e70c3 Updated bformat. 2020-06-17 08:24:48 +02:00
Tristan B. Kildaire ac56eb7689 Added TODO for further thread safety changes to come. 2020-06-16 18:50:50 +02:00
Tristan B. Kildaire d19b552997 Migrated successfully to new bformat. 2020-06-16 18:46:54 +02:00
Tristan B. Kildaire 261448159f Had a mistake migrating to the new bformat, now migrated almost. 2020-06-16 18:46:26 +02:00
Tristan B. Kildaire c4a4d46264 Upgraded to new bformat v1.0.5 2020-06-16 18:46:07 +02:00
Tristan B. Kildaire c7f701e8f6 Use correct bformat version. 2020-06-16 18:39:50 +02:00
Tristan B. Kildaire b783d98ea7 `addConnection` is now threadsafe. 2020-06-16 18:35:00 +02:00
Tristan B. Kildaire 5c13fa3c77 Using new `addConnection` which aims to be thread safe. 2020-06-16 18:32:17 +02:00
Tristan B. Kildaire 60cd993472 Added stub function `addConnection` to the BesterServer type for thread safe Connection object array appending. 2020-06-16 18:29:42 +02:00
Tristan B. Kildaire aec7144106 Converted missing bmessage `sendMessage` call. 2020-06-16 18:20:45 +02:00
Tristan B. Kildaire 9db02f607b Updated to new bmessage API. 2020-06-16 18:19:46 +02:00
Tristan B. Kildaire c646b4bf20 Updated vibe-d 2020-06-16 18:10:18 +02:00
Tristan B. Kildaire 88af27c3cb Stripepd whitespace from username. 2020-05-15 16:47:18 +02:00
Tristan B. Kildaire 2b54142439 Cleaned up and added TODOs 2020-05-15 16:31:35 +02:00
Tristan B. Kildaire 0b5ca124d6 Use new shutdown and send a fatal message. 2020-05-15 16:06:40 +02:00
Tristan B. Kildaire 71632ec1f3 Added comment.
Remove dunused function.
2020-05-15 15:54:37 +02:00
Tristan B. Kildaire 80b0772fd8 Removed some TODOs 2020-05-15 15:52:33 +02:00
Tristan B. Kildaire 32d35cec71 Added "messageType" field to header that is constructed in SEND_CLIENTS for sending to clients. 2020-05-15 12:50:50 +02:00
Tristan B. Kildaire a22b65f7c4 Renamed "responseType" field to "messageType" field. 2020-05-15 12:49:26 +02:00
Tristan B. Kildaire 615ebc823a Set "responseType" field for status reports. 2020-05-15 12:48:23 +02:00
Tristan B. Kildaire 8d0256d105 Added return as to not expect a payload along with the autnetication. 2020-05-14 16:00:23 +02:00
Tristan B. Kildaire 4e091e2f2d Removed todo 2020-05-14 14:25:46 +02:00
Tristan B. Kildaire 06c406ef86 Added todo. 2020-05-14 14:23:57 +02:00
Tristan B. Kildaire 00c16b68da Send special status report "auth_special" id for authentication. 2020-05-14 14:15:01 +02:00
Tristan B. Kildaire 1ade3365b1 Use shutdown instead. 2020-05-14 14:03:54 +02:00
Tristan B. Kildaire 95577698b8 Added ID forwarding. 2020-05-14 11:22:41 +02:00
Tristan B. Kildaire be2953595d Added status report to SEND_SERVERS 2020-05-14 11:12:58 +02:00
Tristan B. Kildaire ea25aabf26 Send status report after SEND_CLIENTS 2020-05-14 11:12:17 +02:00
Tristan B. Kildaire 2993f74b05 Adding new function to send status reports on errors or sucesses. 2020-05-14 11:08:27 +02:00
Tristan B. Kildaire cc934e5ec8 Fixed test handler. 2020-05-14 10:55:51 +02:00
Tristan B. Kildaire e0e7d5ffd0 Refactored. 2020-05-14 00:48:52 +02:00
Tristan B. Kildaire 89f51e5944 Updated test case to take the `id` field into account. 2020-05-14 00:41:24 +02:00
Tristan B. Kildaire fc33bd0b69 Refactored message dispatch. 2020-05-14 00:39:45 +02:00
Tristan B. Kildaire 955cc6d21d Shutdown on general format error. 2020-05-14 00:36:12 +02:00
Tristan B. Kildaire 9f1e63367c Return false. 2020-05-14 00:27:37 +02:00
Tristan B. Kildaire a9ee569314 Removed unused variable and returned `true`. 2020-05-14 00:24:27 +02:00
Tristan B. Kildaire 4442c62ae7 Misplaced sendErrorReport call. 2020-05-14 00:20:57 +02:00
Tristan B. Kildaire 70df082f9a Added dummy response with new `id` and `status` system. 2020-05-14 00:17:13 +02:00
Tristan B. Kildaire 27ada909d6 Send error reports on errors. 2020-05-14 00:05:04 +02:00
Tristan B. Kildaire 1d7f0b8ac2 Created `sendErrorReport` function. 2020-05-14 00:01:36 +02:00
Tristan B. Kildaire 3e570e8066 Added comment and fixed compilation error. 2020-05-13 23:40:16 +02:00
Tristan B. Kildaire 79cfe50353 Shutdown connecction on fatal protocol error. 2020-05-13 23:11:14 +02:00
Tristan B. Kildaire 007e15caa4 Added todo 2020-05-13 23:02:44 +02:00
Tristan B. Kildaire cc359a14f0 Added `"id"` field requirement to `"payload"` block. 2020-05-13 23:00:24 +02:00
Tristan B. Kildaire d5e3a86842 Cleaned up. 2020-05-12 20:50:26 +02:00
Tristan B. Kildaire bd4846bf61 Code clean up. 2020-05-12 20:41:25 +02:00
Tristan B. Kildaire d19a7809ca wip 2020-05-09 18:04:36 +02:00
Tristan B. Kildaire 23bcda9b3c WP 2020-05-09 16:16:48 +02:00
Tristan B. Kildaire b862ba7270 WIP 2020-05-09 16:14:55 +02:00
Tristan B. Kildaire 4d21754fff Typo fix in comment. 2020-05-09 15:54:42 +02:00
Tristan B. Kildaire 1b2c4cabe9 Added comments to types. 2020-05-09 15:53:25 +02:00
Tristan B. Kildaire 48d58f26b7 Include port in address tring. 2020-05-09 15:48:04 +02:00
Tristan B. Kildaire ef272edc77 Disable redis for now. 2020-05-09 15:25:22 +02:00
Tristan B. Kildaire 9c853b22c3 Added `toString`s for all the listeners. 2020-05-09 15:19:57 +02:00
Tristan B. Kildaire 9f565e9c09 Updated dependancies 2020-05-09 15:19:47 +02:00
Tristan B. Kildaire 115f8f25fd Added listener information to `getServerInfo` informer command. 2020-05-09 14:37:24 +02:00
Tristan B. Kildaire e29bb80e5e Added FIXME 2020-05-09 14:30:39 +02:00
Tristan B. Kildaire 7972eb3235 Updated handler test to send response back to test client. 2020-05-09 11:17:24 +02:00
Tristan B. Kildaire 8f0ddd766a WIP 2020-05-08 15:42:28 +02:00
Tristan B. Kildaire 95de9c25d1 Server now authenticates against its datastore. 2020-05-08 14:17:48 +02:00
Tristan B. Kildaire c300c9995f Redis backend works. 2020-05-08 14:16:56 +02:00
Tristan B. Kildaire e6ddbaac62 Added stub function for new database configuration code. 2020-05-08 13:58:00 +02:00
Tristan B. Kildaire a042dc4a0d Added `authenticate` function.
Fixed compiler error.
2020-05-08 13:43:09 +02:00
Tristan B. Kildaire ab64098b6f Added `createAccount` function. 2020-05-08 13:40:47 +02:00
Tristan B. Kildaire 185b7e86e3 Shutdown the datastore on server shutdown. 2020-05-08 13:32:34 +02:00
Tristan B. Kildaire 0e0be00b85 Added missing import. 2020-05-08 13:30:33 +02:00
Tristan B. Kildaire c31e67dd72 Added datastore to `BesterServer`.
Added redis connection code for a redis-backed datastore type.
2020-05-08 13:29:56 +02:00
Tristan B. Kildaire fdcb23ddf8 Added comments.
Added `createAccount` function.
2020-05-08 10:39:02 +02:00
Tristan B. Kildaire 2003505979 Added WIP classes for data store backend. 2020-05-08 10:32:22 +02:00
Tristan B. Kildaire 5a5241a0c8 Added vibe-d as a dependancy. 2020-05-08 10:28:07 +02:00
Tristan B. Kildaire bd409551db Fixed comment.
Added missing comment.
2020-05-07 22:34:29 +02:00
Tristan B. Kildaire eeaba93bb2 Cleaned up 2020-05-07 21:22:45 +02:00
Tristan B. Kildaire c0b328aaff Cleaned up. 2020-05-07 21:22:10 +02:00
Tristan B. Kildaire 28a043dc29 Cleaned up. 2020-05-07 21:14:31 +02:00
Tristan B. Kildaire 7864a60fff Fixed comment. 2020-05-07 20:52:11 +02:00
Tristan B. Kildaire a3d71c7267 Added some missing comments in response.d 2020-05-07 20:39:41 +02:00
Tristan B. Kildaire f58ad52ca5 Fixed comments in server.d 2020-05-07 20:38:31 +02:00
Tristan B. Kildaire de51e7eff3 Removed uneeded whitespace. 2020-05-07 20:27:03 +02:00
Tristan B. Kildaire 65f7574d57 Added missing comments. 2020-05-07 19:43:42 +02:00
Tristan B. Kildaire 5e68fd511d Added todo 2020-05-07 19:34:21 +02:00
Tristan B. Kildaire dc850f845d Shutdown now shuts down all connections (besides active handler connections). 2020-05-07 19:08:19 +02:00
Tristan B. Kildaire 9f43a9f153 Added server shutdown function to shutdown informer.
Added shutdown function for informer to shutdown informer service.
Added shutdown function to informer client to shutdown informer client.
2020-05-07 19:01:00 +02:00
Tristan B. Kildaire cbc52a6ab7 Added control variable to stop informer server.
Close socket on informer stop.
2020-05-07 18:54:33 +02:00
Tristan B. Kildaire 6f29c3770e Close socket after run. 2020-05-07 18:42:22 +02:00
Tristan B. Kildaire 8fc41ac50c Added missing comment for function. 2020-05-07 18:12:45 +02:00
Tristan B. Kildaire 55d4dfabe2 Added new field for future feature. 2020-05-07 18:12:37 +02:00
Tristan B. Kildaire 3b16145c79 Added comment. 2020-05-07 17:42:30 +02:00
Tristan B. Kildaire 18d35d2374 Added comments. 2020-05-05 09:25:05 +02:00
Tristan B. Kildaire 58f5124f46 Removed old code for built-in handlers in sendHandler. 2020-05-05 09:16:24 +02:00
Tristan B. Kildaire 909717bca7 Added comment. 2020-05-05 08:24:32 +02:00
Tristan B. Kildaire cd03e14ff1 Set class `BeesterInformerClient to final. 2020-05-05 08:23:43 +02:00
Tristan B. Kildaire 96d277bbb7 Cleaned up.
Added missing comment.
2020-05-04 23:14:15 +02:00
17 changed files with 881 additions and 251 deletions

View File

@ -5,7 +5,8 @@
],
"copyright": "Copyright © 2020, Tristan B. Kildaire",
"dependencies": {
"bformat": "~>1.0.4"
"bformat": "1.0.8",
"vibe-d": "0.9.0-beta.1"
},
"description": "Bester protocol daemon. ",
"license": "AGPL v3",

View File

@ -1,6 +1,19 @@
{
"fileVersion": 1,
"versions": {
"bformat": "1.0.4"
"bformat": "1.0.8",
"botan": "1.12.18",
"botan-math": "1.0.3",
"diet-ng": "1.7.2",
"eventcore": "0.9.6",
"libasync": "0.8.6",
"libevent": "2.0.2+2.0.16",
"memutils": "1.0.4",
"mir-linux-kernel": "1.0.1",
"openssl": "1.1.6+1.0.1g",
"stdx-allocator": "2.77.5",
"taggedalgebraic": "0.11.16",
"vibe-core": "1.9.2",
"vibe-d": "0.9.0-beta.1"
}
}

View File

@ -1,6 +1,6 @@
{
"network" : {
"types" : ["unix", "tcp4", "tcp6"],
"types" : ["tcp4", "tcp6"],
"unix" : {
"address" : "besterUNIXSock"
},
@ -20,7 +20,17 @@
"type2" : {"handlerBinary" : "./testing/unixSock2.py", "unixDomainSocketPath" : "bSock"}
}
},
"admin" : {
"database" : {
"type" : "redis",
"redis" : {
"address" : "127.0.0.1",
"port" : "6379"
}
},
"admin" : {
"info" : {
}
}
}

View File

@ -6,7 +6,7 @@ import std.socket : SocketOSException, parseAddress, UnixAddress;
import utils.debugging : dprint;
import std.stdio : File, writeln;
import std.json : parseJSON, JSONValue;
import listeners.listener : BesterListener;
import listeners.listener : BesterListener, BesterListenerException;
import listeners.types : TCP4Listener, TCP6Listener, UNIXListener;
import std.file : exists;
@ -32,6 +32,8 @@ void main(string[] args)
/* TODO: Add usage check and arguments before this */
dprint("Main finished, remaining threads are keeping this open if it hangs");
/* TODO: Install signal handler so a shutdown can trigger shutdown */
}
private JSONValue getConfig(string configurationFilePath)
@ -65,28 +67,36 @@ private BesterListener[] getListeners(BesterServer server, JSONValue networkBloc
/* TODO: Error handling and get keys and clean up for formality */
/* Look for IPv4 TCP block */
JSONValue inet4TCPBlock = networkBlock["tcp4"];
dprint("<<< IPv4 TCP Block >>>\n" ~ inet4TCPBlock.toPrettyString());
string inet4Address = inet4TCPBlock["address"].str();
ushort inet4Port = to!(ushort)(inet4TCPBlock["port"].str());
TCP4Listener tcp4Listener = new TCP4Listener(server, parseAddress(inet4Address, inet4Port));
listeners ~= tcp4Listener;
try
{
/* Look for IPv4 TCP block */
JSONValue inet4TCPBlock = networkBlock["tcp4"];
dprint("<<< IPv4 TCP Block >>>\n" ~ inet4TCPBlock.toPrettyString());
string inet4Address = inet4TCPBlock["address"].str();
ushort inet4Port = to!(ushort)(inet4TCPBlock["port"].str());
TCP4Listener tcp4Listener = new TCP4Listener(server, parseAddress(inet4Address, inet4Port));
listeners ~= tcp4Listener;
/* Look for IPv6 TCP block */
JSONValue inet6TCPBlock = networkBlock["tcp6"];
dprint("<<< IPv6 TCP Block >>>\n" ~ inet6TCPBlock.toPrettyString());
string inet6Address = inet6TCPBlock["address"].str();
ushort inet6Port = to!(ushort)(inet6TCPBlock["port"].str());
TCP6Listener tcp6Listener = new TCP6Listener(server, parseAddress(inet6Address, inet6Port));
listeners ~= tcp6Listener;
/* Look for IPv6 TCP block */
JSONValue inet6TCPBlock = networkBlock["tcp6"];
dprint("<<< IPv6 TCP Block >>>\n" ~ inet6TCPBlock.toPrettyString());
string inet6Address = inet6TCPBlock["address"].str();
ushort inet6Port = to!(ushort)(inet6TCPBlock["port"].str());
TCP6Listener tcp6Listener = new TCP6Listener(server, parseAddress(inet6Address, inet6Port));
listeners ~= tcp6Listener;
/* Look for UNIX Domain block */
JSONValue unixDomainBlock = networkBlock["unix"];
dprint("<<< UNIX Domain Block >>>\n" ~ unixDomainBlock.toPrettyString());
string unixAddress = unixDomainBlock["address"].str();
// UNIXListener unixListener = new UNIXListener(server, new UnixAddress(unixAddress));
// listeners ~= unixListener;
/* Look for UNIX Domain block */
JSONValue unixDomainBlock = networkBlock["unix"];
dprint("<<< UNIX Domain Block >>>\n" ~ unixDomainBlock.toPrettyString());
string unixAddress = unixDomainBlock["address"].str();
// UNIXListener unixListener = new UNIXListener(server, new UnixAddress(unixAddress));
// listeners ~= unixListener;
}
catch(BesterListenerException e)
{
}
return listeners;
}

View File

@ -63,11 +63,24 @@ public final class BesterConnection : Thread
debugPrint("New client handler spawned for " ~ clientConnection.remoteAddress().toAddrString());
}
/**
* Shutdown the BesterConnection by stopping
* the read-write loop and closing the socket.
*/
public void shutdown()
{
/* TODO: Send message posssibly, think about this for listeners and informers (etc.) too */
isActive = false;
}
override public string toString()
{
return username ~ "@" ~ clientConnection.remoteAddress().toAddrString();
}
/**
* Returns an array of the username and password.
*/
public string[] getCredentials()
{
return [username, password];
@ -77,7 +90,7 @@ public final class BesterConnection : Thread
private void run()
{
debugPrint("<<< Begin read/send loop >>>");
while(isActive) /*TODO: Remove and also make the stting of this kak not be closing socket */
while(isActive)
{
/* Received JSON message */
JSONValue receivedMessage;
@ -97,7 +110,7 @@ public final class BesterConnection : Thread
if(connectionType == Scope.SERVER)
{
debugPrint("Server connection done, closing BesterConnection.");
isActive = false;
shutdown();
}
}
catch(BesterException exception)
@ -140,19 +153,42 @@ public final class BesterConnection : Thread
}
/* TODO: Comment [], rename [] */
/**
* Dispatches the message to the correct message handler.
*
* Returns `true` on success or partial success, `false`
* on fatal protocol error.
*/
private bool dispatchMessage(Scope scopeField, JSONValue payloadBlock)
{
/* Status of dispatch */
bool dispatchStatus = true;
/* The payload type */
string payloadType;
/* TODO: Bounds checking, type checking */
/* The payload data */
JSONValue payloadData;
/* Get the payload type */
string payloadType = payloadBlock["type"].str;
debugPrint("Payload type is \"" ~ payloadType ~ "\"");
/* The payload tag */
string payloadTag;
/* Get the payload data */
JSONValue payloadData = payloadBlock["data"];
/* Attempt to parse protocol-critical fields */
try
{
/* Get the payload type */
payloadType = payloadBlock["type"].str;
debugPrint("Payload type is \"" ~ payloadType ~ "\"");
/* Get the payload data */
payloadData = payloadBlock["data"];
/* Get the payload tag */
payloadTag = payloadBlock["id"].str();
}
catch(JSONException e)
{
debugPrint("Fatal error when processing packet, missing fields");
return false;
}
/* Lookup the payloadType handler */
MessageHandler chosenHandler = server.findHandler(payloadType);
@ -160,7 +196,36 @@ public final class BesterConnection : Thread
/* Check if it is a dummy type */
if(cmp(payloadType, "dummy") == 0)
{
/* Construct a dummy response */
JSONValue dummyMessage;
/* Construct a header block */
JSONValue headerBlock;
headerBlock["status"] = "0";
/* Attach the header block */
dummyMessage["header"] = headerBlock;
/* Construct the payload block */
JSONValue dummyPayloadBlock;
dummyPayloadBlock["data"] = null;
dummyPayloadBlock["type"] = payloadType;
dummyPayloadBlock["id"] = payloadTag;
/* Attach the payload block */
dummyMessage["payload"] = dummyPayloadBlock;
try
{
/* Send the message */
sendMessage(clientConnection, dummyMessage);
}
catch(NetworkException e)
{
debugPrint("Error sending status message, fatal closing connection");
/* TODO: We should deactivate the connection when this happens */
return false;
}
}
/* Check if the payload is a built-in command */
else if(cmp(payloadType, "builtin") == 0)
@ -182,13 +247,15 @@ public final class BesterConnection : Thread
debugPrint("Closing socket...");
isActive = false;
sendStatus(0, JSONValue());
// sendStatus(0, JSONValue());
}
else
{
debugPrint("Invalid built-in command type");
/* TODO: Generate error response */
dispatchStatus = false;
// dispatchStatus = false;
/* TODO: Send a response as the "builtin" message handler */
}
}
/* If an external handler is found (i.e. not a built-in command) */
@ -205,52 +272,76 @@ public final class BesterConnection : Thread
/* TODO: Continue here, we will make all error handling do on construction as to make this all more compact */
debugPrint("<<< Message Handler [" ~ chosenHandler.getPluginName() ~ "] response >>>\n\n" ~ handlerResponse.toString());
/* Execute the message handler's command (as per its reply) */
handlerResponse.execute(this);
/* Execute the message handler's command (as per its reply) and pass in the tag */
handlerResponse.execute(this, payloadTag);
}
catch(ResponseError e)
{
/* In the case of an error with the message handler, send an error to the client/server */
/* TODO: Send error here */
//JSONValue errorResponse;
//errorResponse["dd"] = 2;
//debugPrint("Response error");
dispatchStatus = false;
/* TODO: Clean up comments */
/* Send error message to client */
sendStatusReport(StatusType.FAILURE, payloadTag);
}
/* TODO: Be more specific with errors and reporting in the future */
catch(Exception e)
{
/* TODO: Remove me */
debugPrint("fhjhfsdjhfdjhgsdkjh UUUUH:" ~e.toString());
dispatchStatus = false;
/* Send error message to client */
sendStatusReport(StatusType.FAILURE, payloadTag);
}
debugPrint("Handler section done (for client)");
/* TODO: Handle response */
}
/* If no message handler for the specified type could be found */
else
{
/* TODO: Implement error handling */
debugPrint("No handler available for payload type \"" ~ payloadType ~ "\"");
/* Send error message to client */
JSONValue handlerName = payloadType;
sendStatus(1, handlerName);
dispatchStatus = false;
sendStatusReport(StatusType.FAILURE, payloadTag);
}
return dispatchStatus;
return true;
}
/* Send a status message to the client */
public void sendStatus(uint code, JSONValue data)
/**
* Type of the status report.
* Either 0 (for success) or 1 (for failure).
*/
public enum StatusType
{
/* Construct a status message */
SUCCESS,
FAILURE
}
/**
* Send a status report for the message with id
* `id` of type `StatusType`.
*/
public void sendStatusReport(StatusType statusType, string id)
{
/* Construct the response */
JSONValue statusMessage;
JSONValue statusBlock;
statusBlock["code"] = to!(string)(code);
statusBlock["data"] = data;
statusMessage["status"] = statusBlock;
/* Construct the header block */
JSONValue headerBlock;
headerBlock["status"] = statusType == 0 ? "good" : "bad";
headerBlock["messageType"] = "statusReport";
/* Attach the header block */
statusMessage["header"] = headerBlock;
/* Create the payload block */
JSONValue payloadBlock;
payloadBlock["id"] = id;
/* Attach the payload block */
statusMessage["payload"] = payloadBlock;
try
{
@ -287,23 +378,29 @@ public final class BesterConnection : Thread
return Scope.UNKNOWN;
}
/**
* Sends an error message on fatal error.
* Used before client shutdown on such
* an error.
*/
private void sendFatalMessage()
{
/* TODO: Implement me */
}
/* Process the received message */
private void processMessage(JSONValue jsonMessage)
{
/* Attempt to convert the message to JSON */
try
{
/* Convert message to JSON */
debugPrint("<<< Received JSON >>>\n\n" ~ jsonMessage.toPrettyString());
/* TODO: Bounds checking, type checking */
/* Get the header */
JSONValue headerBlock = jsonMessage["header"];
/**
* Check to see if this connection is currently "untyped".
*
@ -324,8 +421,11 @@ public final class BesterConnection : Thread
/* TODO: Send message back about an invalid scope */
/* TODO: End this here */
isActive = false;
/* Send fatal message */
sendFatalMessage();
/* Stop the read/write loop */
shutdown();
return;
}
else if(scopeField == Scope.CLIENT)
@ -361,7 +461,10 @@ public final class BesterConnection : Thread
this.password = password;
/* Send error message to client */
sendStatus(5, JSONValue());
// sendStatus(5, JSONValue());
/* TODO: Send authentication success */
sendStatusReport(StatusType.SUCCESS, "auth_special");
}
/* If authentication failed due to malformed message or incorrect details */
else
@ -372,11 +475,11 @@ public final class BesterConnection : Thread
*/
debugPrint("Authenticating the user failed, sending error and closing connection.");
/* Send error message to client */
sendStatus(2, JSONValue());
/* Send fatal message */
sendFatalMessage();
/* Stop the read/write loop */
isActive = false;
shutdown();
return;
}
}
@ -387,35 +490,46 @@ public final class BesterConnection : Thread
/* Set the connection type to `scopeField` */
connectionType = scopeField;
}
/* Attempt to get the payload block and dispatch the message */
bool dispatchStatus;
if(connectionType == Scope.CLIENT)
{
return;
}
}
/* Get the `payload` block */
JSONValue payloadBlock = jsonMessage["payload"];
debugPrint("<<< Payload is >>>\n\n" ~ payloadBlock.toPrettyString());
/* Dispatch the message */
dispatchStatus = dispatchMessage(connectionType, payloadBlock);
/* TODO: Catch error here and not inside dispatchMessage, gets rid of the need for this if statement */
if(dispatchStatus)
/**
* Dispatch the message. If a fatal failure is
* detected then the connection will be shutdown.
*/
if(dispatchMessage(connectionType, payloadBlock))
{
debugPrint("Dispatch succeeded");
}
else
{
/* TODO: Error handling */
debugPrint("Dispatching failed...");
debugPrint("Dispatch failed, deactivating connection...");
/* Send fatal message */
sendFatalMessage();
/* Shutdown the connection */
shutdown();
}
}
/* If the attempt to convert the message to JSON fails */
catch(JSONException exception)
{
debugPrint("General format error");
sendStatus(3, JSONValue());
debugPrint("Fatal format error, deactivating connection...");
/* Send fatal message */
sendFatalMessage();
/* Shutdown the connection */
shutdown();
}
}
}

View File

@ -1,6 +1,6 @@
module handlers.response;
import std.json : JSONValue, JSONException, parseJSON;
import std.json : JSONValue, JSONException, parseJSON, toJSON;
import std.conv : to;
import utils.debugging : debugPrint;
import std.string : cmp;
@ -14,7 +14,10 @@ import std.string : split;
import server.server : BesterServer;
import handlers.commands : Command;
/* The type of the command the message handler wants us to run */
/**
* The type of the command the message handler wants
* us to run
*/
private enum CommandType : ubyte
{
/* Simple message flow (always end point) */
@ -24,9 +27,14 @@ private enum CommandType : ubyte
UNKNOWN
}
/**
* Represents a response message from a handler.
*/
public final class HandlerResponse
{
/* The message-handler's response */
/**
* The message-handler's response.
*/
private JSONValue messageResponse;
/* The command to be executed */
@ -38,6 +46,10 @@ public final class HandlerResponse
/* The associated server */
private BesterServer server;
/**
* Constructs a new `HandlerResponse` object that represents the
* message handler's response message and the execution of it.
*/
this(BesterServer server, MessageHandler handler, JSONValue messageResponse)
{
/* Set the message-handler's response message */
@ -142,10 +154,12 @@ public final class HandlerResponse
}
}
public void execute(BesterConnection originalRequester)
/**
* Executes the command. Either `sendClients`, `sendServers`
* or `sendHandler`.
*/
public void execute(BesterConnection originalRequester, string messageID)
{
/* TODO: Implement me */
/* If the command is SEND_CLIENTS */
if(commandType == CommandType.SEND_CLIENTS)
{
@ -163,16 +177,12 @@ public final class HandlerResponse
BesterConnection[] connectionList = originalRequester.server.getClients(clients);
//debugPrint("Users matched online on server: " ~ to!(string)(connectionList));
/* The fully response message to send back */
JSONValue clientPayload;
// /* Set the `handlerName` field of the header block */
// JSONValue handlerName;
// handlerName["handlerName"] = handler.getPluginName();
/* Set the header of the response */
JSONValue headerBlock;
headerBlock["messageType"] = "receivedMessage";
clientPayload["header"] = headerBlock;
/* Set the payload of the response */
@ -183,10 +193,11 @@ public final class HandlerResponse
/**
* Loop through each BesterConnection in connectionList and
* send the message-handler payload response message to each
* of them.
*/
* Loop through each BesterConnection in connectionList and
* send the message-handler payload response message to each
* of them.
*/
bool allSuccess = true;
for(ulong i = 0; i < connectionList.length; i++)
{
/* Get the conneciton */
@ -206,17 +217,23 @@ public final class HandlerResponse
catch(SocketOSException exception)
{
/**
* If there was an error sending to the client, this can happen
* if the client has disconnected but hasn't yet been removed from
* the connections array and hence we try to send on a dead socket
* or get the remoteAddress on a dead socket, which causes a
* SocketOSException to be called.
*/
debugPrint("Attempted interacting with dead socket");
* If there was an error sending to the client, this can happen
* if the client has disconnected but hasn't yet been removed from
* the connections array and hence we try to send on a dead socket
* or get the remoteAddress on a dead socket, which causes a
* SocketOSException to be called.
*/
debugPrint("Attempted interacting with dead socket");
allSuccess = false;
}
}
debugPrint("SEND_CLIENTS: Completed run");
/**
* Send a status report here.
*/
originalRequester.sendStatusReport(cast(BesterConnection.StatusType)!allSuccess, messageID);
}
else if (commandType == CommandType.SEND_SERVERS)
{
@ -235,12 +252,8 @@ public final class HandlerResponse
/* The fully response message to send back */
JSONValue serverPayload;
/* Set the `scope` field of the header block */
JSONValue scopeField = "server";
/* Set the header of the response */
JSONValue headerBlock;
// headerBlock["handlerName"] = handler.getPluginName();
headerBlock["scope"] = "server";
serverPayload["header"] = headerBlock;
@ -252,6 +265,7 @@ public final class HandlerResponse
/* Attempt connecting to each server and sending the payload */
bool allSuccess = true;
for(ulong i = 0; i < servers.length; i++)
{
/* Get the current server address and port */
@ -281,10 +295,16 @@ public final class HandlerResponse
{
/* TODO: Be more specific with the above exception type */
debugPrint("Error whilst sending payload to server: " ~ e.toString());
allSuccess = false;
}
}
debugPrint("SEND_SERVERS: Completed run");
/**
* Send a status report here.
*/
originalRequester.sendStatusReport(cast(BesterConnection.StatusType)!allSuccess, messageID);
}
else if (commandType == CommandType.SEND_HANDLER)
{
@ -292,55 +312,14 @@ public final class HandlerResponse
string handler = messageResponse["header"]["command"]["data"].str();
debugPrint("Handler to forward to: " ~ handler);
/* Is built-in handler? */
bool isBuiltInHandler; /* TODO: Set value of this */
/* Lookup the payloadType handler */
MessageHandler chosenHandler = server.findHandler(handler);
if(isBuiltInHandler)
{
/**
* If it is a built-in handler then look in the `payloadBlock`
* for a field named ``*/
/* The command to be run */
Command command;
/* Depending on the built-in handler we do specifics */
if(cmp(handler, "GET_CLIENTS") == 0)
{
/* TODO: set `command` in here */
}
/* Run the built-in command */
JSONValue commandData = command.execute();
/**
* Construct the payload as a combination of the previous payload
* and the server's additions.
*/
JSONValue dataArray;
dataArray[0] = messageResponse["data"];
dataArray[1] = commandData;
/* Send the data to the message handler */
HandlerResponse handlerResponse = this.handler.handleMessage(dataArray);
/* Execute the code (this here, recursive) */
handlerResponse.execute(originalRequester);
}
else
{
/* Lookup the payloadType handler */
MessageHandler chosenHandler = server.findHandler(handler);
/* Send the data to the message handler */
HandlerResponse handlerResponse = chosenHandler.handleMessage(messageResponse["data"]);
/* Execute the code (this here, recursive) */
handlerResponse.execute(originalRequester);
}
/* TODO: Add me, shit is going to get recursive here */
/* Send the data to the message handler */
HandlerResponse handlerResponse = chosenHandler.handleMessage(messageResponse["data"]);
/* Execute the code (this here, recursive) */
handlerResponse.execute(originalRequester, messageID);
debugPrint("SEND_HANDLER: Completed run");
}
@ -356,6 +335,9 @@ public final class HandlerResponse
}
}
/**
* Represents an error in handling the response.
*/
public final class ResponseError : BesterException
{

View File

@ -10,20 +10,26 @@ import std.string : cmp;
import handlers.handler;
import server.server;
import connection.connection;
import base.types : BesterException;
/* TODO: Implement me */
/* All this will do is accept incoming connections
* but they will be pooled in the BesterServer.
*/
/**
* Represents a server listener which is a method
* by which conections to the server (client or server)
* can be made.
*/
public class BesterListener : Thread
{
/* The associated BesterServer */
private BesterServer server;
/* The server's socket */
private Socket serverSocket;
/* Whether or not the listener is active */
private bool active = true;
/* the address of this listener */
protected Address address;
this(BesterServer besterServer)
{
@ -34,19 +40,36 @@ public class BesterListener : Thread
this.server = besterServer;
}
/**
* Set the server socket.
*/
public void setServerSocket(Socket serverSocket)
{
/* Set the server socket */
this.serverSocket = serverSocket;
/* Set the address */
address = serverSocket.localAddress();
}
/**
* Get the server socket.
*/
public Socket getServerSocket()
{
return serverSocket;
}
/* Start listen loop */
/**
* Start listen loop.
*/
public void run()
{
serverSocket.listen(1); /* TODO: This value */
debugPrint("Server listen loop started");
while(true)
/* Loop receive and dispatch connections whilst active */
while(active)
{
/* Wait for an incoming connection */
Socket clientConnection = serverSocket.accept();
@ -56,8 +79,23 @@ public class BesterListener : Thread
besterConnection.start();
/* Add this client to the list of connected clients */
server.clients ~= besterConnection;
server.addConnection(besterConnection);
}
/* Close the socket */
serverSocket.close();
}
public void shutdown()
{
active = false;
}
}
public final class BesterListenerException : BesterException
{
this(BesterListener e)
{
super("Could not bind to: " ~ e.toString());
}
}

View File

@ -1,16 +1,26 @@
module listeners.types;
import listeners.listener;
import server.server;
import std.socket : Socket, Address, AddressFamily, SocketType;
import listeners.listener : BesterListener, BesterListenerException;
import server.server : BesterServer;
import std.socket : Socket, Address, AddressFamily, SocketType, SocketException;
/**
* Represents a stream socket listener over UNIX
* domain sockets.
*/
public final class UNIXListener : BesterListener
{
this(BesterServer besterServer, Address address)
{
super(besterServer);
setServerSocket(setupUNIXSocket(address));
try
{
setServerSocket(setupUNIXSocket(address));
}
catch(SocketException e)
{
throw new BesterListenerException(this);
}
}
private Socket setupUNIXSocket(Address address)
@ -19,14 +29,31 @@ public final class UNIXListener : BesterListener
unixSocket.bind(address);
return unixSocket;
}
override public string toString()
{
string address = "unix://"~super.address.toAddrString();
return address;
}
}
/**
* Represents a stream socket listener over TCP
* on IPv4.
*/
public final class TCP4Listener : BesterListener
{
this(BesterServer besterServer, Address address)
{
super(besterServer);
setServerSocket(setupTCP4Socket(address));
try
{
setServerSocket(setupTCP4Socket(address));
}
catch(SocketException e)
{
throw new BesterListenerException(this);
}
}
private Socket setupTCP4Socket(Address address)
@ -35,14 +62,31 @@ public final class TCP4Listener : BesterListener
tcp4Socket.bind(address);
return tcp4Socket;
}
override public string toString()
{
string address = "tcp4://"~super.address.toAddrString()~":"~super.address.toPortString();
return address;
}
}
/**
* Represents a stream socket listener over TCP
* on IPv6.
*/
public final class TCP6Listener : BesterListener
{
this(BesterServer besterServer, Address address)
{
super(besterServer);
setServerSocket(setupTCP6Socket(address));
try
{
setServerSocket(setupTCP6Socket(address));
}
catch(SocketException e)
{
throw new BesterListenerException(this);
}
}
private Socket setupTCP6Socket(Address address)
@ -51,5 +95,11 @@ public final class TCP6Listener : BesterListener
tcp6Socket.bind(address);
return tcp6Socket;
}
override public string toString()
{
string address = "tcp6://"~super.address.toAddrString()~":"~super.address.toPortString();
return address;
}
}

View File

@ -0,0 +1,23 @@
module server.accounts.base;
/**
* This represents the accounts management system of
* the server. It is only an abstract class.
*/
public abstract class BesterDataStore
{
/**
* Creates a new account with the given `username` and
* `password`.
*/
public abstract void createAccount(string username, string password);
/**
* Check if the user, `username`, exists in the database.
*/
public abstract bool userExists(string username);
public abstract bool authenticate(string username, string password);
public abstract void shutdown();
}

View File

@ -0,0 +1,118 @@
module server.accounts.redis;
import vibe.vibe;
import server.accounts.base : BesterDataStore;
import utils.debugging : debugPrint;
/**
* This represents a Redis datastore for the Bester
* server's account management system.
*/
public final class RedisDataStore : BesterDataStore
{
/**
* Redis client.
*/
private RedisClient redisClient;
/**
* Redis database with the account information
*/
private RedisDatabase redisDatabase;
this(string address, ushort port)
{
/* Opens a connection to the redis server */
initializeRedis(address, port);
}
private void initializeRedis(string address, ushort port)
{
redisClient = new RedisClient(address, port);
redisDatabase = redisClient.getDatabase(0);
// createAccount("deavmi", "poes");
}
override public bool userExists(string username)
{
/* TODO: Implement me */
return redisDatabase.exists(username);
// return true;
}
override public bool authenticate(string username, string password)
{
debugPrint(redisClient.info());
debugPrint(redisDatabase.keys("*"));
/* Check if a key exists with the `username` */
bool accountExists = redisDatabase.exists(username);
debugPrint(accountExists);
if(accountExists)
{
/**
* Check within the key if the subkey and value pair exists.
* `(username) [password: <password>], ...`
*/
if(redisDatabase.hexists(username, "password"))
{
/* Get the password sub-field */
string passwordDB = redisDatabase.hget(username, "password");
if(cmp(password, passwordDB) == 0)
{
return true;
}
else
{
return false;
}
}
else
{
/* TODO: Raise exception for missing password sub-key */
}
}
else
{
/* TODO: Raise exception for non-existent account */
}
/* TODO: Remove */
return false;
}
override public void createAccount(string username, string password)
{
/* TODO: Implement me */
/* Check if a key exists with the `username` */
bool accountExists = redisDatabase.exists(username);
if(!accountExists)
{
/**
* Create the new account.
* This involves creating a new key named `username`
* with a field named `"password"` matching to the value
* of `password`.
*/
redisDatabase.hset(username, "password", password);
}
else
{
/* TODO: Raise exception for an already existing account */
}
}
public void f()
{
}
override public void shutdown()
{
/* TODO: Should we shutdown the server? */
redisClient.shutdown();
}
}

View File

@ -3,14 +3,21 @@ module server.informer.client;
import core.thread : Thread;
import server.server : BesterServer;
import std.socket;
import bmessage;
import utils.message : receiveMessage, sendMessage;
import std.json;
import utils.debugging;
import std.string;
import server.informer.utils;
import std.conv : to;
import connection.connection : BesterConnection;
public class BesterInformerClient : Thread
/**
* Represents a handler's connection to the
* Bester informer socket, runs as a seperate
* thread with a read, dispatch, write loop
* to handle commands and responses to the handler
* from the server.
*/
public final class BesterInformerClient : Thread
{
/* The associated `BesterServer` */
private BesterServer server;
@ -18,6 +25,14 @@ public class BesterInformerClient : Thread
/* The socket to the handler */
private Socket handlerSocket;
/* If the connection is still active or not */
private bool active = true;
/**
* Constructs a new `BesterInformerClient` with the
* associated BesterServer, `server`, and handler
* socket, `handlerSocket`.
*/
this(BesterServer server, Socket handlerSocket)
{
super(&worker);
@ -25,6 +40,10 @@ public class BesterInformerClient : Thread
this.handlerSocket = handlerSocket;
}
/**
* Run's the command specified in `commandBlock` and sets the
* response in the variable pointed to by `result`.
*/
private bool runCommand(JSONValue commandBlock, ref JSONValue result)
{
try
@ -46,7 +65,19 @@ public class BesterInformerClient : Thread
string username = commandBlock["data"].str();
result = isClient(server, username);
}
/* TODO: Add any more new command here */
/* Check if the command is `serverInfo` */
else if(cmp(commandType, "serverInfo") == 0)
{
result = getServerInfo(server);
}
/* Check if the command is `quit` */
else if (cmp(commandType, "quit") == 0)
{
/* Set the connection to inactive */
active = false;
result = null; /* TODO: JSOn default value */
}
/* If the command is invalid */
else
{
result = null;
@ -65,7 +96,7 @@ public class BesterInformerClient : Thread
private void worker()
{
/* TODO: Implement me */
while(1)
while(active)
{
/* Receive a message */
JSONValue handlerCommand;
@ -84,8 +115,7 @@ public class BesterInformerClient : Thread
string commandType = handlerCommand["command"]["type"].str();
debugPrint("Command: " ~ commandType);
/* Dispatch to the correct command and return a status */
bool commandStatus = runCommand(handlerCommand["command"], runCommandData);
/* Set the data */
@ -102,8 +132,86 @@ public class BesterInformerClient : Thread
/* Send the response to the handler */
sendMessage(handlerSocket, handlerResponse);
}
/* Close the socket */
handlerSocket.close();
}
/**
* Shutdown the informer client.
*/
public void shutdown()
{
active = false;
}
/**
* This functions returns `string[]` where each element
* contains the username of the locally connected client.
*/
public static string[] listClients(BesterServer server)
{
string[] clientList;
for(ulong i = 0; i < server.clients.length; i++)
{
/* Make sure only to add client connections */
BesterConnection connection = server.clients[i];
if(connection.getType() == BesterConnection.Scope.CLIENT)
{
clientList ~= [connection.getCredentials()[0]];
}
}
return clientList;
}
/**
* This function returns `true` if the provided username
* matches a locally connected client, `false` otherwise.
*/
public static bool isClient(BesterServer server, string username)
{
for(ulong i = 0; i < server.clients.length; i++)
{
/* Make sure only to match client connections */
BesterConnection connection = server.clients[i];
if(connection.getType() == BesterConnection.Scope.CLIENT && cmp(connection.getCredentials[0], username))
{
return true;
}
}
return false;
}
/**
* This function returns server information.
*/
public static JSONValue getServerInfo(BesterServer server)
{
/* Server information */
JSONValue serverInfo;
/* Create the `listeners` block */
JSONValue listenersBlock;
for(ulong i = 0; i < server.listeners.length; i++)
{
JSONValue listener;
listener["address"] = server.listeners[i].toString();
listenersBlock["listener"~to!(string)(i)] = listener;
}
/* TODO: Load additional information from `server.conf`'s `admin[info]` block */
/* TODO: Use as is number, no string */
serverInfo["clientCount"] = to!(string)(server.clients.length);
serverInfo["adminInfo"] = server.getAdminInfo();
serverInfo["listeners"] = listenersBlock;
return serverInfo;
}
}

View File

@ -20,6 +20,19 @@ public final class BesterInformer : Thread
/* Informer socket */
private Socket informerSocket;
/* Whether or not the informer server is active */
private bool active = true;
/* Connected clients */
private BesterInformerClient[] informerClients;
/**
* Constructs a new `BesterInformer` object
* which will accept incoming connections
* (upon calling `.start`) from handlers,
* over a UNIX domain socket, requesting
* server information.
*/
this(BesterServer server)
{
/* Set the worker function */
@ -30,19 +43,41 @@ public final class BesterInformer : Thread
informerSocket.bind(new UnixAddress("bInformer"));
informerSocket.listen(1); /* TODO: Value */
/* Set the associated BesterServer */
this.server = server;
}
/**
* Client send-receive loop.
* Accepts incoming connections to the informer
* and dispatches them to a worker thread.
*/
private void worker()
{
while(1)
while(active)
{
Socket handler = informerSocket.accept();
/* Accept the queued incoming connection */
Socket handlerSocket = informerSocket.accept();
BesterInformerClient newInformer = new BesterInformerClient(server, handler);
/**
* Create a new worker for the informer client, adds it
* to the client queue and dispatches its worker thread.
*/
BesterInformerClient newInformer = new BesterInformerClient(server, handlerSocket);
informerClients ~= newInformer;
newInformer.start();
}
/* Close the informer socket */
informerSocket.close();
}
public void shutdown()
{
for(ulong i = 0; i < informerClients.length; i++)
{
informerClients[i].shutdown();
}
}
}

View File

@ -1,45 +0,0 @@
module server.informer.utils;
import server.server : BesterServer;
import connection.connection : BesterConnection;
import std.string : cmp;
/**
* This functions returns `string[]` where each element
* contains the username of the locally connected client.
*/
public static string[] listClients(BesterServer server)
{
string[] clientList;
for(ulong i = 0; i < server.clients.length; i++)
{
/* Make sure only to add client connections */
BesterConnection connection = server.clients[i];
if(connection.getType() == BesterConnection.Scope.CLIENT)
{
clientList ~= [connection.getCredentials()[0]];
}
}
return clientList;
}
/**
* This function returns `true` if the provided username
* matches a locally connected client, `false` otherwise.
*/
public static bool isClient(BesterServer server, string username)
{
for(ulong i = 0; i < server.clients.length; i++)
{
/* Make sure only to match client connections */
BesterConnection connection = server.clients[i];
if(connection.getType() == BesterConnection.Scope.CLIENT && cmp(connection.getCredentials[0], username))
{
return true;
}
}
return false;
}

View File

@ -4,6 +4,7 @@ import utils.debugging : debugPrint;
import std.conv : to;
import std.socket : Socket, AddressFamily, SocketType, ProtocolType, parseAddress;
import core.thread : Thread;
import core.sync.mutex;
import std.stdio : writeln, File;
import std.json : JSONValue, parseJSON, JSONException, JSONType, toJSON;
import std.string : cmp, strip;
@ -11,30 +12,52 @@ import handlers.handler : MessageHandler;
import listeners.listener : BesterListener;
import connection.connection : BesterConnection;
import server.informer.informer : BesterInformer;
import server.accounts.base : BesterDataStore;
import server.accounts.redis : RedisDataStore;
/**
* Represents an instance of a Bester server.
*/
public final class BesterServer
{
/**
* Message handlers
*
* Associative array of `payloadType (string)`:`MessageHandler`
* TODO: Implement this
* Array of message handlers attached to
* this server.
*/
public MessageHandler[] handlers;
/* The server's socket */
/**
* The server's socket.
*/
private Socket serverSocket;
/* TODO: The above to be replaced */
/* Socket listeners for incoming connections */
private BesterListener[] listeners;
/**
* Socket listeners for incoming connections.
*/
public BesterListener[] listeners;
/* Connected clients */
/**
* Connected clients.
*/
public BesterConnection[] clients;
private Mutex clientsMutex;
/* The informer server */
/**
* The informer server.
*/
private BesterInformer informer;
/**
* Admin information regarding this server.
*/
private JSONValue adminInfo;
/**
* The datastore for the account information.
*/
private BesterDataStore dataStore;
/**
* Returns a list of BesterConnection objects that
* match the usernames provided.
@ -67,11 +90,44 @@ public final class BesterServer
return matchedUsers;
}
/**
* Adds a new Connection, `connection`, to the server.
*/
public void addConnection(BesterConnection connection)
{
/**
* Lock the mutex so that only one listener thread
* may access the array at a time.
*/
clientsMutex.lock();
/**
* Append the connection to the array
*/
clients ~= connection;
/**
* Release the mutex so other listeners can now append
* to the array.
*/
clientsMutex.unlock();
}
/* TODO: Add more thread sfaety here and abroad */
/**
* Adds a listener, `listener`, to this server's
* listener set.
*/
public void addListener(BesterListener listener)
{
this.listeners ~= listener;
}
/**
* Constructs a new BesterServer with the given
* JSON configuration.
*/
this(JSONValue config)
{
/* TODO: Bounds check and JSON type check */
@ -81,8 +137,50 @@ public final class BesterServer
/* TODO: Bounds check and JSON type check */
debugPrint("Setting up message handlers...");
setupHandlers(config["handlers"]);
setupDatabase(config["database"]);
/* Initialize the `clients` array mutex */
clientsMutex = new Mutex();
}
/* TODO: Add comment, implement me */
private void setupDatabase(JSONValue databaseBlock)
{
/* Get the type */
string dbType = databaseBlock["type"].str();
if(cmp(dbType, "redis") == 0)
{
/* get the redis block */
JSONValue redisBlock = databaseBlock["redis"];
/* Get information */
string address = redisBlock["address"].str();
ushort port = to!(ushort)(redisBlock["port"].str());
/* Create the redis datastore */
// dataStore = new RedisDataStore(address, port);
// dataStore.createAccount("bruh","fdgdg");
// writeln("brdfsfdhjk: ", dataStore.userExists("bruh"));
// writeln("brfdddhjk: ", dataStore.userExists("brsdfuh"));
// writeln("brfddhjk: ", dataStore.userExists("bradsfuh"));
// writeln("brfddhjk: ", dataStore.userExists("brasuh"));
// writeln("brfdhdgfgsfdsgfdgfdsjk: ", dataStore.userExists("brdfsauh"));
// writeln("brfdhjk: ", dataStore.userExists("brfdsasuh"));
// writeln("brfdhjk: ", dataStore.userExists("brasuh"));
// writeln("brfdhjk: ", dataStore.userExists("brsauh"));
// writeln("brfdhjk: ", dataStore.userExists("brsaasuh"));
// writeln("brfdhjk: ", dataStore.userExists("brusaasfh"));
// writeln("fhdjfhjdf");
// dataStore.authenticate("dd","dd");
}
}
/**
* Given JSON, `handlerBlock`, this will setup the
* relevant message handlers.
*/
private void setupHandlers(JSONValue handlerBlock)
{
/* TODO: Implement me */
@ -90,7 +188,9 @@ public final class BesterServer
handlers = MessageHandler.constructHandlers(this, handlerBlock);
}
/* Setup the server socket */
/**
* Setup the server socket.
*/
private void setupServerSocket(JSONValue networkBlock)
{
string bindAddress;
@ -114,6 +214,9 @@ public final class BesterServer
serverSocket.bind(parseAddress(bindAddress, listenPort));
}
/**
* Starts all the listeners.
*/
private void startListeners()
{
for(ulong i = 0; i < listeners.length; i++)
@ -132,7 +235,9 @@ public final class BesterServer
informer.start();
}
/* Start listen loop */
/**
* Start listen loop.
*/
public void run()
{
/* Start the listeners */
@ -142,7 +247,9 @@ public final class BesterServer
startInformer();
}
/* Authenticate the user */
/**
* Authenticate the user.
*/
public bool authenticate(string username, string password)
{
/* TODO: Implement me */
@ -157,17 +264,22 @@ public final class BesterServer
/* Make sure username and password are not empty */
if(cmp(username, "") != 0 && cmp(password, "") != 0)
{
/* TODO: Fix me */
//authed = dataStore.authenticate(username, password);
}
else
{
authed = false;
}
debugPrint("Auth" ~ to!(string)(authed));
return authed;
}
/* Returns the MessageHandler object of the requested type */
/**
* Returns the MessageHandler object of the requested type.
*/
public MessageHandler findHandler(string payloadType)
{
/* The found MessageHandler */
@ -192,6 +304,53 @@ public final class BesterServer
return isBuiltIn;
}
}
/**
* Stops the server.
*/
private void shutdown()
{
/* Stop the informer service */
informer.shutdown();
/* Shutdown all the listeners */
shutdownListeners();
/* Shutdown all the clients */
shutdownClients();
/* Shutdown the datastore */
dataStore.shutdown();
}
/**
* Loops through the list of `BesterListener`s and
* shuts each of them down.
*/
private void shutdownListeners()
{
/* Shutdown all the listeners */
for(ulong i = 0; i < listeners.length; i++)
{
listeners[i].shutdown();
}
}
/**
* Loops through the list of `BesterConnection`s and
* shuts each of them down.
*/
private void shutdownClients()
{
/* Shutdown all the clients */
for(ulong i = 0; i < clients.length; i++)
{
clients[i].shutdown();
}
}
public JSONValue getAdminInfo()
{
return adminInfo;
}
}

View File

@ -14,10 +14,15 @@ import bmessage : bformatreceiveMessage = receiveMessage, bformatsendMessage = s
*/
public void receiveMessage(Socket originator, ref JSONValue receiveMessage)
{
if(!bformatreceiveMessage(originator, receiveMessage))
/* The received bytes */
byte[] receivedBytes;
if(!bformatreceiveMessage(originator, receivedBytes))
{
throw new NetworkException(originator);
}
receiveMessage = parseJSON(cast(string)receivedBytes);
}
/**
@ -31,8 +36,11 @@ public void receiveMessage(Socket originator, ref JSONValue receiveMessage)
*/
public void sendMessage(Socket recipient, JSONValue jsonMessage)
{
if(!bformatsendMessage(recipient, jsonMessage))
if(!bformatsendMessage(recipient, cast(byte[])toJSON(jsonMessage)))
{
throw new NetworkException(recipient);
}
}
}
/* TODO: Hash message: Next-gen implementation */

View File

@ -8,18 +8,24 @@ def sendAs(username):
d.connect(("127.0.0.1",2223))
# First do it and authenticate
bys=json.dumps({"header":{"authentication":{"username":username, "password":"passwd"}, "scope":"client"},"payload":{"data":{"bruhMsg":input("Enter message naaier: ")},"type":"type1"}})
bys=json.dumps({"header":{"authentication":{"username":username, "password":"passwd"}, "scope":"client"},"payload":{"data":{"bruhMsg":input("Enter message naaier: ")},"type":"type1", "id" : "lol"}})
print(len(bys), bys)
d.send(len(bys).to_bytes(4, "little"))
d.send(bys.encode())
# Loop prompt and sending of message to tbk
while True:
bys=json.dumps({"header":{"authentication":{"username":"ddd", "password":"passwd"}, "scope":"client"},"payload":{"data":{"bruhMsg":input("Enter message naaier: ")},"type":"type1"}})
bys=json.dumps({"header":{"authentication":{"username":"ddd", "password":"passwd"}, "scope":"client"},"payload":{"data":{"bruhMsg":input("Enter message naaier: ")},"type":"type1", "id":"lol"}})
print(len(bys), bys)
d.send(len(bys).to_bytes(4, "little"))
d.send(bys.encode())
length=int.from_bytes(list(d.recv(4)), "little")
print(length)
receivedDataBytes = d.recv(length)
receivedData = list(receivedDataBytes)
print(receivedDataBytes.decode())
# Now we can do it again (without authentication)

View File

@ -24,8 +24,8 @@ def runTest():
bys = json.dumps({
"header" : {
"status" : "0",
"command" : {"type" : "sendServers", "data": ["10.1.0.7:2223"]}
}, "data" : receivedBys })
"command" : {"type" : "sendClients", "data": ["deavmi"]}
}, "data" : "length of your data: " + str(len(receivedBys)) + "\"" + str(receivedBys) + "\"" })
print(s.send(len(bys).to_bytes(4, "little")))
print(s.send(bys.encode()))