Compare commits
80 Commits
Author | SHA1 | Date |
---|---|---|
Tristan B. Kildaire | 0f75841600 | |
Tristan B. Kildaire | 5fc42a6ef9 | |
Tristan B. Kildaire | 72139e70c3 | |
Tristan B. Kildaire | ac56eb7689 | |
Tristan B. Kildaire | d19b552997 | |
Tristan B. Kildaire | 261448159f | |
Tristan B. Kildaire | c4a4d46264 | |
Tristan B. Kildaire | c7f701e8f6 | |
Tristan B. Kildaire | b783d98ea7 | |
Tristan B. Kildaire | 5c13fa3c77 | |
Tristan B. Kildaire | 60cd993472 | |
Tristan B. Kildaire | aec7144106 | |
Tristan B. Kildaire | 9db02f607b | |
Tristan B. Kildaire | c646b4bf20 | |
Tristan B. Kildaire | 88af27c3cb | |
Tristan B. Kildaire | 2b54142439 | |
Tristan B. Kildaire | 0b5ca124d6 | |
Tristan B. Kildaire | 71632ec1f3 | |
Tristan B. Kildaire | 80b0772fd8 | |
Tristan B. Kildaire | 32d35cec71 | |
Tristan B. Kildaire | a22b65f7c4 | |
Tristan B. Kildaire | 615ebc823a | |
Tristan B. Kildaire | 8d0256d105 | |
Tristan B. Kildaire | 4e091e2f2d | |
Tristan B. Kildaire | 06c406ef86 | |
Tristan B. Kildaire | 00c16b68da | |
Tristan B. Kildaire | 1ade3365b1 | |
Tristan B. Kildaire | 95577698b8 | |
Tristan B. Kildaire | be2953595d | |
Tristan B. Kildaire | ea25aabf26 | |
Tristan B. Kildaire | 2993f74b05 | |
Tristan B. Kildaire | cc934e5ec8 | |
Tristan B. Kildaire | e0e7d5ffd0 | |
Tristan B. Kildaire | 89f51e5944 | |
Tristan B. Kildaire | fc33bd0b69 | |
Tristan B. Kildaire | 955cc6d21d | |
Tristan B. Kildaire | 9f1e63367c | |
Tristan B. Kildaire | a9ee569314 | |
Tristan B. Kildaire | 4442c62ae7 | |
Tristan B. Kildaire | 70df082f9a | |
Tristan B. Kildaire | 27ada909d6 | |
Tristan B. Kildaire | 1d7f0b8ac2 | |
Tristan B. Kildaire | 3e570e8066 | |
Tristan B. Kildaire | 79cfe50353 | |
Tristan B. Kildaire | 007e15caa4 | |
Tristan B. Kildaire | cc359a14f0 | |
Tristan B. Kildaire | d5e3a86842 | |
Tristan B. Kildaire | bd4846bf61 | |
Tristan B. Kildaire | d19a7809ca | |
Tristan B. Kildaire | 23bcda9b3c | |
Tristan B. Kildaire | b862ba7270 | |
Tristan B. Kildaire | 4d21754fff | |
Tristan B. Kildaire | 1b2c4cabe9 | |
Tristan B. Kildaire | 48d58f26b7 | |
Tristan B. Kildaire | ef272edc77 | |
Tristan B. Kildaire | 9c853b22c3 | |
Tristan B. Kildaire | 9f565e9c09 | |
Tristan B. Kildaire | 115f8f25fd | |
Tristan B. Kildaire | e29bb80e5e | |
Tristan B. Kildaire | 7972eb3235 | |
Tristan B. Kildaire | 8f0ddd766a | |
Tristan B. Kildaire | 95de9c25d1 | |
Tristan B. Kildaire | c300c9995f | |
Tristan B. Kildaire | e6ddbaac62 | |
Tristan B. Kildaire | a042dc4a0d | |
Tristan B. Kildaire | ab64098b6f | |
Tristan B. Kildaire | 185b7e86e3 | |
Tristan B. Kildaire | 0e0be00b85 | |
Tristan B. Kildaire | c31e67dd72 | |
Tristan B. Kildaire | fdcb23ddf8 | |
Tristan B. Kildaire | 2003505979 | |
Tristan B. Kildaire | 5a5241a0c8 | |
Tristan B. Kildaire | bd409551db | |
Tristan B. Kildaire | eeaba93bb2 | |
Tristan B. Kildaire | c0b328aaff | |
Tristan B. Kildaire | 28a043dc29 | |
Tristan B. Kildaire | 7864a60fff | |
Tristan B. Kildaire | a3d71c7267 | |
Tristan B. Kildaire | f58ad52ca5 | |
Tristan B. Kildaire | de51e7eff3 |
3
dub.json
3
dub.json
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
10
server.conf
10
server.conf
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"network" : {
|
||||
"types" : ["unix", "tcp4", "tcp6"],
|
||||
"types" : ["tcp4", "tcp6"],
|
||||
"unix" : {
|
||||
"address" : "besterUNIXSock"
|
||||
},
|
||||
|
@ -20,6 +20,14 @@
|
|||
"type2" : {"handlerBinary" : "./testing/unixSock2.py", "unixDomainSocketPath" : "bSock"}
|
||||
}
|
||||
},
|
||||
"database" : {
|
||||
"type" : "redis",
|
||||
"redis" : {
|
||||
"address" : "127.0.0.1",
|
||||
"port" : "6379"
|
||||
}
|
||||
|
||||
},
|
||||
"admin" : {
|
||||
"info" : {
|
||||
|
||||
|
|
50
source/app.d
50
source/app.d
|
@ -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;
|
||||
|
||||
|
@ -67,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;
|
||||
}
|
||||
|
|
|
@ -63,6 +63,10 @@ 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 */
|
||||
|
@ -74,6 +78,9 @@ public final class BesterConnection : Thread
|
|||
return username ~ "@" ~ clientConnection.remoteAddress().toAddrString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the username and password.
|
||||
*/
|
||||
public string[] getCredentials()
|
||||
{
|
||||
return [username, password];
|
||||
|
@ -83,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;
|
||||
|
@ -103,7 +110,7 @@ public final class BesterConnection : Thread
|
|||
if(connectionType == Scope.SERVER)
|
||||
{
|
||||
debugPrint("Server connection done, closing BesterConnection.");
|
||||
isActive = false;
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
catch(BesterException exception)
|
||||
|
@ -146,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);
|
||||
|
@ -166,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)
|
||||
|
@ -188,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) */
|
||||
|
@ -211,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
|
||||
{
|
||||
|
@ -293,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".
|
||||
*
|
||||
|
@ -330,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)
|
||||
|
@ -367,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
|
||||
|
@ -378,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;
|
||||
}
|
||||
}
|
||||
|
@ -393,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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) */
|
||||
|
@ -29,7 +32,9 @@ private enum CommandType : ubyte
|
|||
*/
|
||||
public final class HandlerResponse
|
||||
{
|
||||
/* The message-handler's response */
|
||||
/**
|
||||
* The message-handler's response.
|
||||
*/
|
||||
private JSONValue messageResponse;
|
||||
|
||||
/* The command to be executed */
|
||||
|
@ -41,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 */
|
||||
|
@ -149,10 +158,8 @@ public final class HandlerResponse
|
|||
* Executes the command. Either `sendClients`, `sendServers`
|
||||
* or `sendHandler`.
|
||||
*/
|
||||
public void execute(BesterConnection originalRequester)
|
||||
public void execute(BesterConnection originalRequester, string messageID)
|
||||
{
|
||||
/* TODO: Implement me */
|
||||
|
||||
/* If the command is SEND_CLIENTS */
|
||||
if(commandType == CommandType.SEND_CLIENTS)
|
||||
{
|
||||
|
@ -170,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 */
|
||||
|
@ -190,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 */
|
||||
|
@ -213,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)
|
||||
{
|
||||
|
@ -242,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;
|
||||
|
||||
|
@ -259,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 */
|
||||
|
@ -288,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)
|
||||
{
|
||||
|
@ -306,7 +319,7 @@ public final class HandlerResponse
|
|||
HandlerResponse handlerResponse = chosenHandler.handleMessage(messageResponse["data"]);
|
||||
|
||||
/* Execute the code (this here, recursive) */
|
||||
handlerResponse.execute(originalRequester);
|
||||
handlerResponse.execute(originalRequester, messageID);
|
||||
|
||||
debugPrint("SEND_HANDLER: Completed run");
|
||||
}
|
||||
|
@ -322,6 +335,9 @@ public final class HandlerResponse
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an error in handling the response.
|
||||
*/
|
||||
public final class ResponseError : BesterException
|
||||
{
|
||||
|
||||
|
|
|
@ -10,15 +10,15 @@ 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;
|
||||
|
||||
|
@ -28,6 +28,9 @@ public class BesterListener : Thread
|
|||
/* Whether or not the listener is active */
|
||||
private bool active = true;
|
||||
|
||||
/* the address of this listener */
|
||||
protected Address address;
|
||||
|
||||
this(BesterServer besterServer)
|
||||
{
|
||||
/* Set the function address to be called as the worker function */
|
||||
|
@ -37,18 +40,35 @@ 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");
|
||||
|
||||
/* Loop receive and dispatch connections whilst active */
|
||||
while(active)
|
||||
{
|
||||
/* Wait for an incoming connection */
|
||||
|
@ -59,7 +79,7 @@ public class BesterListener : Thread
|
|||
besterConnection.start();
|
||||
|
||||
/* Add this client to the list of connected clients */
|
||||
server.clients ~= besterConnection;
|
||||
server.addConnection(besterConnection);
|
||||
}
|
||||
|
||||
/* Close the socket */
|
||||
|
@ -70,5 +90,12 @@ public class BesterListener : Thread
|
|||
{
|
||||
active = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public final class BesterListenerException : BesterException
|
||||
{
|
||||
this(BesterListener e)
|
||||
{
|
||||
super("Could not bind to: " ~ e.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -3,12 +3,12 @@ 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;
|
||||
|
||||
/**
|
||||
* Represents a handler's connection to the
|
||||
|
@ -28,6 +28,11 @@ public final class BesterInformerClient : Thread
|
|||
/* 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);
|
||||
|
@ -72,7 +77,6 @@ public final class BesterInformerClient : Thread
|
|||
active = false;
|
||||
result = null; /* TODO: JSOn default value */
|
||||
}
|
||||
/* TODO: Add any more new command here */
|
||||
/* If the command is invalid */
|
||||
else
|
||||
{
|
||||
|
@ -134,9 +138,80 @@ public final class BesterInformerClient : Thread
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
module server.informer.utils;
|
||||
|
||||
import server.server : BesterServer;
|
||||
import connection.connection : BesterConnection;
|
||||
import std.string : cmp;
|
||||
import std.json : JSONValue;
|
||||
import std.conv : to;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/* 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);
|
||||
|
||||
return serverInfo;
|
||||
}
|
|
@ -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 */
|
||||
|
@ -206,6 +318,9 @@ public final class BesterServer
|
|||
|
||||
/* Shutdown all the clients */
|
||||
shutdownClients();
|
||||
|
||||
/* Shutdown the datastore */
|
||||
dataStore.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -233,6 +348,9 @@ public final class BesterServer
|
|||
clients[i].shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public JSONValue getAdminInfo()
|
||||
{
|
||||
return adminInfo;
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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)
|
||||
|
|
|
@ -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()))
|
||||
|
||||
|
|
Loading…
Reference in New Issue