Start a new

This commit is contained in:
Tristan B. Velloza Kildaire 2022-05-25 00:30:37 +02:00
parent 051595a377
commit 4fde6b1a6b
11 changed files with 18 additions and 540 deletions

View File

@ -4,9 +4,8 @@
],
"copyright": "Copyright © 2021, Tristan B. Kildaire",
"dependencies": {
"dlog": "~>0.0.5",
"gogga": "~>0.1.3",
"jcli": "~>0.24.0"
"dlog": "~>0.0.6",
"tasky": "~>1.2.0"
},
"description": "Official implementation of the DNET protocol for the server-side",
"license": "AGPLv3",

10
dub.selections.json Normal file
View File

@ -0,0 +1,10 @@
{
"fileVersion": 1,
"versions": {
"bformat": "3.1.3",
"dlog": "0.0.6",
"eventy": "0.2.4",
"tasky": "1.2.0",
"tristanable": "2.6.12"
}
}

6
source/app.d Normal file
View File

@ -0,0 +1,6 @@
import std.stdio;
void main()
{
writeln("Hello world");
}

View File

@ -1,37 +0,0 @@
/**
* Main module
*/
module dnetd.app;
import dlog;
import dnetd.exceptions : GeneralException;
import std.json : JSONValue;
import dnetd.config : Configuration;
import dnetd.server : Server;
public Logger logger;
string VERSION = "0.0.1";
void main()
{
/* Setup the logger */
logger = new DefaultLogger();
logger.log("Welcome to dnetd v"~VERSION);
/* TODO: Add jcli handling here */
try
{
Configuration config = Configuration.getConfig("config.json");
/* TODO: Server init with config here */
Server server = new Server(config);
}
catch(GeneralException e)
{
logger.log(e.toString());
}
}

View File

@ -1,225 +0,0 @@
/**
* Configuration sub-system
*/
module dnetd.config;
import dnetd.app : logger;
import std.json : JSONValue, JSONException, parseJSON;
import dnetd.exceptions;
import std.stdio : File;
import std.string : cmp, strip;
/**
* In its instance-form this represents a read-in and parsed configuration
* of the configuration file in an OOP format, with the ability to write back
* any modifications made to it (TODO: Add this).
*
* In static use it provides the ability to translate between the JSON configuration
* file and the instance (OOP) form.
*/
public final class Configuration
{
string configPath;
NetworkInformation netInfo;
public static Configuration getConfig(string configPath)
{
JSONValue jsonConfig = readConfig("config.json");
Configuration config = Configuration.fromJSON(jsonConfig);
/* Save the path so we can rehash later on */
config.configPath = configPath;
return config;
}
/**
* Reads in the JSON from the given path to the configuration
* file
*
* On error throws TODO
*/
private static JSONValue readConfig(string path)
{
File file;
file.open(path); /* TODO:Check this for errors */
/* TODO: Only open with read rights */
/* Allocate a buffer for the file */
byte[] contents;
contents.length = file.size(); /* TODO: Check size here */
/* TODO: Check this */
/* TODO: Technically the below is fine */
file.rawRead(contents);
JSONValue config;
try
{
config = parseJSON(cast(string)contents);
}
catch(JSONException e)
{
/* TODO: Get specific error here to show where config syntax is wrong */
throw new ConfigurationError(e);
}
return config;
}
/**
* Load the configuration from a JSON source, returning the
* configuration as a Configuration object, on error, null (TODO: Throw exception rather)
*
* @param jsonConfig the JSONValue configuration
*/
private static Configuration fromJSON(JSONValue jsonConfig)
{
Configuration config = new Configuration();
try
{
/* TODO: Parse config here */
/* Retrieve the `network` block */
JSONValue networkBlock = jsonConfig["network"];
config.netInfo = confNetInfo(networkBlock["info"]);
// TODO: Log the above logger.log()
/* Retrieve the `accounting` block */
JSONValue accountingBlock = jsonConfig["accounting"];
/* Retrieve the `links` block */
JSONValue linksBlock = jsonConfig["links"];
}
catch(JSONException e)
{
/* TODO: Throw proper error here saying which key is missing */
throw new ConfigurationError(e);
}
return config;
}
private static NetworkInformation confNetInfo(JSONValue infoBlock)
{
NetworkInformation netInfo;
/**
* TODO (keycheck): Make sure that `serverName` is present
* and `networkName`
*
* `motd` is optional
*/
bool foundNetworkName;
bool foundServerName;
string[] keys = infoBlock.object.keys();
foreach(string key; keys)
{
if(cmp(key, "serverName") == 0)
{
foundServerName = true;
netInfo.serverName = infoBlock[key].str();
}
else if(cmp(key, "networkName") == 0)
{
foundNetworkName = true;
netInfo.networkName = infoBlock[key].str();
}
}
/**
* Make sure we found the required fields
*
* These are: serverName, networkName, sid (TODO: Finish these)
*/
if(!foundServerName)
{
throw new ConfigurationError("Missing server name");
}
else if(!foundNetworkName)
{
throw new ConfigurationError("Missing network name");
}
/**
* Strip whitespace from the beginnings and endings of
* `serverName`, `networkName`, (TODO: Complete list)
*/
netInfo.serverName = strip(netInfo.serverName);
netInfo.networkName = strip(netInfo.networkName);
/**
* Make sure the required fields were set to a valid
* value
*/
if(cmp(netInfo.networkName, "") == 0)
{
throw new ConfigurationError("Network name cannot be empty");
}
else if(cmp(netInfo.serverName, "") == 0)
{
throw new ConfigurationError("Server name cannot be empty");
}
/* TODO: Add handling for motd and motdFile */
return netInfo;
}
private this()
{
}
}
/**
* Configuration error
*/
public final class ConfigurationError : GeneralException
{
this(string message)
{
super(message);
}
this(JSONException e)
{
super("JSON configuration has a syntax error ("~e.msg~")");
}
}
/**
* Network information
*
* This holds information that pertains to network-wide
* information (such as the network's name) and server-specific
* information such as the message-of-the-day or the server's
* name
*/
struct NetworkInformation
{
string serverName;
string motd;
ushort sid;
string networkName;
}

View File

@ -1,36 +0,0 @@
/**
* Connection handling sub-system
*/
module dnetd.connection.connection;
import core.thread : Thread;
import dnetd.server : Server;
/**
* Represents a client's/server's connection to
* this server
*
* These are normally spawned by the `serviceLoop`
* of Listener sub-classes
*/
public abstract class Connection : Thread
{
/**
* The server instance this Connection
* is associated with
*/
private Server server;
this(Server server)
{
super(&handler);
this.server = server;
}
/**
* Connection handler
*
* This is to be implemented by sub-classes
*/
public abstract void handler();
}

View File

@ -1,30 +0,0 @@
/**
* Socket-based connection handler sub-system
*/
import dnetd.connection.connection : Connection;
import std.socket;
import dnetd.server : Server;
/**
* FIXME: When we do anything so far, I am assuming
* a streaming socket so we shouldn't let the
* SocketListener use any SocketType that isn't STREAM
*/
public final class SocketConnection : Connection
{
private Socket socket;
this(Server server, Socket socket)
{
super(server);
this.socket = socket;
}
public override void handler()
{
while(true)
{
}
}
}

View File

@ -1,33 +0,0 @@
/**
* Exceptions sub-system
*/
module dnetd.exceptions;
import std.exception;
/**
* Base class for all DNETD exceptions
*/
public class GeneralException : Exception
{
/* Name of exception type and description */
private string errorTypeName;
private string message;
/**
* TODO: Make this take in a `string message`
*/
this(string message)
{
import std.string : split;
string[] fragments = split(this.classinfo.name, ".");
errorTypeName = fragments[fragments.length-1];
this.message = message;
super(null);
}
public override string toString()
{
return errorTypeName~": "~message;
}
}

View File

@ -1,42 +0,0 @@
/**
* Module for listeners
*
* These include the base listener class responsible
* for managing a connection with peers and some
* concrete classes implementing this for various
* network protocols
*/
module dnetd.listeners.listeners;
import dnetd.server : Server;
import dnetd.exceptions : GeneralException;
import std.exception;
import core.thread : Thread;
public abstract class Listener : Thread
{
private Server server;
this(Server server)
{
super(&serviceLoop);
this.server = server;
}
/**
* The connection accepting loop of which
* is provided as the "worker" function to
* the thread
*/
public abstract void serviceLoop();
}
public abstract class ListenerException : GeneralException
{
/* TODO: Potentially remove `listener` */
this(Listener listener, string msg)
{
/* TODO: Set message here */
super(msg);
}
}

View File

@ -1,65 +0,0 @@
/**
* Socket-based listener for supporting any
* network protocol that Linux supports, such as
* TCP (STREAM-based) on IPv4 and IPv6 and UNIX
* domain sockets... to name a few.
*/
module dnetd.listeners.socket;
import dnetd.listeners.listeners;
import dnetd.server : Server;
import std.socket;
import std.conv : to;
public final class SocketListenerException : ListenerException
{
this(SocketListener e)
{
string msg = to!(string)(e.getAddress().addressFamily)
~
"-type socket listener error";
super(e, msg);
}
}
public final class SocketListener : Listener
{
private Socket socket;
private Address address;
this(Server server, Address address, SocketType type, ProtocolType proto)
{
super(server);
this.address = address;
try
{
socket = new Socket(address.addressFamily, type, proto);
socket.bind(address);
}
catch(SocketOSException e)
{
throw new SocketListenerException(this);
}
}
/**
* Socket accept-and-connection spawner
* loop
*/
public override void serviceLoop()
{
while(true)
{
Socket clientSock = socket.accept();
//TODO
}
}
public Address getAddress()
{
return address;
}
}

View File

@ -1,69 +0,0 @@
/**
* Server sub-system
*
* This module pertains to the code required to stand-up a
* singular instance of a server and all the state that
* relates to it, such as users, server links and processing/handling
* of messages and commands.
*/
module dnetd.server;
import dnetd.app : logger;
import dnetd.config : ConfigurationError, Configuration;
import std.container.slist : SList;
import dnetd.listeners.listeners : Listener;
import dnetd.connection.connection : Connection;
import core.sync.mutex : Mutex;
/**
* Represents an instance of a dnet server
*/
public final class Server
{
/* Server Configuration */
private Configuration config;
/**
* Listeners
*/
private SList!(Listener) listeners;
private Mutex listenersMutex;
/**
* Connected clients and servers
*
* Inbound and outbound
*/
private SList!(Connection) conns;
private Mutex connsMutex;
this(Configuration config)
{
logger.log("Server instance '"~"PUT ID HERE"~"' starting up...");
this.config = config;
/**
* Initialize all locks for data
* structures
*/
connsMutex = new Mutex();
listeners = new Mutex();
}
/* Rehash server configuration */
public void rehash()
{
try
{
config = Configuration.getConfig(config.configPath);
}
catch(ConfigurationError e)
{
/* TODO: Handle the error here by sending a server message */
logger.log("Error whilst rehashing the configuration");
}
}
}